diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000000..e69de29bb2 diff --git a/404.html b/404.html new file mode 100644 index 0000000000..cc7e12eee4 --- /dev/null +++ b/404.html @@ -0,0 +1,7411 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ +

404 - Not found

+ +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/assets/external/cdn.jsdelivr.net/gh/jdecked/twemoji@15.0.3/assets/svg/1f512.svg b/assets/external/cdn.jsdelivr.net/gh/jdecked/twemoji@15.0.3/assets/svg/1f512.svg new file mode 100644 index 0000000000..4cd1facae8 --- /dev/null +++ b/assets/external/cdn.jsdelivr.net/gh/jdecked/twemoji@15.0.3/assets/svg/1f512.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/external/dummyimage.com/50x12/000000/000000.png b/assets/external/dummyimage.com/50x12/000000/000000.png new file mode 100644 index 0000000000..40678e314e Binary files /dev/null and b/assets/external/dummyimage.com/50x12/000000/000000.png differ diff --git a/assets/external/dummyimage.com/50x12/006064/006064.png b/assets/external/dummyimage.com/50x12/006064/006064.png new file mode 100644 index 0000000000..8e72491062 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/006064/006064.png differ diff --git a/assets/external/dummyimage.com/50x12/00695C/00695C.png b/assets/external/dummyimage.com/50x12/00695C/00695C.png new file mode 100644 index 0000000000..f162f5b987 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/00695C/00695C.png differ diff --git a/assets/external/dummyimage.com/50x12/00796B/00796B.png b/assets/external/dummyimage.com/50x12/00796B/00796B.png new file mode 100644 index 0000000000..57613f2c10 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/00796B/00796B.png differ diff --git a/assets/external/dummyimage.com/50x12/00838F/00838F.png b/assets/external/dummyimage.com/50x12/00838F/00838F.png new file mode 100644 index 0000000000..c9586d890b Binary files /dev/null and b/assets/external/dummyimage.com/50x12/00838F/00838F.png differ diff --git a/assets/external/dummyimage.com/50x12/00897B/00897B.png b/assets/external/dummyimage.com/50x12/00897B/00897B.png new file mode 100644 index 0000000000..15cae0cc46 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/00897B/00897B.png differ diff --git a/assets/external/dummyimage.com/50x12/0091EA/0091EA.png b/assets/external/dummyimage.com/50x12/0091EA/0091EA.png new file mode 100644 index 0000000000..cd9c3e7467 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/0091EA/0091EA.png differ diff --git a/assets/external/dummyimage.com/50x12/009688/009688.png b/assets/external/dummyimage.com/50x12/009688/009688.png new file mode 100644 index 0000000000..2334c2fac9 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/009688/009688.png differ diff --git a/assets/external/dummyimage.com/50x12/0097A7/0097A7.png b/assets/external/dummyimage.com/50x12/0097A7/0097A7.png new file mode 100644 index 0000000000..fd37101516 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/0097A7/0097A7.png differ diff --git a/assets/external/dummyimage.com/50x12/00ACC1/00ACC1.png b/assets/external/dummyimage.com/50x12/00ACC1/00ACC1.png new file mode 100644 index 0000000000..ea19e23d49 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/00ACC1/00ACC1.png differ diff --git a/assets/external/dummyimage.com/50x12/00B0FF/00B0FF.png b/assets/external/dummyimage.com/50x12/00B0FF/00B0FF.png new file mode 100644 index 0000000000..87ce3f00c0 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/00B0FF/00B0FF.png differ diff --git a/assets/external/dummyimage.com/50x12/00B8D4/00B8D4.png b/assets/external/dummyimage.com/50x12/00B8D4/00B8D4.png new file mode 100644 index 0000000000..2389119c55 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/00B8D4/00B8D4.png differ diff --git a/assets/external/dummyimage.com/50x12/00BCD4/00BCD4.png b/assets/external/dummyimage.com/50x12/00BCD4/00BCD4.png new file mode 100644 index 0000000000..d42924856c Binary files /dev/null and b/assets/external/dummyimage.com/50x12/00BCD4/00BCD4.png differ diff --git a/assets/external/dummyimage.com/50x12/00C853/00C853.png b/assets/external/dummyimage.com/50x12/00C853/00C853.png new file mode 100644 index 0000000000..a6859939b9 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/00C853/00C853.png differ diff --git a/assets/external/dummyimage.com/50x12/00E5FF/00E5FF.png b/assets/external/dummyimage.com/50x12/00E5FF/00E5FF.png new file mode 100644 index 0000000000..85b865ffdf Binary files /dev/null and b/assets/external/dummyimage.com/50x12/00E5FF/00E5FF.png differ diff --git a/assets/external/dummyimage.com/50x12/00E676/00E676.png b/assets/external/dummyimage.com/50x12/00E676/00E676.png new file mode 100644 index 0000000000..fca9d0bd73 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/00E676/00E676.png differ diff --git a/assets/external/dummyimage.com/50x12/012D5F/012D5F.png b/assets/external/dummyimage.com/50x12/012D5F/012D5F.png new file mode 100644 index 0000000000..4946575d97 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/012D5F/012D5F.png differ diff --git a/assets/external/dummyimage.com/50x12/01579B/01579B.png b/assets/external/dummyimage.com/50x12/01579B/01579B.png new file mode 100644 index 0000000000..c0550d3d2f Binary files /dev/null and b/assets/external/dummyimage.com/50x12/01579B/01579B.png differ diff --git a/assets/external/dummyimage.com/50x12/0277BD/0277BD.png b/assets/external/dummyimage.com/50x12/0277BD/0277BD.png new file mode 100644 index 0000000000..af0189b087 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/0277BD/0277BD.png differ diff --git a/assets/external/dummyimage.com/50x12/0288D1/0288D1.png b/assets/external/dummyimage.com/50x12/0288D1/0288D1.png new file mode 100644 index 0000000000..d1bda04cad Binary files /dev/null and b/assets/external/dummyimage.com/50x12/0288D1/0288D1.png differ diff --git a/assets/external/dummyimage.com/50x12/03454F/03454F.png b/assets/external/dummyimage.com/50x12/03454F/03454F.png new file mode 100644 index 0000000000..d15bb90d6d Binary files /dev/null and b/assets/external/dummyimage.com/50x12/03454F/03454F.png differ diff --git a/assets/external/dummyimage.com/50x12/039BE5/039BE5.png b/assets/external/dummyimage.com/50x12/039BE5/039BE5.png new file mode 100644 index 0000000000..de98dedb4f Binary files /dev/null and b/assets/external/dummyimage.com/50x12/039BE5/039BE5.png differ diff --git a/assets/external/dummyimage.com/50x12/03A9F6/03A9F6.png b/assets/external/dummyimage.com/50x12/03A9F6/03A9F6.png new file mode 100644 index 0000000000..c4de7ad0dc Binary files /dev/null and b/assets/external/dummyimage.com/50x12/03A9F6/03A9F6.png differ diff --git a/assets/external/dummyimage.com/50x12/045D5C/045D5C.png b/assets/external/dummyimage.com/50x12/045D5C/045D5C.png new file mode 100644 index 0000000000..86687b4dfa Binary files /dev/null and b/assets/external/dummyimage.com/50x12/045D5C/045D5C.png differ diff --git a/assets/external/dummyimage.com/50x12/076399/076399.png b/assets/external/dummyimage.com/50x12/076399/076399.png new file mode 100644 index 0000000000..4eac419a25 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/076399/076399.png differ diff --git a/assets/external/dummyimage.com/50x12/0800F4/0800F4.png b/assets/external/dummyimage.com/50x12/0800F4/0800F4.png new file mode 100644 index 0000000000..0cbf5549b4 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/0800F4/0800F4.png differ diff --git a/assets/external/dummyimage.com/50x12/0D47A1/0D47A1.png b/assets/external/dummyimage.com/50x12/0D47A1/0D47A1.png new file mode 100644 index 0000000000..b22b8f7690 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/0D47A1/0D47A1.png differ diff --git a/assets/external/dummyimage.com/50x12/1565C0/1565C0.png b/assets/external/dummyimage.com/50x12/1565C0/1565C0.png new file mode 100644 index 0000000000..4c719a2bda Binary files /dev/null and b/assets/external/dummyimage.com/50x12/1565C0/1565C0.png differ diff --git a/assets/external/dummyimage.com/50x12/174741/174741.png b/assets/external/dummyimage.com/50x12/174741/174741.png new file mode 100644 index 0000000000..67294202c7 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/174741/174741.png differ diff --git a/assets/external/dummyimage.com/50x12/18FFFF/18FFFF.png b/assets/external/dummyimage.com/50x12/18FFFF/18FFFF.png new file mode 100644 index 0000000000..60f65a7ece Binary files /dev/null and b/assets/external/dummyimage.com/50x12/18FFFF/18FFFF.png differ diff --git a/assets/external/dummyimage.com/50x12/1976D2/1976D2.png b/assets/external/dummyimage.com/50x12/1976D2/1976D2.png new file mode 100644 index 0000000000..7b2c07d2de Binary files /dev/null and b/assets/external/dummyimage.com/50x12/1976D2/1976D2.png differ diff --git a/assets/external/dummyimage.com/50x12/1A237E/1A237E.png b/assets/external/dummyimage.com/50x12/1A237E/1A237E.png new file mode 100644 index 0000000000..e89ecb7b08 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/1A237E/1A237E.png differ diff --git a/assets/external/dummyimage.com/50x12/1B5E20/1B5E20.png b/assets/external/dummyimage.com/50x12/1B5E20/1B5E20.png new file mode 100644 index 0000000000..3424b04127 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/1B5E20/1B5E20.png differ diff --git a/assets/external/dummyimage.com/50x12/1E88E5/1E88E5.png b/assets/external/dummyimage.com/50x12/1E88E5/1E88E5.png new file mode 100644 index 0000000000..c2d22a51c2 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/1E88E5/1E88E5.png differ diff --git a/assets/external/dummyimage.com/50x12/2196F3/2196F3.png b/assets/external/dummyimage.com/50x12/2196F3/2196F3.png new file mode 100644 index 0000000000..a56ed0ae4f Binary files /dev/null and b/assets/external/dummyimage.com/50x12/2196F3/2196F3.png differ diff --git a/assets/external/dummyimage.com/50x12/244E3B/244E3B.png b/assets/external/dummyimage.com/50x12/244E3B/244E3B.png new file mode 100644 index 0000000000..831a7fc660 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/244E3B/244E3B.png differ diff --git a/assets/external/dummyimage.com/50x12/245A5F/245A5F.png b/assets/external/dummyimage.com/50x12/245A5F/245A5F.png new file mode 100644 index 0000000000..1e47cc7db2 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/245A5F/245A5F.png differ diff --git a/assets/external/dummyimage.com/50x12/26C6DA/26C6DA.png b/assets/external/dummyimage.com/50x12/26C6DA/26C6DA.png new file mode 100644 index 0000000000..d534343d24 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/26C6DA/26C6DA.png differ diff --git a/assets/external/dummyimage.com/50x12/283593/283593.png b/assets/external/dummyimage.com/50x12/283593/283593.png new file mode 100644 index 0000000000..7a94d5be80 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/283593/283593.png differ diff --git a/assets/external/dummyimage.com/50x12/28446B/28446B.png b/assets/external/dummyimage.com/50x12/28446B/28446B.png new file mode 100644 index 0000000000..5f61d8b173 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/28446B/28446B.png differ diff --git a/assets/external/dummyimage.com/50x12/2962FF/2962FF.png b/assets/external/dummyimage.com/50x12/2962FF/2962FF.png new file mode 100644 index 0000000000..b30f9a372e Binary files /dev/null and b/assets/external/dummyimage.com/50x12/2962FF/2962FF.png differ diff --git a/assets/external/dummyimage.com/50x12/2979FF/2979FF.png b/assets/external/dummyimage.com/50x12/2979FF/2979FF.png new file mode 100644 index 0000000000..76b7a63b73 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/2979FF/2979FF.png differ diff --git a/assets/external/dummyimage.com/50x12/29B6F6/29B6F6.png b/assets/external/dummyimage.com/50x12/29B6F6/29B6F6.png new file mode 100644 index 0000000000..7827125fd6 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/29B6F6/29B6F6.png differ diff --git a/assets/external/dummyimage.com/50x12/2C545E/2C545E.png b/assets/external/dummyimage.com/50x12/2C545E/2C545E.png new file mode 100644 index 0000000000..fa9d6cfe4b Binary files /dev/null and b/assets/external/dummyimage.com/50x12/2C545E/2C545E.png differ diff --git a/assets/external/dummyimage.com/50x12/2E7D32/2E7D32.png b/assets/external/dummyimage.com/50x12/2E7D32/2E7D32.png new file mode 100644 index 0000000000..4c50893c2e Binary files /dev/null and b/assets/external/dummyimage.com/50x12/2E7D32/2E7D32.png differ diff --git a/assets/external/dummyimage.com/50x12/303F9F/303F9F.png b/assets/external/dummyimage.com/50x12/303F9F/303F9F.png new file mode 100644 index 0000000000..e3fabe19f4 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/303F9F/303F9F.png differ diff --git a/assets/external/dummyimage.com/50x12/304FFE/304FFE.png b/assets/external/dummyimage.com/50x12/304FFE/304FFE.png new file mode 100644 index 0000000000..8249e6cf01 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/304FFE/304FFE.png differ diff --git a/assets/external/dummyimage.com/50x12/311B92/311B92.png b/assets/external/dummyimage.com/50x12/311B92/311B92.png new file mode 100644 index 0000000000..b7ab12a404 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/311B92/311B92.png differ diff --git a/assets/external/dummyimage.com/50x12/33691E/33691E.png b/assets/external/dummyimage.com/50x12/33691E/33691E.png new file mode 100644 index 0000000000..7c220c3c2c Binary files /dev/null and b/assets/external/dummyimage.com/50x12/33691E/33691E.png differ diff --git a/assets/external/dummyimage.com/50x12/37474F/37474F.png b/assets/external/dummyimage.com/50x12/37474F/37474F.png new file mode 100644 index 0000000000..81df7eb94c Binary files /dev/null and b/assets/external/dummyimage.com/50x12/37474F/37474F.png differ diff --git a/assets/external/dummyimage.com/50x12/388E3C/388E3C.png b/assets/external/dummyimage.com/50x12/388E3C/388E3C.png new file mode 100644 index 0000000000..ba0311f118 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/388E3C/388E3C.png differ diff --git a/assets/external/dummyimage.com/50x12/3949AB/3949AB.png b/assets/external/dummyimage.com/50x12/3949AB/3949AB.png new file mode 100644 index 0000000000..9ab7724bb4 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/3949AB/3949AB.png differ diff --git a/assets/external/dummyimage.com/50x12/3B0E79/3B0E79.png b/assets/external/dummyimage.com/50x12/3B0E79/3B0E79.png new file mode 100644 index 0000000000..bb7e30386a Binary files /dev/null and b/assets/external/dummyimage.com/50x12/3B0E79/3B0E79.png differ diff --git a/assets/external/dummyimage.com/50x12/3D5AFE/3D5AFE.png b/assets/external/dummyimage.com/50x12/3D5AFE/3D5AFE.png new file mode 100644 index 0000000000..0e90f3bb40 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/3D5AFE/3D5AFE.png differ diff --git a/assets/external/dummyimage.com/50x12/3F51B5/3F51B5.png b/assets/external/dummyimage.com/50x12/3F51B5/3F51B5.png new file mode 100644 index 0000000000..142a618228 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/3F51B5/3F51B5.png differ diff --git a/assets/external/dummyimage.com/50x12/40C4FF/40C4FF.png b/assets/external/dummyimage.com/50x12/40C4FF/40C4FF.png new file mode 100644 index 0000000000..0120ce7569 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/40C4FF/40C4FF.png differ diff --git a/assets/external/dummyimage.com/50x12/424242/424242.png b/assets/external/dummyimage.com/50x12/424242/424242.png new file mode 100644 index 0000000000..0c4ebb2450 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/424242/424242.png differ diff --git a/assets/external/dummyimage.com/50x12/42A5F5/42A5F5.png b/assets/external/dummyimage.com/50x12/42A5F5/42A5F5.png new file mode 100644 index 0000000000..2b56fb9a5b Binary files /dev/null and b/assets/external/dummyimage.com/50x12/42A5F5/42A5F5.png differ diff --git a/assets/external/dummyimage.com/50x12/43A047/43A047.png b/assets/external/dummyimage.com/50x12/43A047/43A047.png new file mode 100644 index 0000000000..65f8b459e5 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/43A047/43A047.png differ diff --git a/assets/external/dummyimage.com/50x12/440E79/440E79.png b/assets/external/dummyimage.com/50x12/440E79/440E79.png new file mode 100644 index 0000000000..0d500be10b Binary files /dev/null and b/assets/external/dummyimage.com/50x12/440E79/440E79.png differ diff --git a/assets/external/dummyimage.com/50x12/448AFF/448AFF.png b/assets/external/dummyimage.com/50x12/448AFF/448AFF.png new file mode 100644 index 0000000000..467741a413 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/448AFF/448AFF.png differ diff --git a/assets/external/dummyimage.com/50x12/4527A0/4527A0.png b/assets/external/dummyimage.com/50x12/4527A0/4527A0.png new file mode 100644 index 0000000000..5e717061cb Binary files /dev/null and b/assets/external/dummyimage.com/50x12/4527A0/4527A0.png differ diff --git a/assets/external/dummyimage.com/50x12/474C25/474C25.png b/assets/external/dummyimage.com/50x12/474C25/474C25.png new file mode 100644 index 0000000000..ed4b29ef8d Binary files /dev/null and b/assets/external/dummyimage.com/50x12/474C25/474C25.png differ diff --git a/assets/external/dummyimage.com/50x12/483678/483678.png b/assets/external/dummyimage.com/50x12/483678/483678.png new file mode 100644 index 0000000000..091f70e8c5 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/483678/483678.png differ diff --git a/assets/external/dummyimage.com/50x12/491114/491114.png b/assets/external/dummyimage.com/50x12/491114/491114.png new file mode 100644 index 0000000000..4efdbf5274 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/491114/491114.png differ diff --git a/assets/external/dummyimage.com/50x12/4A148C/4A148C.png b/assets/external/dummyimage.com/50x12/4A148C/4A148C.png new file mode 100644 index 0000000000..6d3d2b49c6 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/4A148C/4A148C.png differ diff --git a/assets/external/dummyimage.com/50x12/4AC8F5/4AC8F5.png b/assets/external/dummyimage.com/50x12/4AC8F5/4AC8F5.png new file mode 100644 index 0000000000..8a15dcaeea Binary files /dev/null and b/assets/external/dummyimage.com/50x12/4AC8F5/4AC8F5.png differ diff --git a/assets/external/dummyimage.com/50x12/4B0121/4B0121.png b/assets/external/dummyimage.com/50x12/4B0121/4B0121.png new file mode 100644 index 0000000000..f2e6e14d37 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/4B0121/4B0121.png differ diff --git a/assets/external/dummyimage.com/50x12/4CAF50/4CAF50.png b/assets/external/dummyimage.com/50x12/4CAF50/4CAF50.png new file mode 100644 index 0000000000..c85b69df4c Binary files /dev/null and b/assets/external/dummyimage.com/50x12/4CAF50/4CAF50.png differ diff --git a/assets/external/dummyimage.com/50x12/4DD0E1/4DD0E1.png b/assets/external/dummyimage.com/50x12/4DD0E1/4DD0E1.png new file mode 100644 index 0000000000..b8f8e6c992 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/4DD0E1/4DD0E1.png differ diff --git a/assets/external/dummyimage.com/50x12/4E3880/4E3880.png b/assets/external/dummyimage.com/50x12/4E3880/4E3880.png new file mode 100644 index 0000000000..54e2766724 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/4E3880/4E3880.png differ diff --git a/assets/external/dummyimage.com/50x12/4FC3F7/4FC3F7.png b/assets/external/dummyimage.com/50x12/4FC3F7/4FC3F7.png new file mode 100644 index 0000000000..ac0b0cda48 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/4FC3F7/4FC3F7.png differ diff --git a/assets/external/dummyimage.com/50x12/511218/511218.png b/assets/external/dummyimage.com/50x12/511218/511218.png new file mode 100644 index 0000000000..c79aba2bdf Binary files /dev/null and b/assets/external/dummyimage.com/50x12/511218/511218.png differ diff --git a/assets/external/dummyimage.com/50x12/512DA8/512DA8.png b/assets/external/dummyimage.com/50x12/512DA8/512DA8.png new file mode 100644 index 0000000000..e78fee6ad5 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/512DA8/512DA8.png differ diff --git a/assets/external/dummyimage.com/50x12/536DFE/536DFE.png b/assets/external/dummyimage.com/50x12/536DFE/536DFE.png new file mode 100644 index 0000000000..bacbb00421 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/536DFE/536DFE.png differ diff --git a/assets/external/dummyimage.com/50x12/546E7A/546E7A.png b/assets/external/dummyimage.com/50x12/546E7A/546E7A.png new file mode 100644 index 0000000000..724d18b5f3 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/546E7A/546E7A.png differ diff --git a/assets/external/dummyimage.com/50x12/558B2F/558B2F.png b/assets/external/dummyimage.com/50x12/558B2F/558B2F.png new file mode 100644 index 0000000000..a989acdaa6 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/558B2F/558B2F.png differ diff --git a/assets/external/dummyimage.com/50x12/5B002F/5B002F.png b/assets/external/dummyimage.com/50x12/5B002F/5B002F.png new file mode 100644 index 0000000000..be4fd400e6 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/5B002F/5B002F.png differ diff --git a/assets/external/dummyimage.com/50x12/5C5A30/5C5A30.png b/assets/external/dummyimage.com/50x12/5C5A30/5C5A30.png new file mode 100644 index 0000000000..2a00cd87a3 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/5C5A30/5C5A30.png differ diff --git a/assets/external/dummyimage.com/50x12/5C6BC0/5C6BC0.png b/assets/external/dummyimage.com/50x12/5C6BC0/5C6BC0.png new file mode 100644 index 0000000000..2308ffa23c Binary files /dev/null and b/assets/external/dummyimage.com/50x12/5C6BC0/5C6BC0.png differ diff --git a/assets/external/dummyimage.com/50x12/5D4037/5D4037.png b/assets/external/dummyimage.com/50x12/5D4037/5D4037.png new file mode 100644 index 0000000000..6a6ba519e2 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/5D4037/5D4037.png differ diff --git a/assets/external/dummyimage.com/50x12/5E35B1/5E35B1.png b/assets/external/dummyimage.com/50x12/5E35B1/5E35B1.png new file mode 100644 index 0000000000..1814ac004f Binary files /dev/null and b/assets/external/dummyimage.com/50x12/5E35B1/5E35B1.png differ diff --git a/assets/external/dummyimage.com/50x12/5E3A5E/5E3A5E.png b/assets/external/dummyimage.com/50x12/5E3A5E/5E3A5E.png new file mode 100644 index 0000000000..a2da44923e Binary files /dev/null and b/assets/external/dummyimage.com/50x12/5E3A5E/5E3A5E.png differ diff --git a/assets/external/dummyimage.com/50x12/607D8B/607D8B.png b/assets/external/dummyimage.com/50x12/607D8B/607D8B.png new file mode 100644 index 0000000000..3351867960 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/607D8B/607D8B.png differ diff --git a/assets/external/dummyimage.com/50x12/616161/616161.png b/assets/external/dummyimage.com/50x12/616161/616161.png new file mode 100644 index 0000000000..c4b2b1ca1c Binary files /dev/null and b/assets/external/dummyimage.com/50x12/616161/616161.png differ diff --git a/assets/external/dummyimage.com/50x12/64071A/64071A.png b/assets/external/dummyimage.com/50x12/64071A/64071A.png new file mode 100644 index 0000000000..f907920ab4 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/64071A/64071A.png differ diff --git a/assets/external/dummyimage.com/50x12/64B5F6/64B5F6.png b/assets/external/dummyimage.com/50x12/64B5F6/64B5F6.png new file mode 100644 index 0000000000..eaea59d574 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/64B5F6/64B5F6.png differ diff --git a/assets/external/dummyimage.com/50x12/64DD17/64DD17.png b/assets/external/dummyimage.com/50x12/64DD17/64DD17.png new file mode 100644 index 0000000000..d6296a71fa Binary files /dev/null and b/assets/external/dummyimage.com/50x12/64DD17/64DD17.png differ diff --git a/assets/external/dummyimage.com/50x12/66BB6A/66BB6A.png b/assets/external/dummyimage.com/50x12/66BB6A/66BB6A.png new file mode 100644 index 0000000000..f35509b956 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/66BB6A/66BB6A.png differ diff --git a/assets/external/dummyimage.com/50x12/673AB7/673AB7.png b/assets/external/dummyimage.com/50x12/673AB7/673AB7.png new file mode 100644 index 0000000000..d8c002a33d Binary files /dev/null and b/assets/external/dummyimage.com/50x12/673AB7/673AB7.png differ diff --git a/assets/external/dummyimage.com/50x12/689F38/689F38.png b/assets/external/dummyimage.com/50x12/689F38/689F38.png new file mode 100644 index 0000000000..d17a8b848a Binary files /dev/null and b/assets/external/dummyimage.com/50x12/689F38/689F38.png differ diff --git a/assets/external/dummyimage.com/50x12/69F0AE/69F0AE.png b/assets/external/dummyimage.com/50x12/69F0AE/69F0AE.png new file mode 100644 index 0000000000..d13a118efc Binary files /dev/null and b/assets/external/dummyimage.com/50x12/69F0AE/69F0AE.png differ diff --git a/assets/external/dummyimage.com/50x12/6A1B9A/6A1B9A.png b/assets/external/dummyimage.com/50x12/6A1B9A/6A1B9A.png new file mode 100644 index 0000000000..e472df8447 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/6A1B9A/6A1B9A.png differ diff --git a/assets/external/dummyimage.com/50x12/6B5546/6B5546.png b/assets/external/dummyimage.com/50x12/6B5546/6B5546.png new file mode 100644 index 0000000000..9cb6922142 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/6B5546/6B5546.png differ diff --git a/assets/external/dummyimage.com/50x12/6D4C41/6D4C41.png b/assets/external/dummyimage.com/50x12/6D4C41/6D4C41.png new file mode 100644 index 0000000000..a14540ead7 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/6D4C41/6D4C41.png differ diff --git a/assets/external/dummyimage.com/50x12/701219/701219.png b/assets/external/dummyimage.com/50x12/701219/701219.png new file mode 100644 index 0000000000..e6925e9e93 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/701219/701219.png differ diff --git a/assets/external/dummyimage.com/50x12/755531/755531.png b/assets/external/dummyimage.com/50x12/755531/755531.png new file mode 100644 index 0000000000..2b711155c6 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/755531/755531.png differ diff --git a/assets/external/dummyimage.com/50x12/75645B/75645B.png b/assets/external/dummyimage.com/50x12/75645B/75645B.png new file mode 100644 index 0000000000..7dbf8a7e71 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/75645B/75645B.png differ diff --git a/assets/external/dummyimage.com/50x12/757575/757575.png b/assets/external/dummyimage.com/50x12/757575/757575.png new file mode 100644 index 0000000000..feeec785e4 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/757575/757575.png differ diff --git a/assets/external/dummyimage.com/50x12/76FF03/76FF03.png b/assets/external/dummyimage.com/50x12/76FF03/76FF03.png new file mode 100644 index 0000000000..d58ecb0006 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/76FF03/76FF03.png differ diff --git a/assets/external/dummyimage.com/50x12/78909C/78909C.png b/assets/external/dummyimage.com/50x12/78909C/78909C.png new file mode 100644 index 0000000000..3b40e2ee0a Binary files /dev/null and b/assets/external/dummyimage.com/50x12/78909C/78909C.png differ diff --git a/assets/external/dummyimage.com/50x12/795548/795548.png b/assets/external/dummyimage.com/50x12/795548/795548.png new file mode 100644 index 0000000000..df7da76e40 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/795548/795548.png differ diff --git a/assets/external/dummyimage.com/50x12/796D41/796D41.png b/assets/external/dummyimage.com/50x12/796D41/796D41.png new file mode 100644 index 0000000000..6c49f2e228 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/796D41/796D41.png differ diff --git a/assets/external/dummyimage.com/50x12/7B1FA2/7B1FA2.png b/assets/external/dummyimage.com/50x12/7B1FA2/7B1FA2.png new file mode 100644 index 0000000000..47b7bea70e Binary files /dev/null and b/assets/external/dummyimage.com/50x12/7B1FA2/7B1FA2.png differ diff --git a/assets/external/dummyimage.com/50x12/7C4DFF/7C4DFF.png b/assets/external/dummyimage.com/50x12/7C4DFF/7C4DFF.png new file mode 100644 index 0000000000..bfea0f1879 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/7C4DFF/7C4DFF.png differ diff --git a/assets/external/dummyimage.com/50x12/7CB342/7CB342.png b/assets/external/dummyimage.com/50x12/7CB342/7CB342.png new file mode 100644 index 0000000000..e7843c83e6 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/7CB342/7CB342.png differ diff --git a/assets/external/dummyimage.com/50x12/7D2704/7D2704.png b/assets/external/dummyimage.com/50x12/7D2704/7D2704.png new file mode 100644 index 0000000000..dc3b0db76e Binary files /dev/null and b/assets/external/dummyimage.com/50x12/7D2704/7D2704.png differ diff --git a/assets/external/dummyimage.com/50x12/7DD3EA/7DD3EA.png b/assets/external/dummyimage.com/50x12/7DD3EA/7DD3EA.png new file mode 100644 index 0000000000..c5ed45c9fc Binary files /dev/null and b/assets/external/dummyimage.com/50x12/7DD3EA/7DD3EA.png differ diff --git a/assets/external/dummyimage.com/50x12/7E57C2/7E57C2.png b/assets/external/dummyimage.com/50x12/7E57C2/7E57C2.png new file mode 100644 index 0000000000..90ef7d7a59 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/7E57C2/7E57C2.png differ diff --git a/assets/external/dummyimage.com/50x12/80D8FF/80D8FF.png b/assets/external/dummyimage.com/50x12/80D8FF/80D8FF.png new file mode 100644 index 0000000000..d1c7775ff5 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/80D8FF/80D8FF.png differ diff --git a/assets/external/dummyimage.com/50x12/80DEEA/80DEEA.png b/assets/external/dummyimage.com/50x12/80DEEA/80DEEA.png new file mode 100644 index 0000000000..f6d6719a49 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/80DEEA/80DEEA.png differ diff --git a/assets/external/dummyimage.com/50x12/81C784/81C784.png b/assets/external/dummyimage.com/50x12/81C784/81C784.png new file mode 100644 index 0000000000..5de4f04fde Binary files /dev/null and b/assets/external/dummyimage.com/50x12/81C784/81C784.png differ diff --git a/assets/external/dummyimage.com/50x12/81D4FA/81D4FA.png b/assets/external/dummyimage.com/50x12/81D4FA/81D4FA.png new file mode 100644 index 0000000000..f86ad73857 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/81D4FA/81D4FA.png differ diff --git a/assets/external/dummyimage.com/50x12/82B1FF/82B1FF.png b/assets/external/dummyimage.com/50x12/82B1FF/82B1FF.png new file mode 100644 index 0000000000..b187dbd557 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/82B1FF/82B1FF.png differ diff --git a/assets/external/dummyimage.com/50x12/847A72/847A72.png b/assets/external/dummyimage.com/50x12/847A72/847A72.png new file mode 100644 index 0000000000..d8dcb5efa8 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/847A72/847A72.png differ diff --git a/assets/external/dummyimage.com/50x12/84FFFF/84FFFF.png b/assets/external/dummyimage.com/50x12/84FFFF/84FFFF.png new file mode 100644 index 0000000000..102c63f386 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/84FFFF/84FFFF.png differ diff --git a/assets/external/dummyimage.com/50x12/86007E/86007E.png b/assets/external/dummyimage.com/50x12/86007E/86007E.png new file mode 100644 index 0000000000..171934720a Binary files /dev/null and b/assets/external/dummyimage.com/50x12/86007E/86007E.png differ diff --git a/assets/external/dummyimage.com/50x12/860225/860225.png b/assets/external/dummyimage.com/50x12/860225/860225.png new file mode 100644 index 0000000000..6f2f69b3d6 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/860225/860225.png differ diff --git a/assets/external/dummyimage.com/50x12/8BC34A/8BC34A.png b/assets/external/dummyimage.com/50x12/8BC34A/8BC34A.png new file mode 100644 index 0000000000..ca82e0ce79 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/8BC34A/8BC34A.png differ diff --git a/assets/external/dummyimage.com/50x12/8D6E63/8D6E63.png b/assets/external/dummyimage.com/50x12/8D6E63/8D6E63.png new file mode 100644 index 0000000000..9d1b8c8f54 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/8D6E63/8D6E63.png differ diff --git a/assets/external/dummyimage.com/50x12/8E24AA/8E24AA.png b/assets/external/dummyimage.com/50x12/8E24AA/8E24AA.png new file mode 100644 index 0000000000..afc1fb3c1a Binary files /dev/null and b/assets/external/dummyimage.com/50x12/8E24AA/8E24AA.png differ diff --git a/assets/external/dummyimage.com/50x12/8E6493/8E6493.png b/assets/external/dummyimage.com/50x12/8E6493/8E6493.png new file mode 100644 index 0000000000..34e4b13b32 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/8E6493/8E6493.png differ diff --git a/assets/external/dummyimage.com/50x12/90CAF9/90CAF9.png b/assets/external/dummyimage.com/50x12/90CAF9/90CAF9.png new file mode 100644 index 0000000000..e991aeeb8b Binary files /dev/null and b/assets/external/dummyimage.com/50x12/90CAF9/90CAF9.png differ diff --git a/assets/external/dummyimage.com/50x12/9575CD/9575CD.png b/assets/external/dummyimage.com/50x12/9575CD/9575CD.png new file mode 100644 index 0000000000..5bb4190153 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/9575CD/9575CD.png differ diff --git a/assets/external/dummyimage.com/50x12/9A9D47/9A9D47.png b/assets/external/dummyimage.com/50x12/9A9D47/9A9D47.png new file mode 100644 index 0000000000..f0208601a4 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/9A9D47/9A9D47.png differ diff --git a/assets/external/dummyimage.com/50x12/9B318F/9B318F.png b/assets/external/dummyimage.com/50x12/9B318F/9B318F.png new file mode 100644 index 0000000000..ad83c77e0f Binary files /dev/null and b/assets/external/dummyimage.com/50x12/9B318F/9B318F.png differ diff --git a/assets/external/dummyimage.com/50x12/9B6136/9B6136.png b/assets/external/dummyimage.com/50x12/9B6136/9B6136.png new file mode 100644 index 0000000000..af18f6832d Binary files /dev/null and b/assets/external/dummyimage.com/50x12/9B6136/9B6136.png differ diff --git a/assets/external/dummyimage.com/50x12/9B7B5B/9B7B5B.png b/assets/external/dummyimage.com/50x12/9B7B5B/9B7B5B.png new file mode 100644 index 0000000000..94e94e7b3c Binary files /dev/null and b/assets/external/dummyimage.com/50x12/9B7B5B/9B7B5B.png differ diff --git a/assets/external/dummyimage.com/50x12/9C27B0/9C27B0.png b/assets/external/dummyimage.com/50x12/9C27B0/9C27B0.png new file mode 100644 index 0000000000..46769b4587 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/9C27B0/9C27B0.png differ diff --git a/assets/external/dummyimage.com/50x12/9CCC65/9CCC65.png b/assets/external/dummyimage.com/50x12/9CCC65/9CCC65.png new file mode 100644 index 0000000000..283dd606ff Binary files /dev/null and b/assets/external/dummyimage.com/50x12/9CCC65/9CCC65.png differ diff --git a/assets/external/dummyimage.com/50x12/9E0047/9E0047.png b/assets/external/dummyimage.com/50x12/9E0047/9E0047.png new file mode 100644 index 0000000000..195f1c7839 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/9E0047/9E0047.png differ diff --git a/assets/external/dummyimage.com/50x12/9E9E9E/9E9E9E.png b/assets/external/dummyimage.com/50x12/9E9E9E/9E9E9E.png new file mode 100644 index 0000000000..bd18698352 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/9E9E9E/9E9E9E.png differ diff --git a/assets/external/dummyimage.com/50x12/9F5601/9F5601.png b/assets/external/dummyimage.com/50x12/9F5601/9F5601.png new file mode 100644 index 0000000000..0a13dbcf9b Binary files /dev/null and b/assets/external/dummyimage.com/50x12/9F5601/9F5601.png differ diff --git a/assets/external/dummyimage.com/50x12/9F8201/9F8201.png b/assets/external/dummyimage.com/50x12/9F8201/9F8201.png new file mode 100644 index 0000000000..a68db0e940 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/9F8201/9F8201.png differ diff --git a/assets/external/dummyimage.com/50x12/A07F6C/A07F6C.png b/assets/external/dummyimage.com/50x12/A07F6C/A07F6C.png new file mode 100644 index 0000000000..09beeaf6c7 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/A07F6C/A07F6C.png differ diff --git a/assets/external/dummyimage.com/50x12/A1211F/A1211F.png b/assets/external/dummyimage.com/50x12/A1211F/A1211F.png new file mode 100644 index 0000000000..cd86fdcd5e Binary files /dev/null and b/assets/external/dummyimage.com/50x12/A1211F/A1211F.png differ diff --git a/assets/external/dummyimage.com/50x12/A1887F/A1887F.png b/assets/external/dummyimage.com/50x12/A1887F/A1887F.png new file mode 100644 index 0000000000..11c0c77d35 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/A1887F/A1887F.png differ diff --git a/assets/external/dummyimage.com/50x12/A27D4b/A27D4b.png b/assets/external/dummyimage.com/50x12/A27D4b/A27D4b.png new file mode 100644 index 0000000000..71a02e2c4d Binary files /dev/null and b/assets/external/dummyimage.com/50x12/A27D4b/A27D4b.png differ diff --git a/assets/external/dummyimage.com/50x12/A5D6A7/A5D6A7.png b/assets/external/dummyimage.com/50x12/A5D6A7/A5D6A7.png new file mode 100644 index 0000000000..83a3a06462 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/A5D6A7/A5D6A7.png differ diff --git a/assets/external/dummyimage.com/50x12/A82863/A82863.png b/assets/external/dummyimage.com/50x12/A82863/A82863.png new file mode 100644 index 0000000000..f51b7c7506 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/A82863/A82863.png differ diff --git a/assets/external/dummyimage.com/50x12/AA00FF/AA00FF.png b/assets/external/dummyimage.com/50x12/AA00FF/AA00FF.png new file mode 100644 index 0000000000..e2359037fa Binary files /dev/null and b/assets/external/dummyimage.com/50x12/AA00FF/AA00FF.png differ diff --git a/assets/external/dummyimage.com/50x12/AA8062/AA8062.png b/assets/external/dummyimage.com/50x12/AA8062/AA8062.png new file mode 100644 index 0000000000..3cbf06dc66 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/AA8062/AA8062.png differ diff --git a/assets/external/dummyimage.com/50x12/AB47BC/AB47BC.png b/assets/external/dummyimage.com/50x12/AB47BC/AB47BC.png new file mode 100644 index 0000000000..a2dd1def3c Binary files /dev/null and b/assets/external/dummyimage.com/50x12/AB47BC/AB47BC.png differ diff --git a/assets/external/dummyimage.com/50x12/ABC7B0/ABC7B0.png b/assets/external/dummyimage.com/50x12/ABC7B0/ABC7B0.png new file mode 100644 index 0000000000..7e88e9cb65 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/ABC7B0/ABC7B0.png differ diff --git a/assets/external/dummyimage.com/50x12/ACA783/ACA783.png b/assets/external/dummyimage.com/50x12/ACA783/ACA783.png new file mode 100644 index 0000000000..b66d8c88b5 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/ACA783/ACA783.png differ diff --git a/assets/external/dummyimage.com/50x12/AED581/AED581.png b/assets/external/dummyimage.com/50x12/AED581/AED581.png new file mode 100644 index 0000000000..8844e2670d Binary files /dev/null and b/assets/external/dummyimage.com/50x12/AED581/AED581.png differ diff --git a/assets/external/dummyimage.com/50x12/AEEA00/AEEA00.png b/assets/external/dummyimage.com/50x12/AEEA00/AEEA00.png new file mode 100644 index 0000000000..87651ba9e7 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/AEEA00/AEEA00.png differ diff --git a/assets/external/dummyimage.com/50x12/AFB42B/AFB42B.png b/assets/external/dummyimage.com/50x12/AFB42B/AFB42B.png new file mode 100644 index 0000000000..369c8e6f98 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/AFB42B/AFB42B.png differ diff --git a/assets/external/dummyimage.com/50x12/B00085/B00085.png b/assets/external/dummyimage.com/50x12/B00085/B00085.png new file mode 100644 index 0000000000..399427bab4 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/B00085/B00085.png differ diff --git a/assets/external/dummyimage.com/50x12/B2B49B/B2B49B.png b/assets/external/dummyimage.com/50x12/B2B49B/B2B49B.png new file mode 100644 index 0000000000..a76da55b76 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/B2B49B/B2B49B.png differ diff --git a/assets/external/dummyimage.com/50x12/B2EBF2/B2EBF2.png b/assets/external/dummyimage.com/50x12/B2EBF2/B2EBF2.png new file mode 100644 index 0000000000..a47a8d3275 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/B2EBF2/B2EBF2.png differ diff --git a/assets/external/dummyimage.com/50x12/B2FF59/B2FF59.png b/assets/external/dummyimage.com/50x12/B2FF59/B2FF59.png new file mode 100644 index 0000000000..5f3f6143c3 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/B2FF59/B2FF59.png differ diff --git a/assets/external/dummyimage.com/50x12/B388FF/B388FF.png b/assets/external/dummyimage.com/50x12/B388FF/B388FF.png new file mode 100644 index 0000000000..67267c1521 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/B388FF/B388FF.png differ diff --git a/assets/external/dummyimage.com/50x12/B39DDB/B39DDB.png b/assets/external/dummyimage.com/50x12/B39DDB/B39DDB.png new file mode 100644 index 0000000000..ccebaa1b91 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/B39DDB/B39DDB.png differ diff --git a/assets/external/dummyimage.com/50x12/B3C16C/B3C16C.png b/assets/external/dummyimage.com/50x12/B3C16C/B3C16C.png new file mode 100644 index 0000000000..c75f2e9128 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/B3C16C/B3C16C.png differ diff --git a/assets/external/dummyimage.com/50x12/B3E5FC/B3E5FC.png b/assets/external/dummyimage.com/50x12/B3E5FC/B3E5FC.png new file mode 100644 index 0000000000..32119e77d5 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/B3E5FC/B3E5FC.png differ diff --git a/assets/external/dummyimage.com/50x12/B4B59C/B4B59C.png b/assets/external/dummyimage.com/50x12/B4B59C/B4B59C.png new file mode 100644 index 0000000000..1bbf15d597 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/B4B59C/B4B59C.png differ diff --git a/assets/external/dummyimage.com/50x12/B71C1C/B71C1C.png b/assets/external/dummyimage.com/50x12/B71C1C/B71C1C.png new file mode 100644 index 0000000000..5a876b51e1 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/B71C1C/B71C1C.png differ diff --git a/assets/external/dummyimage.com/50x12/B9F6CA/B9F6CA.png b/assets/external/dummyimage.com/50x12/B9F6CA/B9F6CA.png new file mode 100644 index 0000000000..53a8826fd8 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/B9F6CA/B9F6CA.png differ diff --git a/assets/external/dummyimage.com/50x12/BA2830/BA2830.png b/assets/external/dummyimage.com/50x12/BA2830/BA2830.png new file mode 100644 index 0000000000..efd92973b4 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/BA2830/BA2830.png differ diff --git a/assets/external/dummyimage.com/50x12/BA68C8/BA68C8.png b/assets/external/dummyimage.com/50x12/BA68C8/BA68C8.png new file mode 100644 index 0000000000..e8976490b1 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/BA68C8/BA68C8.png differ diff --git a/assets/external/dummyimage.com/50x12/BB723D/BB723D.png b/assets/external/dummyimage.com/50x12/BB723D/BB723D.png new file mode 100644 index 0000000000..9e0ce95cdf Binary files /dev/null and b/assets/external/dummyimage.com/50x12/BB723D/BB723D.png differ diff --git a/assets/external/dummyimage.com/50x12/BBDEFB/BBDEFB.png b/assets/external/dummyimage.com/50x12/BBDEFB/BBDEFB.png new file mode 100644 index 0000000000..5cec326dd5 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/BBDEFB/BBDEFB.png differ diff --git a/assets/external/dummyimage.com/50x12/BEBD76/BEBD76.png b/assets/external/dummyimage.com/50x12/BEBD76/BEBD76.png new file mode 100644 index 0000000000..3ab3b6bb8a Binary files /dev/null and b/assets/external/dummyimage.com/50x12/BEBD76/BEBD76.png differ diff --git a/assets/external/dummyimage.com/50x12/C0055B/C0055B.png b/assets/external/dummyimage.com/50x12/C0055B/C0055B.png new file mode 100644 index 0000000000..b965cb3792 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/C0055B/C0055B.png differ diff --git a/assets/external/dummyimage.com/50x12/C08A3E/C08A3E.png b/assets/external/dummyimage.com/50x12/C08A3E/C08A3E.png new file mode 100644 index 0000000000..59ce471829 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/C08A3E/C08A3E.png differ diff --git a/assets/external/dummyimage.com/50x12/C0CA33/C0CA33.png b/assets/external/dummyimage.com/50x12/C0CA33/C0CA33.png new file mode 100644 index 0000000000..b2fd5cd57f Binary files /dev/null and b/assets/external/dummyimage.com/50x12/C0CA33/C0CA33.png differ diff --git a/assets/external/dummyimage.com/50x12/C1A487/C1A487.png b/assets/external/dummyimage.com/50x12/C1A487/C1A487.png new file mode 100644 index 0000000000..9378226b2c Binary files /dev/null and b/assets/external/dummyimage.com/50x12/C1A487/C1A487.png differ diff --git a/assets/external/dummyimage.com/50x12/C2185B/C2185B.png b/assets/external/dummyimage.com/50x12/C2185B/C2185B.png new file mode 100644 index 0000000000..a6f7320e01 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/C2185B/C2185B.png differ diff --git a/assets/external/dummyimage.com/50x12/C5A702/C5A702.png b/assets/external/dummyimage.com/50x12/C5A702/C5A702.png new file mode 100644 index 0000000000..3123a1fada Binary files /dev/null and b/assets/external/dummyimage.com/50x12/C5A702/C5A702.png differ diff --git a/assets/external/dummyimage.com/50x12/C5CAE9/C5CAE9.png b/assets/external/dummyimage.com/50x12/C5CAE9/C5CAE9.png new file mode 100644 index 0000000000..36168b9822 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/C5CAE9/C5CAE9.png differ diff --git a/assets/external/dummyimage.com/50x12/C5E1A5/C5E1A5.png b/assets/external/dummyimage.com/50x12/C5E1A5/C5E1A5.png new file mode 100644 index 0000000000..7a9e7a08a4 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/C5E1A5/C5E1A5.png differ diff --git a/assets/external/dummyimage.com/50x12/C62828/C62828.png b/assets/external/dummyimage.com/50x12/C62828/C62828.png new file mode 100644 index 0000000000..47aa9091ea Binary files /dev/null and b/assets/external/dummyimage.com/50x12/C62828/C62828.png differ diff --git a/assets/external/dummyimage.com/50x12/C6FF00/C6FF00.png b/assets/external/dummyimage.com/50x12/C6FF00/C6FF00.png new file mode 100644 index 0000000000..4221810ab2 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/C6FF00/C6FF00.png differ diff --git a/assets/external/dummyimage.com/50x12/C8E6C9/C8E6C9.png b/assets/external/dummyimage.com/50x12/C8E6C9/C8E6C9.png new file mode 100644 index 0000000000..fc872b0de5 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/C8E6C9/C8E6C9.png differ diff --git a/assets/external/dummyimage.com/50x12/C94C30/C94C30.png b/assets/external/dummyimage.com/50x12/C94C30/C94C30.png new file mode 100644 index 0000000000..efdb8c8b82 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/C94C30/C94C30.png differ diff --git a/assets/external/dummyimage.com/50x12/CB023D/CB023D.png b/assets/external/dummyimage.com/50x12/CB023D/CB023D.png new file mode 100644 index 0000000000..2a9773518b Binary files /dev/null and b/assets/external/dummyimage.com/50x12/CB023D/CB023D.png differ diff --git a/assets/external/dummyimage.com/50x12/CC1708/CC1708.png b/assets/external/dummyimage.com/50x12/CC1708/CC1708.png new file mode 100644 index 0000000000..30824c3774 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/CC1708/CC1708.png differ diff --git a/assets/external/dummyimage.com/50x12/CCFF90/CCFF90.png b/assets/external/dummyimage.com/50x12/CCFF90/CCFF90.png new file mode 100644 index 0000000000..2802a3dd41 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/CCFF90/CCFF90.png differ diff --git a/assets/external/dummyimage.com/50x12/CDD087/CDD087.png b/assets/external/dummyimage.com/50x12/CDD087/CDD087.png new file mode 100644 index 0000000000..5b5fbf2796 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/CDD087/CDD087.png differ diff --git a/assets/external/dummyimage.com/50x12/CDDC39/CDDC39.png b/assets/external/dummyimage.com/50x12/CDDC39/CDDC39.png new file mode 100644 index 0000000000..397c845b7e Binary files /dev/null and b/assets/external/dummyimage.com/50x12/CDDC39/CDDC39.png differ diff --git a/assets/external/dummyimage.com/50x12/CE009B/CE009B.png b/assets/external/dummyimage.com/50x12/CE009B/CE009B.png new file mode 100644 index 0000000000..818545096a Binary files /dev/null and b/assets/external/dummyimage.com/50x12/CE009B/CE009B.png differ diff --git a/assets/external/dummyimage.com/50x12/CE93D8/CE93D8.png b/assets/external/dummyimage.com/50x12/CE93D8/CE93D8.png new file mode 100644 index 0000000000..b08086431a Binary files /dev/null and b/assets/external/dummyimage.com/50x12/CE93D8/CE93D8.png differ diff --git a/assets/external/dummyimage.com/50x12/CF0904/CF0904.png b/assets/external/dummyimage.com/50x12/CF0904/CF0904.png new file mode 100644 index 0000000000..bb012176c1 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/CF0904/CF0904.png differ diff --git a/assets/external/dummyimage.com/50x12/D19327/D19327.png b/assets/external/dummyimage.com/50x12/D19327/D19327.png new file mode 100644 index 0000000000..0af722907c Binary files /dev/null and b/assets/external/dummyimage.com/50x12/D19327/D19327.png differ diff --git a/assets/external/dummyimage.com/50x12/D1AF52/D1AF52.png b/assets/external/dummyimage.com/50x12/D1AF52/D1AF52.png new file mode 100644 index 0000000000..af22737041 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/D1AF52/D1AF52.png differ diff --git a/assets/external/dummyimage.com/50x12/D1C4E9/D1C4E9.png b/assets/external/dummyimage.com/50x12/D1C4E9/D1C4E9.png new file mode 100644 index 0000000000..20ede38a91 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/D1C4E9/D1C4E9.png differ diff --git a/assets/external/dummyimage.com/50x12/D32F2F/D32F2F.png b/assets/external/dummyimage.com/50x12/D32F2F/D32F2F.png new file mode 100644 index 0000000000..097d465adc Binary files /dev/null and b/assets/external/dummyimage.com/50x12/D32F2F/D32F2F.png differ diff --git a/assets/external/dummyimage.com/50x12/D3A83A/D3A83A.png b/assets/external/dummyimage.com/50x12/D3A83A/D3A83A.png new file mode 100644 index 0000000000..f2b90b640a Binary files /dev/null and b/assets/external/dummyimage.com/50x12/D3A83A/D3A83A.png differ diff --git a/assets/external/dummyimage.com/50x12/D4E157/D4E157.png b/assets/external/dummyimage.com/50x12/D4E157/D4E157.png new file mode 100644 index 0000000000..dca454a97f Binary files /dev/null and b/assets/external/dummyimage.com/50x12/D4E157/D4E157.png differ diff --git a/assets/external/dummyimage.com/50x12/D50000/D50000.png b/assets/external/dummyimage.com/50x12/D50000/D50000.png new file mode 100644 index 0000000000..a473f40d52 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/D50000/D50000.png differ diff --git a/assets/external/dummyimage.com/50x12/D5AA6F/D5AA6F.png b/assets/external/dummyimage.com/50x12/D5AA6F/D5AA6F.png new file mode 100644 index 0000000000..5cfc006941 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/D5AA6F/D5AA6F.png differ diff --git a/assets/external/dummyimage.com/50x12/D80807/D80807.png b/assets/external/dummyimage.com/50x12/D80807/D80807.png new file mode 100644 index 0000000000..1a814295da Binary files /dev/null and b/assets/external/dummyimage.com/50x12/D80807/D80807.png differ diff --git a/assets/external/dummyimage.com/50x12/D81A14/D81A14.png b/assets/external/dummyimage.com/50x12/D81A14/D81A14.png new file mode 100644 index 0000000000..538ea46286 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/D81A14/D81A14.png differ diff --git a/assets/external/dummyimage.com/50x12/D81B60/D81B60.png b/assets/external/dummyimage.com/50x12/D81B60/D81B60.png new file mode 100644 index 0000000000..f2db2e7089 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/D81B60/D81B60.png differ diff --git a/assets/external/dummyimage.com/50x12/DB0806/DB0806.png b/assets/external/dummyimage.com/50x12/DB0806/DB0806.png new file mode 100644 index 0000000000..764b16cb8d Binary files /dev/null and b/assets/external/dummyimage.com/50x12/DB0806/DB0806.png differ diff --git a/assets/external/dummyimage.com/50x12/DC7ACF/DC7ACF.png b/assets/external/dummyimage.com/50x12/DC7ACF/DC7ACF.png new file mode 100644 index 0000000000..8a50dcb11d Binary files /dev/null and b/assets/external/dummyimage.com/50x12/DC7ACF/DC7ACF.png differ diff --git a/assets/external/dummyimage.com/50x12/DCE775/DCE775.png b/assets/external/dummyimage.com/50x12/DCE775/DCE775.png new file mode 100644 index 0000000000..5b61c60ab7 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/DCE775/DCE775.png differ diff --git a/assets/external/dummyimage.com/50x12/DCEDC8/DCEDC8.png b/assets/external/dummyimage.com/50x12/DCEDC8/DCEDC8.png new file mode 100644 index 0000000000..259ece2aab Binary files /dev/null and b/assets/external/dummyimage.com/50x12/DCEDC8/DCEDC8.png differ diff --git a/assets/external/dummyimage.com/50x12/DDD579/DDD579.png b/assets/external/dummyimage.com/50x12/DDD579/DDD579.png new file mode 100644 index 0000000000..fd71f4887e Binary files /dev/null and b/assets/external/dummyimage.com/50x12/DDD579/DDD579.png differ diff --git a/assets/external/dummyimage.com/50x12/DE2616/DE2616.png b/assets/external/dummyimage.com/50x12/DE2616/DE2616.png new file mode 100644 index 0000000000..2099791155 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/DE2616/DE2616.png differ diff --git a/assets/external/dummyimage.com/50x12/DEA253/DEA253.png b/assets/external/dummyimage.com/50x12/DEA253/DEA253.png new file mode 100644 index 0000000000..2ffb6d1384 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/DEA253/DEA253.png differ diff --git a/assets/external/dummyimage.com/50x12/DFE0E1/DFE0E1.png b/assets/external/dummyimage.com/50x12/DFE0E1/DFE0E1.png new file mode 100644 index 0000000000..3eda457e48 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/DFE0E1/DFE0E1.png differ diff --git a/assets/external/dummyimage.com/50x12/E0E0E0/E0E0E0.png b/assets/external/dummyimage.com/50x12/E0E0E0/E0E0E0.png new file mode 100644 index 0000000000..93a7b560c7 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/E0E0E0/E0E0E0.png differ diff --git a/assets/external/dummyimage.com/50x12/E1BEE7/E1BEE7.png b/assets/external/dummyimage.com/50x12/E1BEE7/E1BEE7.png new file mode 100644 index 0000000000..2d2ee938a8 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/E1BEE7/E1BEE7.png differ diff --git a/assets/external/dummyimage.com/50x12/E3CE81/E3CE81.png b/assets/external/dummyimage.com/50x12/E3CE81/E3CE81.png new file mode 100644 index 0000000000..84156ca59a Binary files /dev/null and b/assets/external/dummyimage.com/50x12/E3CE81/E3CE81.png differ diff --git a/assets/external/dummyimage.com/50x12/E4E4E4/E4E4E4.png b/assets/external/dummyimage.com/50x12/E4E4E4/E4E4E4.png new file mode 100644 index 0000000000..e7cc3333e8 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/E4E4E4/E4E4E4.png differ diff --git a/assets/external/dummyimage.com/50x12/E4EBFD/E4EBFD.png b/assets/external/dummyimage.com/50x12/E4EBFD/E4EBFD.png new file mode 100644 index 0000000000..9df81a8500 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/E4EBFD/E4EBFD.png differ diff --git a/assets/external/dummyimage.com/50x12/E500E5/E500E5.png b/assets/external/dummyimage.com/50x12/E500E5/E500E5.png new file mode 100644 index 0000000000..125ba86fd8 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/E500E5/E500E5.png differ diff --git a/assets/external/dummyimage.com/50x12/E53935/E53935.png b/assets/external/dummyimage.com/50x12/E53935/E53935.png new file mode 100644 index 0000000000..6ee3cb1bea Binary files /dev/null and b/assets/external/dummyimage.com/50x12/E53935/E53935.png differ diff --git a/assets/external/dummyimage.com/50x12/E58347/E58347.png b/assets/external/dummyimage.com/50x12/E58347/E58347.png new file mode 100644 index 0000000000..19747c8ac2 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/E58347/E58347.png differ diff --git a/assets/external/dummyimage.com/50x12/E6EE9C/E6EE9C.png b/assets/external/dummyimage.com/50x12/E6EE9C/E6EE9C.png new file mode 100644 index 0000000000..6d7e119764 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/E6EE9C/E6EE9C.png differ diff --git a/assets/external/dummyimage.com/50x12/E79FA6/E79FA6.png b/assets/external/dummyimage.com/50x12/E79FA6/E79FA6.png new file mode 100644 index 0000000000..d0c0192a19 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/E79FA6/E79FA6.png differ diff --git a/assets/external/dummyimage.com/50x12/E7E7E7/E7E7E7.png b/assets/external/dummyimage.com/50x12/E7E7E7/E7E7E7.png new file mode 100644 index 0000000000..60a539e22b Binary files /dev/null and b/assets/external/dummyimage.com/50x12/E7E7E7/E7E7E7.png differ diff --git a/assets/external/dummyimage.com/50x12/E8B451/E8B451.png b/assets/external/dummyimage.com/50x12/E8B451/E8B451.png new file mode 100644 index 0000000000..32c80ce882 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/E8B451/E8B451.png differ diff --git a/assets/external/dummyimage.com/50x12/E8CE03/E8CE03.png b/assets/external/dummyimage.com/50x12/E8CE03/E8CE03.png new file mode 100644 index 0000000000..76b722d406 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/E8CE03/E8CE03.png differ diff --git a/assets/external/dummyimage.com/50x12/E8F5E9/E8F5E9.png b/assets/external/dummyimage.com/50x12/E8F5E9/E8F5E9.png new file mode 100644 index 0000000000..503c93f43a Binary files /dev/null and b/assets/external/dummyimage.com/50x12/E8F5E9/E8F5E9.png differ diff --git a/assets/external/dummyimage.com/50x12/E91E63/E91E63.png b/assets/external/dummyimage.com/50x12/E91E63/E91E63.png new file mode 100644 index 0000000000..2743772802 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/E91E63/E91E63.png differ diff --git a/assets/external/dummyimage.com/50x12/EC407A/EC407A.png b/assets/external/dummyimage.com/50x12/EC407A/EC407A.png new file mode 100644 index 0000000000..09b978c98d Binary files /dev/null and b/assets/external/dummyimage.com/50x12/EC407A/EC407A.png differ diff --git a/assets/external/dummyimage.com/50x12/EDDEAC/EDDEAC.png b/assets/external/dummyimage.com/50x12/EDDEAC/EDDEAC.png new file mode 100644 index 0000000000..5e7a68b462 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/EDDEAC/EDDEAC.png differ diff --git a/assets/external/dummyimage.com/50x12/EDE7F6/EDE7F6.png b/assets/external/dummyimage.com/50x12/EDE7F6/EDE7F6.png new file mode 100644 index 0000000000..182ba3f736 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/EDE7F6/EDE7F6.png differ diff --git a/assets/external/dummyimage.com/50x12/EE240F/EE240F.png b/assets/external/dummyimage.com/50x12/EE240F/EE240F.png new file mode 100644 index 0000000000..2dc65c7b73 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/EE240F/EE240F.png differ diff --git a/assets/external/dummyimage.com/50x12/EEBB2B/EEBB2B.png b/assets/external/dummyimage.com/50x12/EEBB2B/EEBB2B.png new file mode 100644 index 0000000000..1c7938c5e4 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/EEBB2B/EEBB2B.png differ diff --git a/assets/external/dummyimage.com/50x12/EEECA2/EEECA2.png b/assets/external/dummyimage.com/50x12/EEECA2/EEECA2.png new file mode 100644 index 0000000000..4a43e0eec2 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/EEECA2/EEECA2.png differ diff --git a/assets/external/dummyimage.com/50x12/EEFF41/EEFF41.png b/assets/external/dummyimage.com/50x12/EEFF41/EEFF41.png new file mode 100644 index 0000000000..19925aa7d8 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/EEFF41/EEFF41.png differ diff --git a/assets/external/dummyimage.com/50x12/EF5350/EF5350.png b/assets/external/dummyimage.com/50x12/EF5350/EF5350.png new file mode 100644 index 0000000000..11dbfc2fcc Binary files /dev/null and b/assets/external/dummyimage.com/50x12/EF5350/EF5350.png differ diff --git a/assets/external/dummyimage.com/50x12/EF6C00/EF6C00.png b/assets/external/dummyimage.com/50x12/EF6C00/EF6C00.png new file mode 100644 index 0000000000..1fc8c15aa2 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/EF6C00/EF6C00.png differ diff --git a/assets/external/dummyimage.com/50x12/F000B5/F000B5.png b/assets/external/dummyimage.com/50x12/F000B5/F000B5.png new file mode 100644 index 0000000000..4ab948871d Binary files /dev/null and b/assets/external/dummyimage.com/50x12/F000B5/F000B5.png differ diff --git a/assets/external/dummyimage.com/50x12/F06292/F06292.png b/assets/external/dummyimage.com/50x12/F06292/F06292.png new file mode 100644 index 0000000000..ca70ac3a4c Binary files /dev/null and b/assets/external/dummyimage.com/50x12/F06292/F06292.png differ diff --git a/assets/external/dummyimage.com/50x12/F0F4C3/F0F4C3.png b/assets/external/dummyimage.com/50x12/F0F4C3/F0F4C3.png new file mode 100644 index 0000000000..0f2df04d67 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/F0F4C3/F0F4C3.png differ diff --git a/assets/external/dummyimage.com/50x12/F19652/F19652.png b/assets/external/dummyimage.com/50x12/F19652/F19652.png new file mode 100644 index 0000000000..898d606bf1 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/F19652/F19652.png differ diff --git a/assets/external/dummyimage.com/50x12/F1F8E9/F1F8E9.png b/assets/external/dummyimage.com/50x12/F1F8E9/F1F8E9.png new file mode 100644 index 0000000000..8e35570b99 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/F1F8E9/F1F8E9.png differ diff --git a/assets/external/dummyimage.com/50x12/F3E5F5/F3E5F5.png b/assets/external/dummyimage.com/50x12/F3E5F5/F3E5F5.png new file mode 100644 index 0000000000..41f6dff99a Binary files /dev/null and b/assets/external/dummyimage.com/50x12/F3E5F5/F3E5F5.png differ diff --git a/assets/external/dummyimage.com/50x12/F44336/F44336.png b/assets/external/dummyimage.com/50x12/F44336/F44336.png new file mode 100644 index 0000000000..e545bb0317 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/F44336/F44336.png differ diff --git a/assets/external/dummyimage.com/50x12/F48FB1/F48FB1.png b/assets/external/dummyimage.com/50x12/F48FB1/F48FB1.png new file mode 100644 index 0000000000..452d97228a Binary files /dev/null and b/assets/external/dummyimage.com/50x12/F48FB1/F48FB1.png differ diff --git a/assets/external/dummyimage.com/50x12/F50057/F50057.png b/assets/external/dummyimage.com/50x12/F50057/F50057.png new file mode 100644 index 0000000000..dc172f84e1 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/F50057/F50057.png differ diff --git a/assets/external/dummyimage.com/50x12/F57C00/F57C00.png b/assets/external/dummyimage.com/50x12/F57C00/F57C00.png new file mode 100644 index 0000000000..26bbc45dee Binary files /dev/null and b/assets/external/dummyimage.com/50x12/F57C00/F57C00.png differ diff --git a/assets/external/dummyimage.com/50x12/F5EAD4/F5EAD4.png b/assets/external/dummyimage.com/50x12/F5EAD4/F5EAD4.png new file mode 100644 index 0000000000..4d24a9d17e Binary files /dev/null and b/assets/external/dummyimage.com/50x12/F5EAD4/F5EAD4.png differ diff --git a/assets/external/dummyimage.com/50x12/F6292E/F6292E.png b/assets/external/dummyimage.com/50x12/F6292E/F6292E.png new file mode 100644 index 0000000000..c15d0ca11a Binary files /dev/null and b/assets/external/dummyimage.com/50x12/F6292E/F6292E.png differ diff --git a/assets/external/dummyimage.com/50x12/F86704/F86704.png b/assets/external/dummyimage.com/50x12/F86704/F86704.png new file mode 100644 index 0000000000..75dcc26e14 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/F86704/F86704.png differ diff --git a/assets/external/dummyimage.com/50x12/F8BBD0/F8BBD0.png b/assets/external/dummyimage.com/50x12/F8BBD0/F8BBD0.png new file mode 100644 index 0000000000..cd02b6bf4b Binary files /dev/null and b/assets/external/dummyimage.com/50x12/F8BBD0/F8BBD0.png differ diff --git a/assets/external/dummyimage.com/50x12/F9A825/F9A825.png b/assets/external/dummyimage.com/50x12/F9A825/F9A825.png new file mode 100644 index 0000000000..2700789893 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/F9A825/F9A825.png differ diff --git a/assets/external/dummyimage.com/50x12/FA6801/FA6801.png b/assets/external/dummyimage.com/50x12/FA6801/FA6801.png new file mode 100644 index 0000000000..60be582f59 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FA6801/FA6801.png differ diff --git a/assets/external/dummyimage.com/50x12/FB8C00/FB8C00.png b/assets/external/dummyimage.com/50x12/FB8C00/FB8C00.png new file mode 100644 index 0000000000..d34b12ffc5 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FB8C00/FB8C00.png differ diff --git a/assets/external/dummyimage.com/50x12/FC252D/FC252D.png b/assets/external/dummyimage.com/50x12/FC252D/FC252D.png new file mode 100644 index 0000000000..4ebe12ca57 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FC252D/FC252D.png differ diff --git a/assets/external/dummyimage.com/50x12/FCE4EC/FCE4EC.png b/assets/external/dummyimage.com/50x12/FCE4EC/FCE4EC.png new file mode 100644 index 0000000000..a787358e1c Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FCE4EC/FCE4EC.png differ diff --git a/assets/external/dummyimage.com/50x12/FD571F/FD571F.png b/assets/external/dummyimage.com/50x12/FD571F/FD571F.png new file mode 100644 index 0000000000..704b5bb4e7 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FD571F/FD571F.png differ diff --git a/assets/external/dummyimage.com/50x12/FD9A00/FD9A00.png b/assets/external/dummyimage.com/50x12/FD9A00/FD9A00.png new file mode 100644 index 0000000000..2c788a026b Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FD9A00/FD9A00.png differ diff --git a/assets/external/dummyimage.com/50x12/FD9A31/FD9A31.png b/assets/external/dummyimage.com/50x12/FD9A31/FD9A31.png new file mode 100644 index 0000000000..7211178b13 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FD9A31/FD9A31.png differ diff --git a/assets/external/dummyimage.com/50x12/FDC8EB/FDC8EB.png b/assets/external/dummyimage.com/50x12/FDC8EB/FDC8EB.png new file mode 100644 index 0000000000..2d4e59c174 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FDC8EB/FDC8EB.png differ diff --git a/assets/external/dummyimage.com/50x12/FE8A00/FE8A00.png b/assets/external/dummyimage.com/50x12/FE8A00/FE8A00.png new file mode 100644 index 0000000000..ad4e544b50 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FE8A00/FE8A00.png differ diff --git a/assets/external/dummyimage.com/50x12/FF00FF/FF00FF.png b/assets/external/dummyimage.com/50x12/FF00FF/FF00FF.png new file mode 100644 index 0000000000..2c66ea752a Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FF00FF/FF00FF.png differ diff --git a/assets/external/dummyimage.com/50x12/FF4081/FF4081.png b/assets/external/dummyimage.com/50x12/FF4081/FF4081.png new file mode 100644 index 0000000000..6dd114fcbf Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FF4081/FF4081.png differ diff --git a/assets/external/dummyimage.com/50x12/FF5252/FF5252.png b/assets/external/dummyimage.com/50x12/FF5252/FF5252.png new file mode 100644 index 0000000000..adc4eff085 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FF5252/FF5252.png differ diff --git a/assets/external/dummyimage.com/50x12/FF6D00/FF6D00.png b/assets/external/dummyimage.com/50x12/FF6D00/FF6D00.png new file mode 100644 index 0000000000..f0c412c3e3 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FF6D00/FF6D00.png differ diff --git a/assets/external/dummyimage.com/50x12/FF80AB/FF80AB.png b/assets/external/dummyimage.com/50x12/FF80AB/FF80AB.png new file mode 100644 index 0000000000..465367cc79 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FF80AB/FF80AB.png differ diff --git a/assets/external/dummyimage.com/50x12/FF9100/FF9100.png b/assets/external/dummyimage.com/50x12/FF9100/FF9100.png new file mode 100644 index 0000000000..dd192f18cd Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FF9100/FF9100.png differ diff --git a/assets/external/dummyimage.com/50x12/FF9800/FF9800.png b/assets/external/dummyimage.com/50x12/FF9800/FF9800.png new file mode 100644 index 0000000000..92aaed7629 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FF9800/FF9800.png differ diff --git a/assets/external/dummyimage.com/50x12/FFA726/FFA726.png b/assets/external/dummyimage.com/50x12/FFA726/FFA726.png new file mode 100644 index 0000000000..13e03f494a Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FFA726/FFA726.png differ diff --git a/assets/external/dummyimage.com/50x12/FFCA28/FFCA28.png b/assets/external/dummyimage.com/50x12/FFCA28/FFCA28.png new file mode 100644 index 0000000000..43eca2c0a8 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FFCA28/FFCA28.png differ diff --git a/assets/external/dummyimage.com/50x12/FFD54F/FFD54F.png b/assets/external/dummyimage.com/50x12/FFD54F/FFD54F.png new file mode 100644 index 0000000000..3f032b769d Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FFD54F/FFD54F.png differ diff --git a/assets/external/dummyimage.com/50x12/FFEE58/FFEE58.png b/assets/external/dummyimage.com/50x12/FFEE58/FFEE58.png new file mode 100644 index 0000000000..acf8ff11db Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FFEE58/FFEE58.png differ diff --git a/assets/external/dummyimage.com/50x12/FFF176/FFF176.png b/assets/external/dummyimage.com/50x12/FFF176/FFF176.png new file mode 100644 index 0000000000..2fdc1fe7b2 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FFF176/FFF176.png differ diff --git a/assets/external/dummyimage.com/50x12/FFF59D/FFF59D.png b/assets/external/dummyimage.com/50x12/FFF59D/FFF59D.png new file mode 100644 index 0000000000..2e73c438bc Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FFF59D/FFF59D.png differ diff --git a/assets/external/dummyimage.com/50x12/FFF9C4/FFF9C4.png b/assets/external/dummyimage.com/50x12/FFF9C4/FFF9C4.png new file mode 100644 index 0000000000..7ecc7ffc87 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FFF9C4/FFF9C4.png differ diff --git a/assets/external/dummyimage.com/50x12/FFFF00/FFFF00.png b/assets/external/dummyimage.com/50x12/FFFF00/FFFF00.png new file mode 100644 index 0000000000..7f32e3abeb Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FFFF00/FFFF00.png differ diff --git a/assets/external/dummyimage.com/50x12/FFFF8D/FFFF8D.png b/assets/external/dummyimage.com/50x12/FFFF8D/FFFF8D.png new file mode 100644 index 0000000000..218a150a96 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FFFF8D/FFFF8D.png differ diff --git a/assets/external/dummyimage.com/50x12/FFFFFF/FFFFFF.png b/assets/external/dummyimage.com/50x12/FFFFFF/FFFFFF.png new file mode 100644 index 0000000000..c996366139 Binary files /dev/null and b/assets/external/dummyimage.com/50x12/FFFFFF/FFFFFF.png differ diff --git a/assets/external/fonts.googleapis.com/css.49ea35f2.css b/assets/external/fonts.googleapis.com/css.49ea35f2.css new file mode 100644 index 0000000000..14eb3ad409 --- /dev/null +++ b/assets/external/fonts.googleapis.com/css.49ea35f2.css @@ -0,0 +1,594 @@ +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc2CsTKlA.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc5CsTKlA.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc1CsTKlA.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xEIzIFKw.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xLIzIFKw.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xHIzIFKw.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xIIzI.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic2CsTKlA.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic5CsTKlA.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic1CsTKlA.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCBc4EsA.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fBxc4EsA.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCxc4EsA.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu72xKOzY.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu5mxKOzY.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7mxKOzY.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4WxKOzY.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7WxKOzY.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7GxKOzY.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4mxK.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCBc4EsA.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfBxc4EsA.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCxc4EsA.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEluUlYIw.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEn-UlYIw.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEmOUlYIw.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtElOUlYIw.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEleUlYIw.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEm-Ul.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEluUlYIw.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEn-UlYIw.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEmOUlYIw.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtElOUlYIw.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEleUlYIw.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEm-Ul.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSV0mf0h.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSx0mf0h.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSt0mf0h.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSd0mf0h.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSZ0mf0h.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSV0mf0h.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSx0mf0h.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSt0mf0h.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSd0mf0h.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSZ0mf0h.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2 new file mode 100644 index 0000000000..d88dd2b865 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2 new file mode 100644 index 0000000000..0f8ca12a62 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc1CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc1CsTKlA.woff2 new file mode 100644 index 0000000000..317f651efe Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc1CsTKlA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc2CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc2CsTKlA.woff2 new file mode 100644 index 0000000000..0e37f98731 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc2CsTKlA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2 new file mode 100644 index 0000000000..e0934d94e9 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc5CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc5CsTKlA.woff2 new file mode 100644 index 0000000000..d95067a4d7 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc5CsTKlA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2 new file mode 100644 index 0000000000..83874b7b8c Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2 new file mode 100644 index 0000000000..50a280591b Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2 new file mode 100644 index 0000000000..efbe79a294 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic1CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic1CsTKlA.woff2 new file mode 100644 index 0000000000..ea329ab826 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic1CsTKlA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic2CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic2CsTKlA.woff2 new file mode 100644 index 0000000000..993b327cab Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic2CsTKlA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2 new file mode 100644 index 0000000000..d3cb894a2c Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic5CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic5CsTKlA.woff2 new file mode 100644 index 0000000000..1283c45d72 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic5CsTKlA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2 new file mode 100644 index 0000000000..851fedb92b Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xEIzIFKw.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xEIzIFKw.woff2 new file mode 100644 index 0000000000..8f20a2cc1b Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xEIzIFKw.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2 new file mode 100644 index 0000000000..bed8708004 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2 new file mode 100644 index 0000000000..e1f558cd3f Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xHIzIFKw.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xHIzIFKw.woff2 new file mode 100644 index 0000000000..688c713dc6 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xHIzIFKw.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xIIzI.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xIIzI.woff2 new file mode 100644 index 0000000000..9dc0be8362 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xIIzI.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xLIzIFKw.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xLIzIFKw.woff2 new file mode 100644 index 0000000000..3e5facb890 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xLIzIFKw.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2 new file mode 100644 index 0000000000..1125cc0127 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2 new file mode 100644 index 0000000000..a57fbdc260 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fBBc4.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fBBc4.woff2 new file mode 100644 index 0000000000..72226f5df0 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fBBc4.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fBxc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fBxc4EsA.woff2 new file mode 100644 index 0000000000..b61eed30c0 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fBxc4EsA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCBc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCBc4EsA.woff2 new file mode 100644 index 0000000000..a26ba157a5 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCBc4EsA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2 new file mode 100644 index 0000000000..a69131b3a3 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2 new file mode 100644 index 0000000000..14af54ae68 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCxc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCxc4EsA.woff2 new file mode 100644 index 0000000000..a7026d4c3a Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCxc4EsA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 new file mode 100644 index 0000000000..41637e58ca Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfBBc4.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfBBc4.woff2 new file mode 100644 index 0000000000..22f6f53cd7 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfBBc4.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfBxc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfBxc4EsA.woff2 new file mode 100644 index 0000000000..19fc4b1bab Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfBxc4EsA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCBc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCBc4EsA.woff2 new file mode 100644 index 0000000000..98f53f7441 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCBc4EsA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2 new file mode 100644 index 0000000000..660850eed0 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2 new file mode 100644 index 0000000000..327eb66449 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCxc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCxc4EsA.woff2 new file mode 100644 index 0000000000..c17545332e Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCxc4EsA.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4WxKOzY.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4WxKOzY.woff2 new file mode 100644 index 0000000000..a7f32b6f05 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4WxKOzY.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4mxK.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4mxK.woff2 new file mode 100644 index 0000000000..2d7b215136 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4mxK.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 new file mode 100644 index 0000000000..a4962e9b42 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu72xKOzY.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu72xKOzY.woff2 new file mode 100644 index 0000000000..e3d708f354 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu72xKOzY.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 new file mode 100644 index 0000000000..20c87e676e Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7WxKOzY.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7WxKOzY.woff2 new file mode 100644 index 0000000000..cfd043dbaa Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7WxKOzY.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7mxKOzY.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7mxKOzY.woff2 new file mode 100644 index 0000000000..47ce460fa9 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7mxKOzY.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSV0mf0h.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSV0mf0h.woff2 new file mode 100644 index 0000000000..022274d4c4 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSV0mf0h.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSZ0mf0h.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSZ0mf0h.woff2 new file mode 100644 index 0000000000..48edd1b097 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSZ0mf0h.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSd0mf0h.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSd0mf0h.woff2 new file mode 100644 index 0000000000..cb41535ebb Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSd0mf0h.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2 new file mode 100644 index 0000000000..1d988a3f4c Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSt0mf0h.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSt0mf0h.woff2 new file mode 100644 index 0000000000..11e6a46a40 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSt0mf0h.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSx0mf0h.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSx0mf0h.woff2 new file mode 100644 index 0000000000..50fb8e7113 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSx0mf0h.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtElOUlYIw.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtElOUlYIw.woff2 new file mode 100644 index 0000000000..1f1c97fecd Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtElOUlYIw.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEleUlYIw.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEleUlYIw.woff2 new file mode 100644 index 0000000000..162300519f Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEleUlYIw.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEluUlYIw.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEluUlYIw.woff2 new file mode 100644 index 0000000000..6f232c38a1 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEluUlYIw.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEm-Ul.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEm-Ul.woff2 new file mode 100644 index 0000000000..a3e5aef7c9 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEm-Ul.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEmOUlYIw.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEmOUlYIw.woff2 new file mode 100644 index 0000000000..f73f27d6e1 Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEmOUlYIw.woff2 differ diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEn-UlYIw.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEn-UlYIw.woff2 new file mode 100644 index 0000000000..135d06e00d Binary files /dev/null and b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEn-UlYIw.woff2 differ diff --git a/assets/external/github.com/photoprism/photoprism/raw/144927b953a947fa6ae9ad6476281f647f0eca3f/docs/img/search.png b/assets/external/github.com/photoprism/photoprism/raw/144927b953a947fa6ae9ad6476281f647f0eca3f/docs/img/search.png new file mode 100644 index 0000000000..f4ac01766a Binary files /dev/null and b/assets/external/github.com/photoprism/photoprism/raw/144927b953a947fa6ae9ad6476281f647f0eca3f/docs/img/search.png differ diff --git a/assets/external/github.com/photoprism/photoprism/raw/31562d43cb1a8d73b3e07330a487872a872253f3/docs/img/screenshot.jpg b/assets/external/github.com/photoprism/photoprism/raw/31562d43cb1a8d73b3e07330a487872a872253f3/docs/img/screenshot.jpg new file mode 100644 index 0000000000..51d09c4e26 Binary files /dev/null and b/assets/external/github.com/photoprism/photoprism/raw/31562d43cb1a8d73b3e07330a487872a872253f3/docs/img/screenshot.jpg differ diff --git a/assets/external/github.com/photoprism/photoprism/raw/3d2305685124bdf98d2e08b98f5af12c2536b1b7/docs/img/screenshot.png b/assets/external/github.com/photoprism/photoprism/raw/3d2305685124bdf98d2e08b98f5af12c2536b1b7/docs/img/screenshot.png new file mode 100644 index 0000000000..5363cd8a57 Binary files /dev/null and b/assets/external/github.com/photoprism/photoprism/raw/3d2305685124bdf98d2e08b98f5af12c2536b1b7/docs/img/screenshot.png differ diff --git a/assets/external/github.com/photoprism/photoprism/raw/50bec9c866c2abb46175d6207a2be0db38f9ee26/assets/docs/img/screenshot-detailview.jpg b/assets/external/github.com/photoprism/photoprism/raw/50bec9c866c2abb46175d6207a2be0db38f9ee26/assets/docs/img/screenshot-detailview.jpg new file mode 100644 index 0000000000..a731427ecf Binary files /dev/null and b/assets/external/github.com/photoprism/photoprism/raw/50bec9c866c2abb46175d6207a2be0db38f9ee26/assets/docs/img/screenshot-detailview.jpg differ diff --git a/assets/external/github.com/photoprism/photoprism/raw/5d41015e6e37c2fe451b2acd22dde2f58181bb5a/docs/img/search.png b/assets/external/github.com/photoprism/photoprism/raw/5d41015e6e37c2fe451b2acd22dde2f58181bb5a/docs/img/search.png new file mode 100644 index 0000000000..8bbc097d5b Binary files /dev/null and b/assets/external/github.com/photoprism/photoprism/raw/5d41015e6e37c2fe451b2acd22dde2f58181bb5a/docs/img/search.png differ diff --git a/assets/external/github.com/photoprism/photoprism/raw/7120ae873784ee1a61b1f83c1ff8ba52b041f136/docs/img/search.png b/assets/external/github.com/photoprism/photoprism/raw/7120ae873784ee1a61b1f83c1ff8ba52b041f136/docs/img/search.png new file mode 100644 index 0000000000..0c07567007 Binary files /dev/null and b/assets/external/github.com/photoprism/photoprism/raw/7120ae873784ee1a61b1f83c1ff8ba52b041f136/docs/img/search.png differ diff --git a/assets/external/github.com/photoprism/photoprism/raw/d521167ad64280602953bcc1886dbacf093cca8e/docs/img/search.png b/assets/external/github.com/photoprism/photoprism/raw/d521167ad64280602953bcc1886dbacf093cca8e/docs/img/search.png new file mode 100644 index 0000000000..ce289f95d4 Binary files /dev/null and b/assets/external/github.com/photoprism/photoprism/raw/d521167ad64280602953bcc1886dbacf093cca8e/docs/img/search.png differ diff --git a/assets/external/github.com/photoprism/photoprism/raw/d5f46df060bfd53520280bfa67f011eefb7bf16d/docs/img/search.png b/assets/external/github.com/photoprism/photoprism/raw/d5f46df060bfd53520280bfa67f011eefb7bf16d/docs/img/search.png new file mode 100644 index 0000000000..377a9310dc Binary files /dev/null and b/assets/external/github.com/photoprism/photoprism/raw/d5f46df060bfd53520280bfa67f011eefb7bf16d/docs/img/search.png differ diff --git a/assets/external/github.com/photoprism/photoprism/raw/fbca5316f4bcc8856dd279550ce27f5054f67df0/assets/docs/img/screenshot-zebra.jpg b/assets/external/github.com/photoprism/photoprism/raw/fbca5316f4bcc8856dd279550ce27f5054f67df0/assets/docs/img/screenshot-zebra.jpg new file mode 100644 index 0000000000..6ae763002e Binary files /dev/null and b/assets/external/github.com/photoprism/photoprism/raw/fbca5316f4bcc8856dd279550ce27f5054f67df0/assets/docs/img/screenshot-zebra.jpg differ diff --git a/assets/external/pbs.twimg.com/media/DrvRwPjX4AAnhR1.jpg:large.jpg b/assets/external/pbs.twimg.com/media/DrvRwPjX4AAnhR1.jpg:large.jpg new file mode 100644 index 0000000000..eed799005c Binary files /dev/null and b/assets/external/pbs.twimg.com/media/DrvRwPjX4AAnhR1.jpg:large.jpg differ diff --git a/assets/external/unpkg.com/mermaid@11/dist/mermaid.min.js b/assets/external/unpkg.com/mermaid@11/dist/mermaid.min.js new file mode 100644 index 0000000000..5620450154 --- /dev/null +++ b/assets/external/unpkg.com/mermaid@11/dist/mermaid.min.js @@ -0,0 +1,2186 @@ +"use strict";var __esbuild_esm_mermaid=(()=>{var Pve=Object.create;var G1=Object.defineProperty;var Bve=Object.getOwnPropertyDescriptor;var Fve=Object.getOwnPropertyNames;var zve=Object.getPrototypeOf,Gve=Object.prototype.hasOwnProperty;var o=(t,e)=>G1(t,"name",{value:e,configurable:!0});var R=(t,e)=>()=>(t&&(e=t(t=0)),e);var gi=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),hr=(t,e)=>{for(var r in e)G1(t,r,{get:e[r],enumerable:!0})},Rb=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Fve(e))!Gve.call(t,i)&&i!==r&&G1(t,i,{get:()=>e[i],enumerable:!(n=Bve(e,i))||n.enumerable});return t},dr=(t,e,r)=>(Rb(t,e,"default"),r&&Rb(r,e,"default")),Xi=(t,e,r)=>(r=t!=null?Pve(zve(t)):{},Rb(e||!t||!t.__esModule?G1(r,"default",{value:t,enumerable:!0}):r,t)),$ve=t=>Rb(G1({},"__esModule",{value:!0}),t);var Nb=gi((AC,_C)=>{"use strict";(function(t,e){typeof AC=="object"&&typeof _C<"u"?_C.exports=e():typeof define=="function"&&define.amd?define(e):(t=typeof globalThis<"u"?globalThis:t||self).dayjs=e()})(AC,function(){"use strict";var t=1e3,e=6e4,r=36e5,n="millisecond",i="second",a="minute",s="hour",l="day",u="week",h="month",f="quarter",d="year",p="date",m="Invalid Date",g=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,y=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,v={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:o(function(k){var I=["th","st","nd","rd"],C=k%100;return"["+k+(I[(C-20)%10]||I[C]||I[0])+"]"},"ordinal")},x=o(function(k,I,C){var O=String(k);return!O||O.length>=I?k:""+Array(I+1-O.length).join(C)+k},"m"),b={s:x,z:o(function(k){var I=-k.utcOffset(),C=Math.abs(I),O=Math.floor(C/60),D=C%60;return(I<=0?"+":"-")+x(O,2,"0")+":"+x(D,2,"0")},"z"),m:o(function k(I,C){if(I.date()1)return k(F[0])}else{var B=I.name;S[B]=I,D=B}return!O&&D&&(w=D),D||!O&&w},"t"),A=o(function(k,I){if(E(k))return k.clone();var C=typeof I=="object"?I:{};return C.date=k,C.args=arguments,new M(C)},"O"),L=b;L.l=_,L.i=E,L.w=function(k,I){return A(k,{locale:I.$L,utc:I.$u,x:I.$x,$offset:I.$offset})};var M=function(){function k(C){this.$L=_(C.locale,null,!0),this.parse(C),this.$x=this.$x||C.x||{},this[T]=!0}o(k,"M");var I=k.prototype;return I.parse=function(C){this.$d=function(O){var D=O.date,P=O.utc;if(D===null)return new Date(NaN);if(L.u(D))return new Date;if(D instanceof Date)return new Date(D);if(typeof D=="string"&&!/Z$/i.test(D)){var F=D.match(g);if(F){var B=F[2]-1||0,$=(F[7]||"0").substring(0,3);return P?new Date(Date.UTC(F[1],B,F[3]||1,F[4]||0,F[5]||0,F[6]||0,$)):new Date(F[1],B,F[3]||1,F[4]||0,F[5]||0,F[6]||0,$)}}return new Date(D)}(C),this.init()},I.init=function(){var C=this.$d;this.$y=C.getFullYear(),this.$M=C.getMonth(),this.$D=C.getDate(),this.$W=C.getDay(),this.$H=C.getHours(),this.$m=C.getMinutes(),this.$s=C.getSeconds(),this.$ms=C.getMilliseconds()},I.$utils=function(){return L},I.isValid=function(){return this.$d.toString()!==m},I.isSame=function(C,O){var D=A(C);return this.startOf(O)<=D&&D<=this.endOf(O)},I.isAfter=function(C,O){return A(C){"use strict";LF=Xi(Nb(),1),Zc={trace:0,debug:1,info:2,warn:3,error:4,fatal:5},V={trace:o((...t)=>{},"trace"),debug:o((...t)=>{},"debug"),info:o((...t)=>{},"info"),warn:o((...t)=>{},"warn"),error:o((...t)=>{},"error"),fatal:o((...t)=>{},"fatal")},$1=o(function(t="fatal"){let e=Zc.fatal;typeof t=="string"?t.toLowerCase()in Zc&&(e=Zc[t]):typeof t=="number"&&(e=t),V.trace=()=>{},V.debug=()=>{},V.info=()=>{},V.warn=()=>{},V.error=()=>{},V.fatal=()=>{},e<=Zc.fatal&&(V.fatal=console.error?console.error.bind(console,Eo("FATAL"),"color: orange"):console.log.bind(console,"\x1B[35m",Eo("FATAL"))),e<=Zc.error&&(V.error=console.error?console.error.bind(console,Eo("ERROR"),"color: orange"):console.log.bind(console,"\x1B[31m",Eo("ERROR"))),e<=Zc.warn&&(V.warn=console.warn?console.warn.bind(console,Eo("WARN"),"color: orange"):console.log.bind(console,"\x1B[33m",Eo("WARN"))),e<=Zc.info&&(V.info=console.info?console.info.bind(console,Eo("INFO"),"color: lightblue"):console.log.bind(console,"\x1B[34m",Eo("INFO"))),e<=Zc.debug&&(V.debug=console.debug?console.debug.bind(console,Eo("DEBUG"),"color: lightgreen"):console.log.bind(console,"\x1B[32m",Eo("DEBUG"))),e<=Zc.trace&&(V.trace=console.debug?console.debug.bind(console,Eo("TRACE"),"color: lightgreen"):console.log.bind(console,"\x1B[32m",Eo("TRACE")))},"setLogLevel"),Eo=o(t=>`%c${(0,LF.default)().format("ss.SSS")} : ${t} : `,"format")});var Vve,np,LC,DF,Mb=R(()=>{"use strict";Vve=Object.freeze({left:0,top:0,width:16,height:16}),np=Object.freeze({rotate:0,vFlip:!1,hFlip:!1}),LC=Object.freeze({...Vve,...np}),DF=Object.freeze({...LC,body:"",hidden:!1})});var Uve,RF,NF=R(()=>{"use strict";Mb();Uve=Object.freeze({width:null,height:null}),RF=Object.freeze({...Uve,...np})});var Ib,DC,Ob,MF=R(()=>{"use strict";Ib=/^[a-z0-9]+(-[a-z0-9]+)*$/,DC=o((t,e,r,n="")=>{let i=t.split(":");if(t.slice(0,1)==="@"){if(i.length<2||i.length>3)return null;n=i.shift().slice(1)}if(i.length>3||!i.length)return null;if(i.length>1){let l=i.pop(),u=i.pop(),h={provider:i.length>0?i[0]:n,prefix:u,name:l};return e&&!Ob(h)?null:h}let a=i[0],s=a.split("-");if(s.length>1){let l={provider:n,prefix:s.shift(),name:s.join("-")};return e&&!Ob(l)?null:l}if(r&&n===""){let l={provider:n,prefix:"",name:a};return e&&!Ob(l,r)?null:l}return null},"stringToIcon"),Ob=o((t,e)=>t?!!((t.provider===""||t.provider.match(Ib))&&(e&&t.prefix===""||t.prefix.match(Ib))&&t.name.match(Ib)):!1,"validateIconName")});function IF(t,e){let r={};!t.hFlip!=!e.hFlip&&(r.hFlip=!0),!t.vFlip!=!e.vFlip&&(r.vFlip=!0);let n=((t.rotate||0)+(e.rotate||0))%4;return n&&(r.rotate=n),r}var OF=R(()=>{"use strict";o(IF,"mergeIconTransformations")});function RC(t,e){let r=IF(t,e);for(let n in DF)n in np?n in t&&!(n in r)&&(r[n]=np[n]):n in e?r[n]=e[n]:n in t&&(r[n]=t[n]);return r}var PF=R(()=>{"use strict";Mb();OF();o(RC,"mergeIconData")});function BF(t,e){let r=t.icons,n=t.aliases||Object.create(null),i=Object.create(null);function a(s){if(r[s])return i[s]=[];if(!(s in i)){i[s]=null;let l=n[s]&&n[s].parent,u=l&&a(l);u&&(i[s]=[l].concat(u))}return i[s]}return o(a,"resolve"),(e||Object.keys(r).concat(Object.keys(n))).forEach(a),i}var FF=R(()=>{"use strict";o(BF,"getIconsTree")});function zF(t,e,r){let n=t.icons,i=t.aliases||Object.create(null),a={};function s(l){a=RC(n[l]||i[l],a)}return o(s,"parse"),s(e),r.forEach(s),RC(t,a)}function NC(t,e){if(t.icons[e])return zF(t,e,[]);let r=BF(t,[e])[e];return r?zF(t,e,r):null}var GF=R(()=>{"use strict";PF();FF();o(zF,"internalGetIconData");o(NC,"getIconData")});function MC(t,e,r){if(e===1)return t;if(r=r||100,typeof t=="number")return Math.ceil(t*e*r)/r;if(typeof t!="string")return t;let n=t.split(Hve);if(n===null||!n.length)return t;let i=[],a=n.shift(),s=Yve.test(a);for(;;){if(s){let l=parseFloat(a);isNaN(l)?i.push(a):i.push(Math.ceil(l*e*r)/r)}else i.push(a);if(a=n.shift(),a===void 0)return i.join("");s=!s}}var Hve,Yve,$F=R(()=>{"use strict";Hve=/(-?[0-9.]*[0-9]+[0-9.]*)/g,Yve=/^-?[0-9.]*[0-9]+[0-9.]*$/g;o(MC,"calculateSize")});function Wve(t,e="defs"){let r="",n=t.indexOf("<"+e);for(;n>=0;){let i=t.indexOf(">",n),a=t.indexOf("",a);if(s===-1)break;r+=t.slice(i+1,a).trim(),t=t.slice(0,n).trim()+t.slice(s+1)}return{defs:r,content:t}}function qve(t,e){return t?""+t+""+e:e}function VF(t,e,r){let n=Wve(t);return qve(n.defs,e+n.content+r)}var UF=R(()=>{"use strict";o(Wve,"splitSVGDefs");o(qve,"mergeDefsAndContent");o(VF,"wrapSVGContent")});function IC(t,e){let r={...LC,...t},n={...RF,...e},i={left:r.left,top:r.top,width:r.width,height:r.height},a=r.body;[r,n].forEach(y=>{let v=[],x=y.hFlip,b=y.vFlip,w=y.rotate;x?b?w+=2:(v.push("translate("+(i.width+i.left).toString()+" "+(0-i.top).toString()+")"),v.push("scale(-1 1)"),i.top=i.left=0):b&&(v.push("translate("+(0-i.left).toString()+" "+(i.height+i.top).toString()+")"),v.push("scale(1 -1)"),i.top=i.left=0);let S;switch(w<0&&(w-=Math.floor(w/4)*4),w=w%4,w){case 1:S=i.height/2+i.top,v.unshift("rotate(90 "+S.toString()+" "+S.toString()+")");break;case 2:v.unshift("rotate(180 "+(i.width/2+i.left).toString()+" "+(i.height/2+i.top).toString()+")");break;case 3:S=i.width/2+i.left,v.unshift("rotate(-90 "+S.toString()+" "+S.toString()+")");break}w%2===1&&(i.left!==i.top&&(S=i.left,i.left=i.top,i.top=S),i.width!==i.height&&(S=i.width,i.width=i.height,i.height=S)),v.length&&(a=VF(a,'',""))});let s=n.width,l=n.height,u=i.width,h=i.height,f,d;s===null?(d=l===null?"1em":l==="auto"?h:l,f=MC(d,u/h)):(f=s==="auto"?u:s,d=l===null?MC(f,h/u):l==="auto"?h:l);let p={},m=o((y,v)=>{Xve(v)||(p[y]=v.toString())},"setAttr");m("width",f),m("height",d);let g=[i.left,i.top,u,h];return p.viewBox=g.join(" "),{attributes:p,viewBox:g,body:a}}var Xve,HF=R(()=>{"use strict";Mb();NF();$F();UF();Xve=o(t=>t==="unset"||t==="undefined"||t==="none","isUnsetKeyword");o(IC,"iconToSVG")});function OC(t,e=Kve){let r=[],n;for(;n=jve.exec(t);)r.push(n[1]);if(!r.length)return t;let i="suffix"+(Math.random()*16777216|Date.now()).toString(16);return r.forEach(a=>{let s=typeof e=="function"?e(a):e+(Qve++).toString(),l=a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");t=t.replace(new RegExp('([#;"])('+l+')([")]|\\.[a-z])',"g"),"$1"+s+i+"$3")}),t=t.replace(new RegExp(i,"g"),""),t}var jve,Kve,Qve,YF=R(()=>{"use strict";jve=/\sid="(\S+)"/g,Kve="IconifyId"+Date.now().toString(16)+(Math.random()*16777216|0).toString(16),Qve=0;o(OC,"replaceIDs")});function PC(t,e){let r=t.indexOf("xlink:")===-1?"":' xmlns:xlink="http://www.w3.org/1999/xlink"';for(let n in e)r+=" "+n+'="'+e[n]+'"';return'"+t+""}var WF=R(()=>{"use strict";o(PC,"iconToHTML")});var XF=gi((ait,qF)=>{"use strict";var ip=1e3,ap=ip*60,sp=ap*60,$f=sp*24,Zve=$f*7,Jve=$f*365.25;qF.exports=function(t,e){e=e||{};var r=typeof t;if(r==="string"&&t.length>0)return e2e(t);if(r==="number"&&isFinite(t))return e.long?r2e(t):t2e(t);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(t))};function e2e(t){if(t=String(t),!(t.length>100)){var e=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(t);if(e){var r=parseFloat(e[1]),n=(e[2]||"ms").toLowerCase();switch(n){case"years":case"year":case"yrs":case"yr":case"y":return r*Jve;case"weeks":case"week":case"w":return r*Zve;case"days":case"day":case"d":return r*$f;case"hours":case"hour":case"hrs":case"hr":case"h":return r*sp;case"minutes":case"minute":case"mins":case"min":case"m":return r*ap;case"seconds":case"second":case"secs":case"sec":case"s":return r*ip;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return r;default:return}}}}o(e2e,"parse");function t2e(t){var e=Math.abs(t);return e>=$f?Math.round(t/$f)+"d":e>=sp?Math.round(t/sp)+"h":e>=ap?Math.round(t/ap)+"m":e>=ip?Math.round(t/ip)+"s":t+"ms"}o(t2e,"fmtShort");function r2e(t){var e=Math.abs(t);return e>=$f?Pb(t,e,$f,"day"):e>=sp?Pb(t,e,sp,"hour"):e>=ap?Pb(t,e,ap,"minute"):e>=ip?Pb(t,e,ip,"second"):t+" ms"}o(r2e,"fmtLong");function Pb(t,e,r,n){var i=e>=r*1.5;return Math.round(t/r)+" "+n+(i?"s":"")}o(Pb,"plural")});var KF=gi((oit,jF)=>{"use strict";function n2e(t){r.debug=r,r.default=r,r.coerce=u,r.disable=a,r.enable=i,r.enabled=s,r.humanize=XF(),r.destroy=h,Object.keys(t).forEach(f=>{r[f]=t[f]}),r.names=[],r.skips=[],r.formatters={};function e(f){let d=0;for(let p=0;p{if(E==="%%")return"%";S++;let A=r.formatters[_];if(typeof A=="function"){let L=v[S];E=A.call(x,L),v.splice(S,1),S--}return E}),r.formatArgs.call(x,v),(x.log||r.log).apply(x,v)}return o(y,"debug"),y.namespace=f,y.useColors=r.useColors(),y.color=r.selectColor(f),y.extend=n,y.destroy=r.destroy,Object.defineProperty(y,"enabled",{enumerable:!0,configurable:!1,get:o(()=>p!==null?p:(m!==r.namespaces&&(m=r.namespaces,g=r.enabled(f)),g),"get"),set:o(v=>{p=v},"set")}),typeof r.init=="function"&&r.init(y),y}o(r,"createDebug");function n(f,d){let p=r(this.namespace+(typeof d>"u"?":":d)+f);return p.log=this.log,p}o(n,"extend");function i(f){r.save(f),r.namespaces=f,r.names=[],r.skips=[];let d,p=(typeof f=="string"?f:"").split(/[\s,]+/),m=p.length;for(d=0;d"-"+d)].join(",");return r.enable(""),f}o(a,"disable");function s(f){if(f[f.length-1]==="*")return!0;let d,p;for(d=0,p=r.skips.length;d{"use strict";Ys.formatArgs=a2e;Ys.save=s2e;Ys.load=o2e;Ys.useColors=i2e;Ys.storage=l2e();Ys.destroy=(()=>{let t=!1;return()=>{t||(t=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})();Ys.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"];function i2e(){if(typeof window<"u"&&window.process&&(window.process.type==="renderer"||window.process.__nwjs))return!0;if(typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;let t;return typeof document<"u"&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||typeof window<"u"&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||typeof navigator<"u"&&navigator.userAgent&&(t=navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/))&&parseInt(t[1],10)>=31||typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)}o(i2e,"useColors");function a2e(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+Bb.exports.humanize(this.diff),!this.useColors)return;let e="color: "+this.color;t.splice(1,0,e,"color: inherit");let r=0,n=0;t[0].replace(/%[a-zA-Z%]/g,i=>{i!=="%%"&&(r++,i==="%c"&&(n=r))}),t.splice(n,0,e)}o(a2e,"formatArgs");Ys.log=console.debug||console.log||(()=>{});function s2e(t){try{t?Ys.storage.setItem("debug",t):Ys.storage.removeItem("debug")}catch{}}o(s2e,"save");function o2e(){let t;try{t=Ys.storage.getItem("debug")}catch{}return!t&&typeof process<"u"&&"env"in process&&(t=process.env.DEBUG),t}o(o2e,"load");function l2e(){try{return localStorage}catch{}}o(l2e,"localstorage");Bb.exports=KF()(Ys);var{formatters:c2e}=Bb.exports;c2e.j=function(t){try{return JSON.stringify(t)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}});var uit,ZF=R(()=>{"use strict";MF();GF();HF();YF();WF();uit=Xi(QF(),1)});var FC,BC,JF,Fb,u2e,zb,V1=R(()=>{"use strict";ut();ZF();FC={body:'?',height:80,width:80},BC=new Map,JF=new Map,Fb=o(t=>{for(let e of t){if(!e.name)throw new Error('Invalid icon loader. Must have a "name" property with non-empty string value.');if(V.debug("Registering icon pack:",e.name),"loader"in e)JF.set(e.name,e.loader);else if("icons"in e)BC.set(e.name,e.icons);else throw V.error("Invalid icon loader:",e),new Error('Invalid icon loader. Must have either "icons" or "loader" property.')}},"registerIconPacks"),u2e=o(async(t,e)=>{let r=DC(t,!0,e!==void 0);if(!r)throw new Error(`Invalid icon name: ${t}`);let n=r.prefix||e;if(!n)throw new Error(`Icon name must contain a prefix: ${t}`);let i=BC.get(n);if(!i){let s=JF.get(n);if(!s)throw new Error(`Icon set not found: ${r.prefix}`);try{i={...await s(),prefix:n},BC.set(n,i)}catch(l){throw V.error(l),new Error(`Failed to load icon set: ${r.prefix}`)}}let a=NC(i,r.name);if(!a)throw new Error(`Icon not found: ${t}`);return a},"getRegisteredIconData"),zb=o(async(t,e)=>{let r;try{r=await u2e(t,e?.fallbackPrefix)}catch(a){V.error(a),r=FC}let n=IC(r,e);return PC(OC(n.body),n.attributes)},"getIconSVG")});function Gb(t){for(var e=[],r=1;r{"use strict";o(Gb,"dedent")});var $b,Vf,ez,Vb=R(()=>{"use strict";$b=/^-{3}\s*[\n\r](.*?)[\n\r]-{3}\s*[\n\r]+/s,Vf=/%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi,ez=/\s*%%.*\n/gm});var op,GC=R(()=>{"use strict";op=class extends Error{static{o(this,"UnknownDiagramError")}constructor(e){super(e),this.name="UnknownDiagramError"}}});var Uf,lp,Ub,$C,tz,Hf=R(()=>{"use strict";ut();Vb();GC();Uf={},lp=o(function(t,e){t=t.replace($b,"").replace(Vf,"").replace(ez,` +`);for(let[r,{detector:n}]of Object.entries(Uf))if(n(t,e))return r;throw new op(`No diagram type detected matching given configuration for text: ${t}`)},"detectType"),Ub=o((...t)=>{for(let{id:e,detector:r,loader:n}of t)$C(e,r,n)},"registerLazyLoadedDiagrams"),$C=o((t,e,r)=>{Uf[t]&&V.warn(`Detector with key ${t} already exists. Overwriting.`),Uf[t]={detector:e,loader:r},V.debug(`Detector with key ${t} added${r?" with loader":""}`)},"addDetector"),tz=o(t=>Uf[t].loader,"getDiagramLoader")});var U1,rz,VC=R(()=>{"use strict";U1=function(){var t=o(function(_e,me,W,fe){for(W=W||{},fe=_e.length;fe--;W[_e[fe]]=me);return W},"o"),e=[1,24],r=[1,25],n=[1,26],i=[1,27],a=[1,28],s=[1,63],l=[1,64],u=[1,65],h=[1,66],f=[1,67],d=[1,68],p=[1,69],m=[1,29],g=[1,30],y=[1,31],v=[1,32],x=[1,33],b=[1,34],w=[1,35],S=[1,36],T=[1,37],E=[1,38],_=[1,39],A=[1,40],L=[1,41],M=[1,42],N=[1,43],k=[1,44],I=[1,45],C=[1,46],O=[1,47],D=[1,48],P=[1,50],F=[1,51],B=[1,52],$=[1,53],z=[1,54],Y=[1,55],Q=[1,56],X=[1,57],ie=[1,58],j=[1,59],J=[1,60],Z=[14,42],H=[14,34,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74],q=[12,14,34,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74],K=[1,82],se=[1,83],ce=[1,84],ue=[1,85],te=[12,14,42],De=[12,14,33,42],oe=[12,14,33,42,76,77,79,80],ke=[12,33],Ie=[34,36,37,38,39,40,41,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74],Se={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,start:3,mermaidDoc:4,direction:5,direction_tb:6,direction_bt:7,direction_rl:8,direction_lr:9,graphConfig:10,C4_CONTEXT:11,NEWLINE:12,statements:13,EOF:14,C4_CONTAINER:15,C4_COMPONENT:16,C4_DYNAMIC:17,C4_DEPLOYMENT:18,otherStatements:19,diagramStatements:20,otherStatement:21,title:22,accDescription:23,acc_title:24,acc_title_value:25,acc_descr:26,acc_descr_value:27,acc_descr_multiline_value:28,boundaryStatement:29,boundaryStartStatement:30,boundaryStopStatement:31,boundaryStart:32,LBRACE:33,ENTERPRISE_BOUNDARY:34,attributes:35,SYSTEM_BOUNDARY:36,BOUNDARY:37,CONTAINER_BOUNDARY:38,NODE:39,NODE_L:40,NODE_R:41,RBRACE:42,diagramStatement:43,PERSON:44,PERSON_EXT:45,SYSTEM:46,SYSTEM_DB:47,SYSTEM_QUEUE:48,SYSTEM_EXT:49,SYSTEM_EXT_DB:50,SYSTEM_EXT_QUEUE:51,CONTAINER:52,CONTAINER_DB:53,CONTAINER_QUEUE:54,CONTAINER_EXT:55,CONTAINER_EXT_DB:56,CONTAINER_EXT_QUEUE:57,COMPONENT:58,COMPONENT_DB:59,COMPONENT_QUEUE:60,COMPONENT_EXT:61,COMPONENT_EXT_DB:62,COMPONENT_EXT_QUEUE:63,REL:64,BIREL:65,REL_U:66,REL_D:67,REL_L:68,REL_R:69,REL_B:70,REL_INDEX:71,UPDATE_EL_STYLE:72,UPDATE_REL_STYLE:73,UPDATE_LAYOUT_CONFIG:74,attribute:75,STR:76,STR_KEY:77,STR_VALUE:78,ATTRIBUTE:79,ATTRIBUTE_EMPTY:80,$accept:0,$end:1},terminals_:{2:"error",6:"direction_tb",7:"direction_bt",8:"direction_rl",9:"direction_lr",11:"C4_CONTEXT",12:"NEWLINE",14:"EOF",15:"C4_CONTAINER",16:"C4_COMPONENT",17:"C4_DYNAMIC",18:"C4_DEPLOYMENT",22:"title",23:"accDescription",24:"acc_title",25:"acc_title_value",26:"acc_descr",27:"acc_descr_value",28:"acc_descr_multiline_value",33:"LBRACE",34:"ENTERPRISE_BOUNDARY",36:"SYSTEM_BOUNDARY",37:"BOUNDARY",38:"CONTAINER_BOUNDARY",39:"NODE",40:"NODE_L",41:"NODE_R",42:"RBRACE",44:"PERSON",45:"PERSON_EXT",46:"SYSTEM",47:"SYSTEM_DB",48:"SYSTEM_QUEUE",49:"SYSTEM_EXT",50:"SYSTEM_EXT_DB",51:"SYSTEM_EXT_QUEUE",52:"CONTAINER",53:"CONTAINER_DB",54:"CONTAINER_QUEUE",55:"CONTAINER_EXT",56:"CONTAINER_EXT_DB",57:"CONTAINER_EXT_QUEUE",58:"COMPONENT",59:"COMPONENT_DB",60:"COMPONENT_QUEUE",61:"COMPONENT_EXT",62:"COMPONENT_EXT_DB",63:"COMPONENT_EXT_QUEUE",64:"REL",65:"BIREL",66:"REL_U",67:"REL_D",68:"REL_L",69:"REL_R",70:"REL_B",71:"REL_INDEX",72:"UPDATE_EL_STYLE",73:"UPDATE_REL_STYLE",74:"UPDATE_LAYOUT_CONFIG",76:"STR",77:"STR_KEY",78:"STR_VALUE",79:"ATTRIBUTE",80:"ATTRIBUTE_EMPTY"},productions_:[0,[3,1],[3,1],[5,1],[5,1],[5,1],[5,1],[4,1],[10,4],[10,4],[10,4],[10,4],[10,4],[13,1],[13,1],[13,2],[19,1],[19,2],[19,3],[21,1],[21,1],[21,2],[21,2],[21,1],[29,3],[30,3],[30,3],[30,4],[32,2],[32,2],[32,2],[32,2],[32,2],[32,2],[32,2],[31,1],[20,1],[20,2],[20,3],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,1],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[43,2],[35,1],[35,2],[75,1],[75,2],[75,1],[75,1]],performAction:o(function(me,W,fe,ge,re,he,ne){var ae=he.length-1;switch(re){case 3:ge.setDirection("TB");break;case 4:ge.setDirection("BT");break;case 5:ge.setDirection("RL");break;case 6:ge.setDirection("LR");break;case 8:case 9:case 10:case 11:case 12:ge.setC4Type(he[ae-3]);break;case 19:ge.setTitle(he[ae].substring(6)),this.$=he[ae].substring(6);break;case 20:ge.setAccDescription(he[ae].substring(15)),this.$=he[ae].substring(15);break;case 21:this.$=he[ae].trim(),ge.setTitle(this.$);break;case 22:case 23:this.$=he[ae].trim(),ge.setAccDescription(this.$);break;case 28:he[ae].splice(2,0,"ENTERPRISE"),ge.addPersonOrSystemBoundary(...he[ae]),this.$=he[ae];break;case 29:he[ae].splice(2,0,"SYSTEM"),ge.addPersonOrSystemBoundary(...he[ae]),this.$=he[ae];break;case 30:ge.addPersonOrSystemBoundary(...he[ae]),this.$=he[ae];break;case 31:he[ae].splice(2,0,"CONTAINER"),ge.addContainerBoundary(...he[ae]),this.$=he[ae];break;case 32:ge.addDeploymentNode("node",...he[ae]),this.$=he[ae];break;case 33:ge.addDeploymentNode("nodeL",...he[ae]),this.$=he[ae];break;case 34:ge.addDeploymentNode("nodeR",...he[ae]),this.$=he[ae];break;case 35:ge.popBoundaryParseStack();break;case 39:ge.addPersonOrSystem("person",...he[ae]),this.$=he[ae];break;case 40:ge.addPersonOrSystem("external_person",...he[ae]),this.$=he[ae];break;case 41:ge.addPersonOrSystem("system",...he[ae]),this.$=he[ae];break;case 42:ge.addPersonOrSystem("system_db",...he[ae]),this.$=he[ae];break;case 43:ge.addPersonOrSystem("system_queue",...he[ae]),this.$=he[ae];break;case 44:ge.addPersonOrSystem("external_system",...he[ae]),this.$=he[ae];break;case 45:ge.addPersonOrSystem("external_system_db",...he[ae]),this.$=he[ae];break;case 46:ge.addPersonOrSystem("external_system_queue",...he[ae]),this.$=he[ae];break;case 47:ge.addContainer("container",...he[ae]),this.$=he[ae];break;case 48:ge.addContainer("container_db",...he[ae]),this.$=he[ae];break;case 49:ge.addContainer("container_queue",...he[ae]),this.$=he[ae];break;case 50:ge.addContainer("external_container",...he[ae]),this.$=he[ae];break;case 51:ge.addContainer("external_container_db",...he[ae]),this.$=he[ae];break;case 52:ge.addContainer("external_container_queue",...he[ae]),this.$=he[ae];break;case 53:ge.addComponent("component",...he[ae]),this.$=he[ae];break;case 54:ge.addComponent("component_db",...he[ae]),this.$=he[ae];break;case 55:ge.addComponent("component_queue",...he[ae]),this.$=he[ae];break;case 56:ge.addComponent("external_component",...he[ae]),this.$=he[ae];break;case 57:ge.addComponent("external_component_db",...he[ae]),this.$=he[ae];break;case 58:ge.addComponent("external_component_queue",...he[ae]),this.$=he[ae];break;case 60:ge.addRel("rel",...he[ae]),this.$=he[ae];break;case 61:ge.addRel("birel",...he[ae]),this.$=he[ae];break;case 62:ge.addRel("rel_u",...he[ae]),this.$=he[ae];break;case 63:ge.addRel("rel_d",...he[ae]),this.$=he[ae];break;case 64:ge.addRel("rel_l",...he[ae]),this.$=he[ae];break;case 65:ge.addRel("rel_r",...he[ae]),this.$=he[ae];break;case 66:ge.addRel("rel_b",...he[ae]),this.$=he[ae];break;case 67:he[ae].splice(0,1),ge.addRel("rel",...he[ae]),this.$=he[ae];break;case 68:ge.updateElStyle("update_el_style",...he[ae]),this.$=he[ae];break;case 69:ge.updateRelStyle("update_rel_style",...he[ae]),this.$=he[ae];break;case 70:ge.updateLayoutConfig("update_layout_config",...he[ae]),this.$=he[ae];break;case 71:this.$=[he[ae]];break;case 72:he[ae].unshift(he[ae-1]),this.$=he[ae];break;case 73:case 75:this.$=he[ae].trim();break;case 74:let we={};we[he[ae-1].trim()]=he[ae].trim(),this.$=we;break;case 76:this.$="";break}},"anonymous"),table:[{3:1,4:2,5:3,6:[1,5],7:[1,6],8:[1,7],9:[1,8],10:4,11:[1,9],15:[1,10],16:[1,11],17:[1,12],18:[1,13]},{1:[3]},{1:[2,1]},{1:[2,2]},{1:[2,7]},{1:[2,3]},{1:[2,4]},{1:[2,5]},{1:[2,6]},{12:[1,14]},{12:[1,15]},{12:[1,16]},{12:[1,17]},{12:[1,18]},{13:19,19:20,20:21,21:22,22:e,23:r,24:n,26:i,28:a,29:49,30:61,32:62,34:s,36:l,37:u,38:h,39:f,40:d,41:p,43:23,44:m,45:g,46:y,47:v,48:x,49:b,50:w,51:S,52:T,53:E,54:_,55:A,56:L,57:M,58:N,59:k,60:I,61:C,62:O,63:D,64:P,65:F,66:B,67:$,68:z,69:Y,70:Q,71:X,72:ie,73:j,74:J},{13:70,19:20,20:21,21:22,22:e,23:r,24:n,26:i,28:a,29:49,30:61,32:62,34:s,36:l,37:u,38:h,39:f,40:d,41:p,43:23,44:m,45:g,46:y,47:v,48:x,49:b,50:w,51:S,52:T,53:E,54:_,55:A,56:L,57:M,58:N,59:k,60:I,61:C,62:O,63:D,64:P,65:F,66:B,67:$,68:z,69:Y,70:Q,71:X,72:ie,73:j,74:J},{13:71,19:20,20:21,21:22,22:e,23:r,24:n,26:i,28:a,29:49,30:61,32:62,34:s,36:l,37:u,38:h,39:f,40:d,41:p,43:23,44:m,45:g,46:y,47:v,48:x,49:b,50:w,51:S,52:T,53:E,54:_,55:A,56:L,57:M,58:N,59:k,60:I,61:C,62:O,63:D,64:P,65:F,66:B,67:$,68:z,69:Y,70:Q,71:X,72:ie,73:j,74:J},{13:72,19:20,20:21,21:22,22:e,23:r,24:n,26:i,28:a,29:49,30:61,32:62,34:s,36:l,37:u,38:h,39:f,40:d,41:p,43:23,44:m,45:g,46:y,47:v,48:x,49:b,50:w,51:S,52:T,53:E,54:_,55:A,56:L,57:M,58:N,59:k,60:I,61:C,62:O,63:D,64:P,65:F,66:B,67:$,68:z,69:Y,70:Q,71:X,72:ie,73:j,74:J},{13:73,19:20,20:21,21:22,22:e,23:r,24:n,26:i,28:a,29:49,30:61,32:62,34:s,36:l,37:u,38:h,39:f,40:d,41:p,43:23,44:m,45:g,46:y,47:v,48:x,49:b,50:w,51:S,52:T,53:E,54:_,55:A,56:L,57:M,58:N,59:k,60:I,61:C,62:O,63:D,64:P,65:F,66:B,67:$,68:z,69:Y,70:Q,71:X,72:ie,73:j,74:J},{14:[1,74]},t(Z,[2,13],{43:23,29:49,30:61,32:62,20:75,34:s,36:l,37:u,38:h,39:f,40:d,41:p,44:m,45:g,46:y,47:v,48:x,49:b,50:w,51:S,52:T,53:E,54:_,55:A,56:L,57:M,58:N,59:k,60:I,61:C,62:O,63:D,64:P,65:F,66:B,67:$,68:z,69:Y,70:Q,71:X,72:ie,73:j,74:J}),t(Z,[2,14]),t(H,[2,16],{12:[1,76]}),t(Z,[2,36],{12:[1,77]}),t(q,[2,19]),t(q,[2,20]),{25:[1,78]},{27:[1,79]},t(q,[2,23]),{35:80,75:81,76:K,77:se,79:ce,80:ue},{35:86,75:81,76:K,77:se,79:ce,80:ue},{35:87,75:81,76:K,77:se,79:ce,80:ue},{35:88,75:81,76:K,77:se,79:ce,80:ue},{35:89,75:81,76:K,77:se,79:ce,80:ue},{35:90,75:81,76:K,77:se,79:ce,80:ue},{35:91,75:81,76:K,77:se,79:ce,80:ue},{35:92,75:81,76:K,77:se,79:ce,80:ue},{35:93,75:81,76:K,77:se,79:ce,80:ue},{35:94,75:81,76:K,77:se,79:ce,80:ue},{35:95,75:81,76:K,77:se,79:ce,80:ue},{35:96,75:81,76:K,77:se,79:ce,80:ue},{35:97,75:81,76:K,77:se,79:ce,80:ue},{35:98,75:81,76:K,77:se,79:ce,80:ue},{35:99,75:81,76:K,77:se,79:ce,80:ue},{35:100,75:81,76:K,77:se,79:ce,80:ue},{35:101,75:81,76:K,77:se,79:ce,80:ue},{35:102,75:81,76:K,77:se,79:ce,80:ue},{35:103,75:81,76:K,77:se,79:ce,80:ue},{35:104,75:81,76:K,77:se,79:ce,80:ue},t(te,[2,59]),{35:105,75:81,76:K,77:se,79:ce,80:ue},{35:106,75:81,76:K,77:se,79:ce,80:ue},{35:107,75:81,76:K,77:se,79:ce,80:ue},{35:108,75:81,76:K,77:se,79:ce,80:ue},{35:109,75:81,76:K,77:se,79:ce,80:ue},{35:110,75:81,76:K,77:se,79:ce,80:ue},{35:111,75:81,76:K,77:se,79:ce,80:ue},{35:112,75:81,76:K,77:se,79:ce,80:ue},{35:113,75:81,76:K,77:se,79:ce,80:ue},{35:114,75:81,76:K,77:se,79:ce,80:ue},{35:115,75:81,76:K,77:se,79:ce,80:ue},{20:116,29:49,30:61,32:62,34:s,36:l,37:u,38:h,39:f,40:d,41:p,43:23,44:m,45:g,46:y,47:v,48:x,49:b,50:w,51:S,52:T,53:E,54:_,55:A,56:L,57:M,58:N,59:k,60:I,61:C,62:O,63:D,64:P,65:F,66:B,67:$,68:z,69:Y,70:Q,71:X,72:ie,73:j,74:J},{12:[1,118],33:[1,117]},{35:119,75:81,76:K,77:se,79:ce,80:ue},{35:120,75:81,76:K,77:se,79:ce,80:ue},{35:121,75:81,76:K,77:se,79:ce,80:ue},{35:122,75:81,76:K,77:se,79:ce,80:ue},{35:123,75:81,76:K,77:se,79:ce,80:ue},{35:124,75:81,76:K,77:se,79:ce,80:ue},{35:125,75:81,76:K,77:se,79:ce,80:ue},{14:[1,126]},{14:[1,127]},{14:[1,128]},{14:[1,129]},{1:[2,8]},t(Z,[2,15]),t(H,[2,17],{21:22,19:130,22:e,23:r,24:n,26:i,28:a}),t(Z,[2,37],{19:20,20:21,21:22,43:23,29:49,30:61,32:62,13:131,22:e,23:r,24:n,26:i,28:a,34:s,36:l,37:u,38:h,39:f,40:d,41:p,44:m,45:g,46:y,47:v,48:x,49:b,50:w,51:S,52:T,53:E,54:_,55:A,56:L,57:M,58:N,59:k,60:I,61:C,62:O,63:D,64:P,65:F,66:B,67:$,68:z,69:Y,70:Q,71:X,72:ie,73:j,74:J}),t(q,[2,21]),t(q,[2,22]),t(te,[2,39]),t(De,[2,71],{75:81,35:132,76:K,77:se,79:ce,80:ue}),t(oe,[2,73]),{78:[1,133]},t(oe,[2,75]),t(oe,[2,76]),t(te,[2,40]),t(te,[2,41]),t(te,[2,42]),t(te,[2,43]),t(te,[2,44]),t(te,[2,45]),t(te,[2,46]),t(te,[2,47]),t(te,[2,48]),t(te,[2,49]),t(te,[2,50]),t(te,[2,51]),t(te,[2,52]),t(te,[2,53]),t(te,[2,54]),t(te,[2,55]),t(te,[2,56]),t(te,[2,57]),t(te,[2,58]),t(te,[2,60]),t(te,[2,61]),t(te,[2,62]),t(te,[2,63]),t(te,[2,64]),t(te,[2,65]),t(te,[2,66]),t(te,[2,67]),t(te,[2,68]),t(te,[2,69]),t(te,[2,70]),{31:134,42:[1,135]},{12:[1,136]},{33:[1,137]},t(ke,[2,28]),t(ke,[2,29]),t(ke,[2,30]),t(ke,[2,31]),t(ke,[2,32]),t(ke,[2,33]),t(ke,[2,34]),{1:[2,9]},{1:[2,10]},{1:[2,11]},{1:[2,12]},t(H,[2,18]),t(Z,[2,38]),t(De,[2,72]),t(oe,[2,74]),t(te,[2,24]),t(te,[2,35]),t(Ie,[2,25]),t(Ie,[2,26],{12:[1,138]}),t(Ie,[2,27])],defaultActions:{2:[2,1],3:[2,2],4:[2,7],5:[2,3],6:[2,4],7:[2,5],8:[2,6],74:[2,8],126:[2,9],127:[2,10],128:[2,11],129:[2,12]},parseError:o(function(me,W){if(W.recoverable)this.trace(me);else{var fe=new Error(me);throw fe.hash=W,fe}},"parseError"),parse:o(function(me){var W=this,fe=[0],ge=[],re=[null],he=[],ne=this.table,ae="",we=0,Te=0,Ce=0,Ae=2,Ge=1,Me=he.slice.call(arguments,1),ye=Object.create(this.lexer),He={yy:{}};for(var ze in this.yy)Object.prototype.hasOwnProperty.call(this.yy,ze)&&(He.yy[ze]=this.yy[ze]);ye.setInput(me,He.yy),He.yy.lexer=ye,He.yy.parser=this,typeof ye.yylloc>"u"&&(ye.yylloc={});var Ze=ye.yylloc;he.push(Ze);var gt=ye.options&&ye.options.ranges;typeof He.yy.parseError=="function"?this.parseError=He.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function yt(St){fe.length=fe.length-2*St,re.length=re.length-St,he.length=he.length-St}o(yt,"popStack");function tt(){var St;return St=ge.pop()||ye.lex()||Ge,typeof St!="number"&&(St instanceof Array&&(ge=St,St=ge.pop()),St=W.symbols_[St]||St),St}o(tt,"lex");for(var Ye,Je,Ve,je,kt,at,xt={},it,dt,lt,It;;){if(Ve=fe[fe.length-1],this.defaultActions[Ve]?je=this.defaultActions[Ve]:((Ye===null||typeof Ye>"u")&&(Ye=tt()),je=ne[Ve]&&ne[Ve][Ye]),typeof je>"u"||!je.length||!je[0]){var mt="";It=[];for(it in ne[Ve])this.terminals_[it]&&it>Ae&&It.push("'"+this.terminals_[it]+"'");ye.showPosition?mt="Parse error on line "+(we+1)+`: +`+ye.showPosition()+` +Expecting `+It.join(", ")+", got '"+(this.terminals_[Ye]||Ye)+"'":mt="Parse error on line "+(we+1)+": Unexpected "+(Ye==Ge?"end of input":"'"+(this.terminals_[Ye]||Ye)+"'"),this.parseError(mt,{text:ye.match,token:this.terminals_[Ye]||Ye,line:ye.yylineno,loc:Ze,expected:It})}if(je[0]instanceof Array&&je.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Ve+", token: "+Ye);switch(je[0]){case 1:fe.push(Ye),re.push(ye.yytext),he.push(ye.yylloc),fe.push(je[1]),Ye=null,Je?(Ye=Je,Je=null):(Te=ye.yyleng,ae=ye.yytext,we=ye.yylineno,Ze=ye.yylloc,Ce>0&&Ce--);break;case 2:if(dt=this.productions_[je[1]][1],xt.$=re[re.length-dt],xt._$={first_line:he[he.length-(dt||1)].first_line,last_line:he[he.length-1].last_line,first_column:he[he.length-(dt||1)].first_column,last_column:he[he.length-1].last_column},gt&&(xt._$.range=[he[he.length-(dt||1)].range[0],he[he.length-1].range[1]]),at=this.performAction.apply(xt,[ae,Te,we,He.yy,je[1],re,he].concat(Me)),typeof at<"u")return at;dt&&(fe=fe.slice(0,-1*dt*2),re=re.slice(0,-1*dt),he=he.slice(0,-1*dt)),fe.push(this.productions_[je[1]][0]),re.push(xt.$),he.push(xt._$),lt=ne[fe[fe.length-2]][fe[fe.length-1]],fe.push(lt);break;case 3:return!0}}return!0},"parse")},Ue=function(){var _e={EOF:1,parseError:o(function(W,fe){if(this.yy.parser)this.yy.parser.parseError(W,fe);else throw new Error(W)},"parseError"),setInput:o(function(me,W){return this.yy=W||this.yy||{},this._input=me,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var me=this._input[0];this.yytext+=me,this.yyleng++,this.offset++,this.match+=me,this.matched+=me;var W=me.match(/(?:\r\n?|\n).*/g);return W?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),me},"input"),unput:o(function(me){var W=me.length,fe=me.split(/(?:\r\n?|\n)/g);this._input=me+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-W),this.offset-=W;var ge=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),fe.length-1&&(this.yylineno-=fe.length-1);var re=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:fe?(fe.length===ge.length?this.yylloc.first_column:0)+ge[ge.length-fe.length].length-fe[0].length:this.yylloc.first_column-W},this.options.ranges&&(this.yylloc.range=[re[0],re[0]+this.yyleng-W]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(me){this.unput(this.match.slice(me))},"less"),pastInput:o(function(){var me=this.matched.substr(0,this.matched.length-this.match.length);return(me.length>20?"...":"")+me.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var me=this.match;return me.length<20&&(me+=this._input.substr(0,20-me.length)),(me.substr(0,20)+(me.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var me=this.pastInput(),W=new Array(me.length+1).join("-");return me+this.upcomingInput()+` +`+W+"^"},"showPosition"),test_match:o(function(me,W){var fe,ge,re;if(this.options.backtrack_lexer&&(re={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(re.yylloc.range=this.yylloc.range.slice(0))),ge=me[0].match(/(?:\r\n?|\n).*/g),ge&&(this.yylineno+=ge.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:ge?ge[ge.length-1].length-ge[ge.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+me[0].length},this.yytext+=me[0],this.match+=me[0],this.matches=me,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(me[0].length),this.matched+=me[0],fe=this.performAction.call(this,this.yy,this,W,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),fe)return fe;if(this._backtrack){for(var he in re)this[he]=re[he];return!1}return!1},"test_match"),next:o(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var me,W,fe,ge;this._more||(this.yytext="",this.match="");for(var re=this._currentRules(),he=0;heW[0].length)){if(W=fe,ge=he,this.options.backtrack_lexer){if(me=this.test_match(fe,re[he]),me!==!1)return me;if(this._backtrack){W=!1;continue}else return!1}else if(!this.options.flex)break}return W?(me=this.test_match(W,re[ge]),me!==!1?me:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:o(function(){var W=this.next();return W||this.lex()},"lex"),begin:o(function(W){this.conditionStack.push(W)},"begin"),popState:o(function(){var W=this.conditionStack.length-1;return W>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:o(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:o(function(W){return W=this.conditionStack.length-1-Math.abs(W||0),W>=0?this.conditionStack[W]:"INITIAL"},"topState"),pushState:o(function(W){this.begin(W)},"pushState"),stateStackSize:o(function(){return this.conditionStack.length},"stateStackSize"),options:{},performAction:o(function(W,fe,ge,re){var he=re;switch(ge){case 0:return 6;case 1:return 7;case 2:return 8;case 3:return 9;case 4:return 22;case 5:return 23;case 6:return this.begin("acc_title"),24;break;case 7:return this.popState(),"acc_title_value";break;case 8:return this.begin("acc_descr"),26;break;case 9:return this.popState(),"acc_descr_value";break;case 10:this.begin("acc_descr_multiline");break;case 11:this.popState();break;case 12:return"acc_descr_multiline_value";case 13:break;case 14:c;break;case 15:return 12;case 16:break;case 17:return 11;case 18:return 15;case 19:return 16;case 20:return 17;case 21:return 18;case 22:return this.begin("person_ext"),45;break;case 23:return this.begin("person"),44;break;case 24:return this.begin("system_ext_queue"),51;break;case 25:return this.begin("system_ext_db"),50;break;case 26:return this.begin("system_ext"),49;break;case 27:return this.begin("system_queue"),48;break;case 28:return this.begin("system_db"),47;break;case 29:return this.begin("system"),46;break;case 30:return this.begin("boundary"),37;break;case 31:return this.begin("enterprise_boundary"),34;break;case 32:return this.begin("system_boundary"),36;break;case 33:return this.begin("container_ext_queue"),57;break;case 34:return this.begin("container_ext_db"),56;break;case 35:return this.begin("container_ext"),55;break;case 36:return this.begin("container_queue"),54;break;case 37:return this.begin("container_db"),53;break;case 38:return this.begin("container"),52;break;case 39:return this.begin("container_boundary"),38;break;case 40:return this.begin("component_ext_queue"),63;break;case 41:return this.begin("component_ext_db"),62;break;case 42:return this.begin("component_ext"),61;break;case 43:return this.begin("component_queue"),60;break;case 44:return this.begin("component_db"),59;break;case 45:return this.begin("component"),58;break;case 46:return this.begin("node"),39;break;case 47:return this.begin("node"),39;break;case 48:return this.begin("node_l"),40;break;case 49:return this.begin("node_r"),41;break;case 50:return this.begin("rel"),64;break;case 51:return this.begin("birel"),65;break;case 52:return this.begin("rel_u"),66;break;case 53:return this.begin("rel_u"),66;break;case 54:return this.begin("rel_d"),67;break;case 55:return this.begin("rel_d"),67;break;case 56:return this.begin("rel_l"),68;break;case 57:return this.begin("rel_l"),68;break;case 58:return this.begin("rel_r"),69;break;case 59:return this.begin("rel_r"),69;break;case 60:return this.begin("rel_b"),70;break;case 61:return this.begin("rel_index"),71;break;case 62:return this.begin("update_el_style"),72;break;case 63:return this.begin("update_rel_style"),73;break;case 64:return this.begin("update_layout_config"),74;break;case 65:return"EOF_IN_STRUCT";case 66:return this.begin("attribute"),"ATTRIBUTE_EMPTY";break;case 67:this.begin("attribute");break;case 68:this.popState(),this.popState();break;case 69:return 80;case 70:break;case 71:return 80;case 72:this.begin("string");break;case 73:this.popState();break;case 74:return"STR";case 75:this.begin("string_kv");break;case 76:return this.begin("string_kv_key"),"STR_KEY";break;case 77:this.popState(),this.begin("string_kv_value");break;case 78:return"STR_VALUE";case 79:this.popState(),this.popState();break;case 80:return"STR";case 81:return"LBRACE";case 82:return"RBRACE";case 83:return"SPACE";case 84:return"EOL";case 85:return 14}},"anonymous"),rules:[/^(?:.*direction\s+TB[^\n]*)/,/^(?:.*direction\s+BT[^\n]*)/,/^(?:.*direction\s+RL[^\n]*)/,/^(?:.*direction\s+LR[^\n]*)/,/^(?:title\s[^#\n;]+)/,/^(?:accDescription\s[^#\n;]+)/,/^(?:accTitle\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*\{\s*)/,/^(?:[\}])/,/^(?:[^\}]*)/,/^(?:%%(?!\{)*[^\n]*(\r?\n?)+)/,/^(?:%%[^\n]*(\r?\n)*)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:C4Context\b)/,/^(?:C4Container\b)/,/^(?:C4Component\b)/,/^(?:C4Dynamic\b)/,/^(?:C4Deployment\b)/,/^(?:Person_Ext\b)/,/^(?:Person\b)/,/^(?:SystemQueue_Ext\b)/,/^(?:SystemDb_Ext\b)/,/^(?:System_Ext\b)/,/^(?:SystemQueue\b)/,/^(?:SystemDb\b)/,/^(?:System\b)/,/^(?:Boundary\b)/,/^(?:Enterprise_Boundary\b)/,/^(?:System_Boundary\b)/,/^(?:ContainerQueue_Ext\b)/,/^(?:ContainerDb_Ext\b)/,/^(?:Container_Ext\b)/,/^(?:ContainerQueue\b)/,/^(?:ContainerDb\b)/,/^(?:Container\b)/,/^(?:Container_Boundary\b)/,/^(?:ComponentQueue_Ext\b)/,/^(?:ComponentDb_Ext\b)/,/^(?:Component_Ext\b)/,/^(?:ComponentQueue\b)/,/^(?:ComponentDb\b)/,/^(?:Component\b)/,/^(?:Deployment_Node\b)/,/^(?:Node\b)/,/^(?:Node_L\b)/,/^(?:Node_R\b)/,/^(?:Rel\b)/,/^(?:BiRel\b)/,/^(?:Rel_Up\b)/,/^(?:Rel_U\b)/,/^(?:Rel_Down\b)/,/^(?:Rel_D\b)/,/^(?:Rel_Left\b)/,/^(?:Rel_L\b)/,/^(?:Rel_Right\b)/,/^(?:Rel_R\b)/,/^(?:Rel_Back\b)/,/^(?:RelIndex\b)/,/^(?:UpdateElementStyle\b)/,/^(?:UpdateRelStyle\b)/,/^(?:UpdateLayoutConfig\b)/,/^(?:$)/,/^(?:[(][ ]*[,])/,/^(?:[(])/,/^(?:[)])/,/^(?:,,)/,/^(?:,)/,/^(?:[ ]*["]["])/,/^(?:[ ]*["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:[ ]*[\$])/,/^(?:[^=]*)/,/^(?:[=][ ]*["])/,/^(?:[^"]+)/,/^(?:["])/,/^(?:[^,]+)/,/^(?:\{)/,/^(?:\})/,/^(?:[\s]+)/,/^(?:[\n\r]+)/,/^(?:$)/],conditions:{acc_descr_multiline:{rules:[11,12],inclusive:!1},acc_descr:{rules:[9],inclusive:!1},acc_title:{rules:[7],inclusive:!1},string_kv_value:{rules:[78,79],inclusive:!1},string_kv_key:{rules:[77],inclusive:!1},string_kv:{rules:[76],inclusive:!1},string:{rules:[73,74],inclusive:!1},attribute:{rules:[68,69,70,71,72,75,80],inclusive:!1},update_layout_config:{rules:[65,66,67,68],inclusive:!1},update_rel_style:{rules:[65,66,67,68],inclusive:!1},update_el_style:{rules:[65,66,67,68],inclusive:!1},rel_b:{rules:[65,66,67,68],inclusive:!1},rel_r:{rules:[65,66,67,68],inclusive:!1},rel_l:{rules:[65,66,67,68],inclusive:!1},rel_d:{rules:[65,66,67,68],inclusive:!1},rel_u:{rules:[65,66,67,68],inclusive:!1},rel_bi:{rules:[],inclusive:!1},rel:{rules:[65,66,67,68],inclusive:!1},node_r:{rules:[65,66,67,68],inclusive:!1},node_l:{rules:[65,66,67,68],inclusive:!1},node:{rules:[65,66,67,68],inclusive:!1},index:{rules:[],inclusive:!1},rel_index:{rules:[65,66,67,68],inclusive:!1},component_ext_queue:{rules:[],inclusive:!1},component_ext_db:{rules:[65,66,67,68],inclusive:!1},component_ext:{rules:[65,66,67,68],inclusive:!1},component_queue:{rules:[65,66,67,68],inclusive:!1},component_db:{rules:[65,66,67,68],inclusive:!1},component:{rules:[65,66,67,68],inclusive:!1},container_boundary:{rules:[65,66,67,68],inclusive:!1},container_ext_queue:{rules:[65,66,67,68],inclusive:!1},container_ext_db:{rules:[65,66,67,68],inclusive:!1},container_ext:{rules:[65,66,67,68],inclusive:!1},container_queue:{rules:[65,66,67,68],inclusive:!1},container_db:{rules:[65,66,67,68],inclusive:!1},container:{rules:[65,66,67,68],inclusive:!1},birel:{rules:[65,66,67,68],inclusive:!1},system_boundary:{rules:[65,66,67,68],inclusive:!1},enterprise_boundary:{rules:[65,66,67,68],inclusive:!1},boundary:{rules:[65,66,67,68],inclusive:!1},system_ext_queue:{rules:[65,66,67,68],inclusive:!1},system_ext_db:{rules:[65,66,67,68],inclusive:!1},system_ext:{rules:[65,66,67,68],inclusive:!1},system_queue:{rules:[65,66,67,68],inclusive:!1},system_db:{rules:[65,66,67,68],inclusive:!1},system:{rules:[65,66,67,68],inclusive:!1},person_ext:{rules:[65,66,67,68],inclusive:!1},person:{rules:[65,66,67,68],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,8,10,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,81,82,83,84,85],inclusive:!0}}};return _e}();Se.lexer=Ue;function Pe(){this.yy={}}return o(Pe,"Parser"),Pe.prototype=Se,Se.Parser=Pe,new Pe}();U1.parser=U1;rz=U1});var UC,On,cp=R(()=>{"use strict";UC=o((t,e,{depth:r=2,clobber:n=!1}={})=>{let i={depth:r,clobber:n};return Array.isArray(e)&&!Array.isArray(t)?(e.forEach(a=>UC(t,a,i)),t):Array.isArray(e)&&Array.isArray(t)?(e.forEach(a=>{t.includes(a)||t.push(a)}),t):t===void 0||r<=0?t!=null&&typeof t=="object"&&typeof e=="object"?Object.assign(t,e):e:(e!==void 0&&typeof t=="object"&&typeof e=="object"&&Object.keys(e).forEach(a=>{typeof e[a]=="object"&&(t[a]===void 0||typeof t[a]=="object")?(t[a]===void 0&&(t[a]=Array.isArray(e[a])?[]:{}),t[a]=UC(t[a],e[a],{depth:r-1,clobber:n})):(n||typeof t[a]!="object"&&typeof e[a]!="object")&&(t[a]=e[a])}),t)},"assignWithDepth"),On=UC});var Hb,nz,iz=R(()=>{"use strict";Hb={min:{r:0,g:0,b:0,s:0,l:0,a:0},max:{r:255,g:255,b:255,h:360,s:100,l:100,a:1},clamp:{r:o(t=>t>=255?255:t<0?0:t,"r"),g:o(t=>t>=255?255:t<0?0:t,"g"),b:o(t=>t>=255?255:t<0?0:t,"b"),h:o(t=>t%360,"h"),s:o(t=>t>=100?100:t<0?0:t,"s"),l:o(t=>t>=100?100:t<0?0:t,"l"),a:o(t=>t>=1?1:t<0?0:t,"a")},toLinear:o(t=>{let e=t/255;return t>.03928?Math.pow((e+.055)/1.055,2.4):e/12.92},"toLinear"),hue2rgb:o((t,e,r)=>(r<0&&(r+=1),r>1&&(r-=1),r<.16666666666666666?t+(e-t)*6*r:r<.5?e:r<.6666666666666666?t+(e-t)*(.6666666666666666-r)*6:t),"hue2rgb"),hsl2rgb:o(({h:t,s:e,l:r},n)=>{if(!e)return r*2.55;t/=360,e/=100,r/=100;let i=r<.5?r*(1+e):r+e-r*e,a=2*r-i;switch(n){case"r":return Hb.hue2rgb(a,i,t+.3333333333333333)*255;case"g":return Hb.hue2rgb(a,i,t)*255;case"b":return Hb.hue2rgb(a,i,t-.3333333333333333)*255}},"hsl2rgb"),rgb2hsl:o(({r:t,g:e,b:r},n)=>{t/=255,e/=255,r/=255;let i=Math.max(t,e,r),a=Math.min(t,e,r),s=(i+a)/2;if(n==="l")return s*100;if(i===a)return 0;let l=i-a,u=s>.5?l/(2-i-a):l/(i+a);if(n==="s")return u*100;switch(i){case t:return((e-r)/l+(e{"use strict";h2e={clamp:o((t,e,r)=>e>r?Math.min(e,Math.max(r,t)):Math.min(r,Math.max(e,t)),"clamp"),round:o(t=>Math.round(t*1e10)/1e10,"round")},az=h2e});var f2e,oz,lz=R(()=>{"use strict";f2e={dec2hex:o(t=>{let e=Math.round(t).toString(16);return e.length>1?e:`0${e}`},"dec2hex")},oz=f2e});var d2e,Bt,jl=R(()=>{"use strict";iz();sz();lz();d2e={channel:nz,lang:az,unit:oz},Bt=d2e});var Jc,Li,H1=R(()=>{"use strict";jl();Jc={};for(let t=0;t<=255;t++)Jc[t]=Bt.unit.dec2hex(t);Li={ALL:0,RGB:1,HSL:2}});var HC,cz,uz=R(()=>{"use strict";H1();HC=class{static{o(this,"Type")}constructor(){this.type=Li.ALL}get(){return this.type}set(e){if(this.type&&this.type!==e)throw new Error("Cannot change both RGB and HSL channels at the same time");this.type=e}reset(){this.type=Li.ALL}is(e){return this.type===e}},cz=HC});var YC,hz,fz=R(()=>{"use strict";jl();uz();H1();YC=class{static{o(this,"Channels")}constructor(e,r){this.color=r,this.changed=!1,this.data=e,this.type=new cz}set(e,r){return this.color=r,this.changed=!1,this.data=e,this.type.type=Li.ALL,this}_ensureHSL(){let e=this.data,{h:r,s:n,l:i}=e;r===void 0&&(e.h=Bt.channel.rgb2hsl(e,"h")),n===void 0&&(e.s=Bt.channel.rgb2hsl(e,"s")),i===void 0&&(e.l=Bt.channel.rgb2hsl(e,"l"))}_ensureRGB(){let e=this.data,{r,g:n,b:i}=e;r===void 0&&(e.r=Bt.channel.hsl2rgb(e,"r")),n===void 0&&(e.g=Bt.channel.hsl2rgb(e,"g")),i===void 0&&(e.b=Bt.channel.hsl2rgb(e,"b"))}get r(){let e=this.data,r=e.r;return!this.type.is(Li.HSL)&&r!==void 0?r:(this._ensureHSL(),Bt.channel.hsl2rgb(e,"r"))}get g(){let e=this.data,r=e.g;return!this.type.is(Li.HSL)&&r!==void 0?r:(this._ensureHSL(),Bt.channel.hsl2rgb(e,"g"))}get b(){let e=this.data,r=e.b;return!this.type.is(Li.HSL)&&r!==void 0?r:(this._ensureHSL(),Bt.channel.hsl2rgb(e,"b"))}get h(){let e=this.data,r=e.h;return!this.type.is(Li.RGB)&&r!==void 0?r:(this._ensureRGB(),Bt.channel.rgb2hsl(e,"h"))}get s(){let e=this.data,r=e.s;return!this.type.is(Li.RGB)&&r!==void 0?r:(this._ensureRGB(),Bt.channel.rgb2hsl(e,"s"))}get l(){let e=this.data,r=e.l;return!this.type.is(Li.RGB)&&r!==void 0?r:(this._ensureRGB(),Bt.channel.rgb2hsl(e,"l"))}get a(){return this.data.a}set r(e){this.type.set(Li.RGB),this.changed=!0,this.data.r=e}set g(e){this.type.set(Li.RGB),this.changed=!0,this.data.g=e}set b(e){this.type.set(Li.RGB),this.changed=!0,this.data.b=e}set h(e){this.type.set(Li.HSL),this.changed=!0,this.data.h=e}set s(e){this.type.set(Li.HSL),this.changed=!0,this.data.s=e}set l(e){this.type.set(Li.HSL),this.changed=!0,this.data.l=e}set a(e){this.changed=!0,this.data.a=e}},hz=YC});var p2e,oh,Y1=R(()=>{"use strict";fz();p2e=new hz({r:0,g:0,b:0,a:0},"transparent"),oh=p2e});var dz,Yf,WC=R(()=>{"use strict";Y1();H1();dz={re:/^#((?:[a-f0-9]{2}){2,4}|[a-f0-9]{3})$/i,parse:o(t=>{if(t.charCodeAt(0)!==35)return;let e=t.match(dz.re);if(!e)return;let r=e[1],n=parseInt(r,16),i=r.length,a=i%4===0,s=i>4,l=s?1:17,u=s?8:4,h=a?0:-1,f=s?255:15;return oh.set({r:(n>>u*(h+3)&f)*l,g:(n>>u*(h+2)&f)*l,b:(n>>u*(h+1)&f)*l,a:a?(n&f)*l/255:1},t)},"parse"),stringify:o(t=>{let{r:e,g:r,b:n,a:i}=t;return i<1?`#${Jc[Math.round(e)]}${Jc[Math.round(r)]}${Jc[Math.round(n)]}${Jc[Math.round(i*255)]}`:`#${Jc[Math.round(e)]}${Jc[Math.round(r)]}${Jc[Math.round(n)]}`},"stringify")},Yf=dz});var Yb,W1,pz=R(()=>{"use strict";jl();Y1();Yb={re:/^hsla?\(\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?(?:deg|grad|rad|turn)?)\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?%)\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?%)(?:\s*?(?:,|\/)\s*?\+?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?(%)?))?\s*?\)$/i,hueRe:/^(.+?)(deg|grad|rad|turn)$/i,_hue2deg:o(t=>{let e=t.match(Yb.hueRe);if(e){let[,r,n]=e;switch(n){case"grad":return Bt.channel.clamp.h(parseFloat(r)*.9);case"rad":return Bt.channel.clamp.h(parseFloat(r)*180/Math.PI);case"turn":return Bt.channel.clamp.h(parseFloat(r)*360)}}return Bt.channel.clamp.h(parseFloat(t))},"_hue2deg"),parse:o(t=>{let e=t.charCodeAt(0);if(e!==104&&e!==72)return;let r=t.match(Yb.re);if(!r)return;let[,n,i,a,s,l]=r;return oh.set({h:Yb._hue2deg(n),s:Bt.channel.clamp.s(parseFloat(i)),l:Bt.channel.clamp.l(parseFloat(a)),a:s?Bt.channel.clamp.a(l?parseFloat(s)/100:parseFloat(s)):1},t)},"parse"),stringify:o(t=>{let{h:e,s:r,l:n,a:i}=t;return i<1?`hsla(${Bt.lang.round(e)}, ${Bt.lang.round(r)}%, ${Bt.lang.round(n)}%, ${i})`:`hsl(${Bt.lang.round(e)}, ${Bt.lang.round(r)}%, ${Bt.lang.round(n)}%)`},"stringify")},W1=Yb});var Wb,qC,mz=R(()=>{"use strict";WC();Wb={colors:{aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyanaqua:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",rebeccapurple:"#663399",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",transparent:"#00000000",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"},parse:o(t=>{t=t.toLowerCase();let e=Wb.colors[t];if(e)return Yf.parse(e)},"parse"),stringify:o(t=>{let e=Yf.stringify(t);for(let r in Wb.colors)if(Wb.colors[r]===e)return r},"stringify")},qC=Wb});var gz,q1,yz=R(()=>{"use strict";jl();Y1();gz={re:/^rgba?\(\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))(?:\s*?(?:,|\/)\s*?\+?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?)))?\s*?\)$/i,parse:o(t=>{let e=t.charCodeAt(0);if(e!==114&&e!==82)return;let r=t.match(gz.re);if(!r)return;let[,n,i,a,s,l,u,h,f]=r;return oh.set({r:Bt.channel.clamp.r(i?parseFloat(n)*2.55:parseFloat(n)),g:Bt.channel.clamp.g(s?parseFloat(a)*2.55:parseFloat(a)),b:Bt.channel.clamp.b(u?parseFloat(l)*2.55:parseFloat(l)),a:h?Bt.channel.clamp.a(f?parseFloat(h)/100:parseFloat(h)):1},t)},"parse"),stringify:o(t=>{let{r:e,g:r,b:n,a:i}=t;return i<1?`rgba(${Bt.lang.round(e)}, ${Bt.lang.round(r)}, ${Bt.lang.round(n)}, ${Bt.lang.round(i)})`:`rgb(${Bt.lang.round(e)}, ${Bt.lang.round(r)}, ${Bt.lang.round(n)})`},"stringify")},q1=gz});var m2e,Di,eu=R(()=>{"use strict";WC();pz();mz();yz();H1();m2e={format:{keyword:qC,hex:Yf,rgb:q1,rgba:q1,hsl:W1,hsla:W1},parse:o(t=>{if(typeof t!="string")return t;let e=Yf.parse(t)||q1.parse(t)||W1.parse(t)||qC.parse(t);if(e)return e;throw new Error(`Unsupported color format: "${t}"`)},"parse"),stringify:o(t=>!t.changed&&t.color?t.color:t.type.is(Li.HSL)||t.data.r===void 0?W1.stringify(t):t.a<1||!Number.isInteger(t.r)||!Number.isInteger(t.g)||!Number.isInteger(t.b)?q1.stringify(t):Yf.stringify(t),"stringify")},Di=m2e});var g2e,qb,XC=R(()=>{"use strict";jl();eu();g2e=o((t,e)=>{let r=Di.parse(t);for(let n in e)r[n]=Bt.channel.clamp[n](e[n]);return Di.stringify(r)},"change"),qb=g2e});var y2e,Ws,jC=R(()=>{"use strict";jl();Y1();eu();XC();y2e=o((t,e,r=0,n=1)=>{if(typeof t!="number")return qb(t,{a:e});let i=oh.set({r:Bt.channel.clamp.r(t),g:Bt.channel.clamp.g(e),b:Bt.channel.clamp.b(r),a:Bt.channel.clamp.a(n)});return Di.stringify(i)},"rgba"),Ws=y2e});var v2e,X1,vz=R(()=>{"use strict";jl();eu();v2e=o((t,e)=>Bt.lang.round(Di.parse(t)[e]),"channel"),X1=v2e});var x2e,xz,bz=R(()=>{"use strict";jl();eu();x2e=o(t=>{let{r:e,g:r,b:n}=Di.parse(t),i=.2126*Bt.channel.toLinear(e)+.7152*Bt.channel.toLinear(r)+.0722*Bt.channel.toLinear(n);return Bt.lang.round(i)},"luminance"),xz=x2e});var b2e,wz,Tz=R(()=>{"use strict";bz();b2e=o(t=>xz(t)>=.5,"isLight"),wz=b2e});var w2e,Wa,kz=R(()=>{"use strict";Tz();w2e=o(t=>!wz(t),"isDark"),Wa=w2e});var T2e,Xb,KC=R(()=>{"use strict";jl();eu();T2e=o((t,e,r)=>{let n=Di.parse(t),i=n[e],a=Bt.channel.clamp[e](i+r);return i!==a&&(n[e]=a),Di.stringify(n)},"adjustChannel"),Xb=T2e});var k2e,Et,Ez=R(()=>{"use strict";KC();k2e=o((t,e)=>Xb(t,"l",e),"lighten"),Et=k2e});var E2e,Dt,Cz=R(()=>{"use strict";KC();E2e=o((t,e)=>Xb(t,"l",-e),"darken"),Dt=E2e});var C2e,Oe,Sz=R(()=>{"use strict";eu();XC();C2e=o((t,e)=>{let r=Di.parse(t),n={};for(let i in e)e[i]&&(n[i]=r[i]+e[i]);return qb(t,n)},"adjust"),Oe=C2e});var S2e,Az,_z=R(()=>{"use strict";eu();jC();S2e=o((t,e,r=50)=>{let{r:n,g:i,b:a,a:s}=Di.parse(t),{r:l,g:u,b:h,a:f}=Di.parse(e),d=r/100,p=d*2-1,m=s-f,y=((p*m===-1?p:(p+m)/(1+p*m))+1)/2,v=1-y,x=n*y+l*v,b=i*y+u*v,w=a*y+h*v,S=s*d+f*(1-d);return Ws(x,b,w,S)},"mix"),Az=S2e});var A2e,ot,Lz=R(()=>{"use strict";eu();_z();A2e=o((t,e=100)=>{let r=Di.parse(t);return r.r=255-r.r,r.g=255-r.g,r.b=255-r.b,Az(r,t,e)},"invert"),ot=A2e});var Dz=R(()=>{"use strict";jC();vz();kz();Ez();Cz();Sz();Lz()});var al=R(()=>{"use strict";Dz()});var lh,ch,j1=R(()=>{"use strict";lh="#ffffff",ch="#f2f2f2"});var yi,up=R(()=>{"use strict";al();yi=o((t,e)=>e?Oe(t,{s:-40,l:10}):Oe(t,{s:-40,l:-10}),"mkBorder")});var QC,Nz,Mz=R(()=>{"use strict";al();j1();up();QC=class{static{o(this,"Theme")}constructor(){this.background="#f4f4f4",this.primaryColor="#fff4dd",this.noteBkgColor="#fff5ad",this.noteTextColor="#333",this.THEME_COLOR_LIMIT=12,this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px"}updateColors(){if(this.primaryTextColor=this.primaryTextColor||(this.darkMode?"#eee":"#333"),this.secondaryColor=this.secondaryColor||Oe(this.primaryColor,{h:-120}),this.tertiaryColor=this.tertiaryColor||Oe(this.primaryColor,{h:180,l:5}),this.primaryBorderColor=this.primaryBorderColor||yi(this.primaryColor,this.darkMode),this.secondaryBorderColor=this.secondaryBorderColor||yi(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=this.tertiaryBorderColor||yi(this.tertiaryColor,this.darkMode),this.noteBorderColor=this.noteBorderColor||yi(this.noteBkgColor,this.darkMode),this.noteBkgColor=this.noteBkgColor||"#fff5ad",this.noteTextColor=this.noteTextColor||"#333",this.secondaryTextColor=this.secondaryTextColor||ot(this.secondaryColor),this.tertiaryTextColor=this.tertiaryTextColor||ot(this.tertiaryColor),this.lineColor=this.lineColor||ot(this.background),this.arrowheadColor=this.arrowheadColor||ot(this.background),this.textColor=this.textColor||this.primaryTextColor,this.border2=this.border2||this.tertiaryBorderColor,this.nodeBkg=this.nodeBkg||this.primaryColor,this.mainBkg=this.mainBkg||this.primaryColor,this.nodeBorder=this.nodeBorder||this.primaryBorderColor,this.clusterBkg=this.clusterBkg||this.tertiaryColor,this.clusterBorder=this.clusterBorder||this.tertiaryBorderColor,this.defaultLinkColor=this.defaultLinkColor||this.lineColor,this.titleColor=this.titleColor||this.tertiaryTextColor,this.edgeLabelBackground=this.edgeLabelBackground||(this.darkMode?Dt(this.secondaryColor,30):this.secondaryColor),this.nodeTextColor=this.nodeTextColor||this.primaryTextColor,this.actorBorder=this.actorBorder||this.primaryBorderColor,this.actorBkg=this.actorBkg||this.mainBkg,this.actorTextColor=this.actorTextColor||this.primaryTextColor,this.actorLineColor=this.actorLineColor||this.actorBorder,this.labelBoxBkgColor=this.labelBoxBkgColor||this.actorBkg,this.signalColor=this.signalColor||this.textColor,this.signalTextColor=this.signalTextColor||this.textColor,this.labelBoxBorderColor=this.labelBoxBorderColor||this.actorBorder,this.labelTextColor=this.labelTextColor||this.actorTextColor,this.loopTextColor=this.loopTextColor||this.actorTextColor,this.activationBorderColor=this.activationBorderColor||Dt(this.secondaryColor,10),this.activationBkgColor=this.activationBkgColor||this.secondaryColor,this.sequenceNumberColor=this.sequenceNumberColor||ot(this.lineColor),this.sectionBkgColor=this.sectionBkgColor||this.tertiaryColor,this.altSectionBkgColor=this.altSectionBkgColor||"white",this.sectionBkgColor=this.sectionBkgColor||this.secondaryColor,this.sectionBkgColor2=this.sectionBkgColor2||this.primaryColor,this.excludeBkgColor=this.excludeBkgColor||"#eeeeee",this.taskBorderColor=this.taskBorderColor||this.primaryBorderColor,this.taskBkgColor=this.taskBkgColor||this.primaryColor,this.activeTaskBorderColor=this.activeTaskBorderColor||this.primaryColor,this.activeTaskBkgColor=this.activeTaskBkgColor||Et(this.primaryColor,23),this.gridColor=this.gridColor||"lightgrey",this.doneTaskBkgColor=this.doneTaskBkgColor||"lightgrey",this.doneTaskBorderColor=this.doneTaskBorderColor||"grey",this.critBorderColor=this.critBorderColor||"#ff8888",this.critBkgColor=this.critBkgColor||"red",this.todayLineColor=this.todayLineColor||"red",this.taskTextColor=this.taskTextColor||this.textColor,this.taskTextOutsideColor=this.taskTextOutsideColor||this.textColor,this.taskTextLightColor=this.taskTextLightColor||this.textColor,this.taskTextColor=this.taskTextColor||this.primaryTextColor,this.taskTextDarkColor=this.taskTextDarkColor||this.textColor,this.taskTextClickableColor=this.taskTextClickableColor||"#003163",this.personBorder=this.personBorder||this.primaryBorderColor,this.personBkg=this.personBkg||this.mainBkg,this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||this.tertiaryColor,this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.nodeBorder,this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.transitionColor=this.transitionColor||this.lineColor,this.specialStateColor=this.lineColor,this.cScale0=this.cScale0||this.primaryColor,this.cScale1=this.cScale1||this.secondaryColor,this.cScale2=this.cScale2||this.tertiaryColor,this.cScale3=this.cScale3||Oe(this.primaryColor,{h:30}),this.cScale4=this.cScale4||Oe(this.primaryColor,{h:60}),this.cScale5=this.cScale5||Oe(this.primaryColor,{h:90}),this.cScale6=this.cScale6||Oe(this.primaryColor,{h:120}),this.cScale7=this.cScale7||Oe(this.primaryColor,{h:150}),this.cScale8=this.cScale8||Oe(this.primaryColor,{h:210,l:150}),this.cScale9=this.cScale9||Oe(this.primaryColor,{h:270}),this.cScale10=this.cScale10||Oe(this.primaryColor,{h:300}),this.cScale11=this.cScale11||Oe(this.primaryColor,{h:330}),this.darkMode)for(let r=0;r{this[n]=e[n]}),this.updateColors(),r.forEach(n=>{this[n]=e[n]})}},Nz=o(t=>{let e=new QC;return e.calculate(t),e},"getThemeVariables")});var ZC,Iz,Oz=R(()=>{"use strict";al();up();ZC=class{static{o(this,"Theme")}constructor(){this.background="#333",this.primaryColor="#1f2020",this.secondaryColor=Et(this.primaryColor,16),this.tertiaryColor=Oe(this.primaryColor,{h:-160}),this.primaryBorderColor=ot(this.background),this.secondaryBorderColor=yi(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=yi(this.tertiaryColor,this.darkMode),this.primaryTextColor=ot(this.primaryColor),this.secondaryTextColor=ot(this.secondaryColor),this.tertiaryTextColor=ot(this.tertiaryColor),this.lineColor=ot(this.background),this.textColor=ot(this.background),this.mainBkg="#1f2020",this.secondBkg="calculated",this.mainContrastColor="lightgrey",this.darkTextColor=Et(ot("#323D47"),10),this.lineColor="calculated",this.border1="#ccc",this.border2=Ws(255,255,255,.25),this.arrowheadColor="calculated",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.labelBackground="#181818",this.textColor="#ccc",this.THEME_COLOR_LIMIT=12,this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="#F9FFFE",this.edgeLabelBackground="calculated",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="calculated",this.actorLineColor="calculated",this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="calculated",this.activationBkgColor="calculated",this.sequenceNumberColor="black",this.sectionBkgColor=Dt("#EAE8D9",30),this.altSectionBkgColor="calculated",this.sectionBkgColor2="#EAE8D9",this.excludeBkgColor=Dt(this.sectionBkgColor,10),this.taskBorderColor=Ws(255,255,255,70),this.taskBkgColor="calculated",this.taskTextColor="calculated",this.taskTextLightColor="calculated",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor=Ws(255,255,255,50),this.activeTaskBkgColor="#81B1DB",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="grey",this.critBorderColor="#E83737",this.critBkgColor="#E83737",this.taskTextDarkColor="calculated",this.todayLineColor="#DB5757",this.personBorder=this.primaryBorderColor,this.personBkg=this.mainBkg,this.archEdgeColor="calculated",this.archEdgeArrowColor="calculated",this.archEdgeWidth="3",this.archGroupBorderColor=this.primaryBorderColor,this.archGroupBorderWidth="2px",this.labelColor="calculated",this.errorBkgColor="#a44141",this.errorTextColor="#ddd"}updateColors(){this.secondBkg=Et(this.mainBkg,16),this.lineColor=this.mainContrastColor,this.arrowheadColor=this.mainContrastColor,this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.edgeLabelBackground=Et(this.labelBackground,25),this.actorBorder=this.border1,this.actorBkg=this.mainBkg,this.actorTextColor=this.mainContrastColor,this.actorLineColor=this.actorBorder,this.signalColor=this.mainContrastColor,this.signalTextColor=this.mainContrastColor,this.labelBoxBkgColor=this.actorBkg,this.labelBoxBorderColor=this.actorBorder,this.labelTextColor=this.mainContrastColor,this.loopTextColor=this.mainContrastColor,this.noteBorderColor=this.secondaryBorderColor,this.noteBkgColor=this.secondBkg,this.noteTextColor=this.secondaryTextColor,this.activationBorderColor=this.border1,this.activationBkgColor=this.secondBkg,this.altSectionBkgColor=this.background,this.taskBkgColor=Et(this.mainBkg,23),this.taskTextColor=this.darkTextColor,this.taskTextLightColor=this.mainContrastColor,this.taskTextOutsideColor=this.taskTextLightColor,this.gridColor=this.mainContrastColor,this.doneTaskBkgColor=this.mainContrastColor,this.taskTextDarkColor=this.darkTextColor,this.archEdgeColor=this.lineColor,this.archEdgeArrowColor=this.lineColor,this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||"#555",this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.primaryBorderColor,this.specialStateColor="#f4f4f4",this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=Oe(this.primaryColor,{h:64}),this.fillType3=Oe(this.secondaryColor,{h:64}),this.fillType4=Oe(this.primaryColor,{h:-64}),this.fillType5=Oe(this.secondaryColor,{h:-64}),this.fillType6=Oe(this.primaryColor,{h:128}),this.fillType7=Oe(this.secondaryColor,{h:128}),this.cScale1=this.cScale1||"#0b0000",this.cScale2=this.cScale2||"#4d1037",this.cScale3=this.cScale3||"#3f5258",this.cScale4=this.cScale4||"#4f2f1b",this.cScale5=this.cScale5||"#6e0a0a",this.cScale6=this.cScale6||"#3b0048",this.cScale7=this.cScale7||"#995a01",this.cScale8=this.cScale8||"#154706",this.cScale9=this.cScale9||"#161722",this.cScale10=this.cScale10||"#00296f",this.cScale11=this.cScale11||"#01629c",this.cScale12=this.cScale12||"#010029",this.cScale0=this.cScale0||this.primaryColor,this.cScale1=this.cScale1||this.secondaryColor,this.cScale2=this.cScale2||this.tertiaryColor,this.cScale3=this.cScale3||Oe(this.primaryColor,{h:30}),this.cScale4=this.cScale4||Oe(this.primaryColor,{h:60}),this.cScale5=this.cScale5||Oe(this.primaryColor,{h:90}),this.cScale6=this.cScale6||Oe(this.primaryColor,{h:120}),this.cScale7=this.cScale7||Oe(this.primaryColor,{h:150}),this.cScale8=this.cScale8||Oe(this.primaryColor,{h:210}),this.cScale9=this.cScale9||Oe(this.primaryColor,{h:270}),this.cScale10=this.cScale10||Oe(this.primaryColor,{h:300}),this.cScale11=this.cScale11||Oe(this.primaryColor,{h:330});for(let e=0;e{this[n]=e[n]}),this.updateColors(),r.forEach(n=>{this[n]=e[n]})}},Iz=o(t=>{let e=new ZC;return e.calculate(t),e},"getThemeVariables")});var JC,hp,jb=R(()=>{"use strict";al();up();j1();JC=class{static{o(this,"Theme")}constructor(){this.background="#f4f4f4",this.primaryColor="#ECECFF",this.secondaryColor=Oe(this.primaryColor,{h:120}),this.secondaryColor="#ffffde",this.tertiaryColor=Oe(this.primaryColor,{h:-160}),this.primaryBorderColor=yi(this.primaryColor,this.darkMode),this.secondaryBorderColor=yi(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=yi(this.tertiaryColor,this.darkMode),this.primaryTextColor=ot(this.primaryColor),this.secondaryTextColor=ot(this.secondaryColor),this.tertiaryTextColor=ot(this.tertiaryColor),this.lineColor=ot(this.background),this.textColor=ot(this.background),this.background="white",this.mainBkg="#ECECFF",this.secondBkg="#ffffde",this.lineColor="#333333",this.border1="#9370DB",this.border2="#aaaa33",this.arrowheadColor="#333333",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.labelBackground="rgba(232,232,232, 0.8)",this.textColor="#333",this.THEME_COLOR_LIMIT=12,this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="calculated",this.edgeLabelBackground="calculated",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="black",this.actorLineColor="calculated",this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="calculated",this.altSectionBkgColor="calculated",this.sectionBkgColor2="calculated",this.excludeBkgColor="#eeeeee",this.taskBorderColor="calculated",this.taskBkgColor="calculated",this.taskTextLightColor="calculated",this.taskTextColor=this.taskTextLightColor,this.taskTextDarkColor="calculated",this.taskTextOutsideColor=this.taskTextDarkColor,this.taskTextClickableColor="calculated",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="calculated",this.critBorderColor="calculated",this.critBkgColor="calculated",this.todayLineColor="calculated",this.sectionBkgColor=Ws(102,102,255,.49),this.altSectionBkgColor="white",this.sectionBkgColor2="#fff400",this.taskBorderColor="#534fbc",this.taskBkgColor="#8a90dd",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="black",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="#534fbc",this.activeTaskBkgColor="#bfc7ff",this.gridColor="lightgrey",this.doneTaskBkgColor="lightgrey",this.doneTaskBorderColor="grey",this.critBorderColor="#ff8888",this.critBkgColor="red",this.todayLineColor="red",this.personBorder=this.primaryBorderColor,this.personBkg=this.mainBkg,this.archEdgeColor="calculated",this.archEdgeArrowColor="calculated",this.archEdgeWidth="3",this.archGroupBorderColor=this.primaryBorderColor,this.archGroupBorderWidth="2px",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222",this.updateColors()}updateColors(){this.cScale0=this.cScale0||this.primaryColor,this.cScale1=this.cScale1||this.secondaryColor,this.cScale2=this.cScale2||this.tertiaryColor,this.cScale3=this.cScale3||Oe(this.primaryColor,{h:30}),this.cScale4=this.cScale4||Oe(this.primaryColor,{h:60}),this.cScale5=this.cScale5||Oe(this.primaryColor,{h:90}),this.cScale6=this.cScale6||Oe(this.primaryColor,{h:120}),this.cScale7=this.cScale7||Oe(this.primaryColor,{h:150}),this.cScale8=this.cScale8||Oe(this.primaryColor,{h:210}),this.cScale9=this.cScale9||Oe(this.primaryColor,{h:270}),this.cScale10=this.cScale10||Oe(this.primaryColor,{h:300}),this.cScale11=this.cScale11||Oe(this.primaryColor,{h:330}),this.cScalePeer1=this.cScalePeer1||Dt(this.secondaryColor,45),this.cScalePeer2=this.cScalePeer2||Dt(this.tertiaryColor,40);for(let e=0;e{this[n]=e[n]}),this.updateColors(),r.forEach(n=>{this[n]=e[n]})}},hp=o(t=>{let e=new JC;return e.calculate(t),e},"getThemeVariables")});var e7,Pz,Bz=R(()=>{"use strict";al();j1();up();e7=class{static{o(this,"Theme")}constructor(){this.background="#f4f4f4",this.primaryColor="#cde498",this.secondaryColor="#cdffb2",this.background="white",this.mainBkg="#cde498",this.secondBkg="#cdffb2",this.lineColor="green",this.border1="#13540c",this.border2="#6eaa49",this.arrowheadColor="green",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.tertiaryColor=Et("#cde498",10),this.primaryBorderColor=yi(this.primaryColor,this.darkMode),this.secondaryBorderColor=yi(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=yi(this.tertiaryColor,this.darkMode),this.primaryTextColor=ot(this.primaryColor),this.secondaryTextColor=ot(this.secondaryColor),this.tertiaryTextColor=ot(this.primaryColor),this.lineColor=ot(this.background),this.textColor=ot(this.background),this.THEME_COLOR_LIMIT=12,this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="#333",this.edgeLabelBackground="#e8e8e8",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="black",this.actorLineColor="calculated",this.signalColor="#333",this.signalTextColor="#333",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="#326932",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="#6eaa49",this.altSectionBkgColor="white",this.sectionBkgColor2="#6eaa49",this.excludeBkgColor="#eeeeee",this.taskBorderColor="calculated",this.taskBkgColor="#487e3a",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="black",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="lightgrey",this.doneTaskBkgColor="lightgrey",this.doneTaskBorderColor="grey",this.critBorderColor="#ff8888",this.critBkgColor="red",this.todayLineColor="red",this.personBorder=this.primaryBorderColor,this.personBkg=this.mainBkg,this.archEdgeColor="calculated",this.archEdgeArrowColor="calculated",this.archEdgeWidth="3",this.archGroupBorderColor=this.primaryBorderColor,this.archGroupBorderWidth="2px",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222"}updateColors(){this.actorBorder=Dt(this.mainBkg,20),this.actorBkg=this.mainBkg,this.labelBoxBkgColor=this.actorBkg,this.labelTextColor=this.actorTextColor,this.loopTextColor=this.actorTextColor,this.noteBorderColor=this.border2,this.noteTextColor=this.actorTextColor,this.actorLineColor=this.actorBorder,this.cScale0=this.cScale0||this.primaryColor,this.cScale1=this.cScale1||this.secondaryColor,this.cScale2=this.cScale2||this.tertiaryColor,this.cScale3=this.cScale3||Oe(this.primaryColor,{h:30}),this.cScale4=this.cScale4||Oe(this.primaryColor,{h:60}),this.cScale5=this.cScale5||Oe(this.primaryColor,{h:90}),this.cScale6=this.cScale6||Oe(this.primaryColor,{h:120}),this.cScale7=this.cScale7||Oe(this.primaryColor,{h:150}),this.cScale8=this.cScale8||Oe(this.primaryColor,{h:210}),this.cScale9=this.cScale9||Oe(this.primaryColor,{h:270}),this.cScale10=this.cScale10||Oe(this.primaryColor,{h:300}),this.cScale11=this.cScale11||Oe(this.primaryColor,{h:330}),this.cScalePeer1=this.cScalePeer1||Dt(this.secondaryColor,45),this.cScalePeer2=this.cScalePeer2||Dt(this.tertiaryColor,40);for(let e=0;e{this[n]=e[n]}),this.updateColors(),r.forEach(n=>{this[n]=e[n]})}},Pz=o(t=>{let e=new e7;return e.calculate(t),e},"getThemeVariables")});var t7,Fz,zz=R(()=>{"use strict";al();up();j1();t7=class{static{o(this,"Theme")}constructor(){this.primaryColor="#eee",this.contrast="#707070",this.secondaryColor=Et(this.contrast,55),this.background="#ffffff",this.tertiaryColor=Oe(this.primaryColor,{h:-160}),this.primaryBorderColor=yi(this.primaryColor,this.darkMode),this.secondaryBorderColor=yi(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=yi(this.tertiaryColor,this.darkMode),this.primaryTextColor=ot(this.primaryColor),this.secondaryTextColor=ot(this.secondaryColor),this.tertiaryTextColor=ot(this.tertiaryColor),this.lineColor=ot(this.background),this.textColor=ot(this.background),this.mainBkg="#eee",this.secondBkg="calculated",this.lineColor="#666",this.border1="#999",this.border2="calculated",this.note="#ffa",this.text="#333",this.critical="#d42",this.done="#bbb",this.arrowheadColor="#333333",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.THEME_COLOR_LIMIT=12,this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="calculated",this.edgeLabelBackground="white",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="calculated",this.actorLineColor=this.actorBorder,this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="calculated",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="calculated",this.altSectionBkgColor="white",this.sectionBkgColor2="calculated",this.excludeBkgColor="#eeeeee",this.taskBorderColor="calculated",this.taskBkgColor="calculated",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="calculated",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="calculated",this.critBkgColor="calculated",this.critBorderColor="calculated",this.todayLineColor="calculated",this.personBorder=this.primaryBorderColor,this.personBkg=this.mainBkg,this.archEdgeColor="calculated",this.archEdgeArrowColor="calculated",this.archEdgeWidth="3",this.archGroupBorderColor=this.primaryBorderColor,this.archGroupBorderWidth="2px",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222"}updateColors(){this.secondBkg=Et(this.contrast,55),this.border2=this.contrast,this.actorBorder=Et(this.border1,23),this.actorBkg=this.mainBkg,this.actorTextColor=this.text,this.actorLineColor=this.actorBorder,this.signalColor=this.text,this.signalTextColor=this.text,this.labelBoxBkgColor=this.actorBkg,this.labelBoxBorderColor=this.actorBorder,this.labelTextColor=this.text,this.loopTextColor=this.text,this.noteBorderColor="#999",this.noteBkgColor="#666",this.noteTextColor="#fff",this.cScale0=this.cScale0||"#555",this.cScale1=this.cScale1||"#F4F4F4",this.cScale2=this.cScale2||"#555",this.cScale3=this.cScale3||"#BBB",this.cScale4=this.cScale4||"#777",this.cScale5=this.cScale5||"#999",this.cScale6=this.cScale6||"#DDD",this.cScale7=this.cScale7||"#FFF",this.cScale8=this.cScale8||"#DDD",this.cScale9=this.cScale9||"#BBB",this.cScale10=this.cScale10||"#999",this.cScale11=this.cScale11||"#777";for(let e=0;e{this[n]=e[n]}),this.updateColors(),r.forEach(n=>{this[n]=e[n]})}},Fz=o(t=>{let e=new t7;return e.calculate(t),e},"getThemeVariables")});var Co,Kb=R(()=>{"use strict";Mz();Oz();jb();Bz();zz();Co={base:{getThemeVariables:Nz},dark:{getThemeVariables:Iz},default:{getThemeVariables:hp},forest:{getThemeVariables:Pz},neutral:{getThemeVariables:Fz}}});var tu,Gz=R(()=>{"use strict";tu={flowchart:{useMaxWidth:!0,titleTopMargin:25,subGraphTitleMargin:{top:0,bottom:0},diagramPadding:8,htmlLabels:!0,nodeSpacing:50,rankSpacing:50,curve:"basis",padding:15,defaultRenderer:"dagre-wrapper",wrappingWidth:200},sequence:{useMaxWidth:!0,hideUnusedParticipants:!1,activationWidth:10,diagramMarginX:50,diagramMarginY:10,actorMargin:50,width:150,height:65,boxMargin:10,boxTextMargin:5,noteMargin:10,messageMargin:35,messageAlign:"center",mirrorActors:!0,forceMenus:!1,bottomMarginAdj:1,rightAngles:!1,showSequenceNumbers:!1,actorFontSize:14,actorFontFamily:'"Open Sans", sans-serif',actorFontWeight:400,noteFontSize:14,noteFontFamily:'"trebuchet ms", verdana, arial, sans-serif',noteFontWeight:400,noteAlign:"center",messageFontSize:16,messageFontFamily:'"trebuchet ms", verdana, arial, sans-serif',messageFontWeight:400,wrap:!1,wrapPadding:10,labelBoxWidth:50,labelBoxHeight:20},gantt:{useMaxWidth:!0,titleTopMargin:25,barHeight:20,barGap:4,topPadding:50,rightPadding:75,leftPadding:75,gridLineStartPadding:35,fontSize:11,sectionFontSize:11,numberSectionStyles:4,axisFormat:"%Y-%m-%d",topAxis:!1,displayMode:"",weekday:"sunday"},journey:{useMaxWidth:!0,diagramMarginX:50,diagramMarginY:10,leftMargin:150,width:150,height:50,boxMargin:10,boxTextMargin:5,noteMargin:10,messageMargin:35,messageAlign:"center",bottomMarginAdj:1,rightAngles:!1,taskFontSize:14,taskFontFamily:'"Open Sans", sans-serif',taskMargin:50,activationWidth:10,textPlacement:"fo",actorColours:["#8FBC8F","#7CFC00","#00FFFF","#20B2AA","#B0E0E6","#FFFFE0"],sectionFills:["#191970","#8B008B","#4B0082","#2F4F4F","#800000","#8B4513","#00008B"],sectionColours:["#fff"]},class:{useMaxWidth:!0,titleTopMargin:25,arrowMarkerAbsolute:!1,dividerMargin:10,padding:5,textHeight:10,defaultRenderer:"dagre-wrapper",htmlLabels:!1},state:{useMaxWidth:!0,titleTopMargin:25,dividerMargin:10,sizeUnit:5,padding:8,textHeight:10,titleShift:-15,noteMargin:10,forkWidth:70,forkHeight:7,miniPadding:2,fontSizeFactor:5.02,fontSize:24,labelHeight:16,edgeLengthFactor:"20",compositTitleSize:35,radius:5,defaultRenderer:"dagre-wrapper"},er:{useMaxWidth:!0,titleTopMargin:25,diagramPadding:20,layoutDirection:"TB",minEntityWidth:100,minEntityHeight:75,entityPadding:15,stroke:"gray",fill:"honeydew",fontSize:12},pie:{useMaxWidth:!0,textPosition:.75},quadrantChart:{useMaxWidth:!0,chartWidth:500,chartHeight:500,titleFontSize:20,titlePadding:10,quadrantPadding:5,xAxisLabelPadding:5,yAxisLabelPadding:5,xAxisLabelFontSize:16,yAxisLabelFontSize:16,quadrantLabelFontSize:16,quadrantTextTopPadding:5,pointTextPadding:5,pointLabelFontSize:12,pointRadius:5,xAxisPosition:"top",yAxisPosition:"left",quadrantInternalBorderStrokeWidth:1,quadrantExternalBorderStrokeWidth:2},xyChart:{useMaxWidth:!0,width:700,height:500,titleFontSize:20,titlePadding:10,showTitle:!0,xAxis:{$ref:"#/$defs/XYChartAxisConfig",showLabel:!0,labelFontSize:14,labelPadding:5,showTitle:!0,titleFontSize:16,titlePadding:5,showTick:!0,tickLength:5,tickWidth:2,showAxisLine:!0,axisLineWidth:2},yAxis:{$ref:"#/$defs/XYChartAxisConfig",showLabel:!0,labelFontSize:14,labelPadding:5,showTitle:!0,titleFontSize:16,titlePadding:5,showTick:!0,tickLength:5,tickWidth:2,showAxisLine:!0,axisLineWidth:2},chartOrientation:"vertical",plotReservedSpacePercent:50},requirement:{useMaxWidth:!0,rect_fill:"#f9f9f9",text_color:"#333",rect_border_size:"0.5px",rect_border_color:"#bbb",rect_min_width:200,rect_min_height:200,fontSize:14,rect_padding:10,line_height:20},mindmap:{useMaxWidth:!0,padding:10,maxNodeWidth:200},timeline:{useMaxWidth:!0,diagramMarginX:50,diagramMarginY:10,leftMargin:150,width:150,height:50,boxMargin:10,boxTextMargin:5,noteMargin:10,messageMargin:35,messageAlign:"center",bottomMarginAdj:1,rightAngles:!1,taskFontSize:14,taskFontFamily:'"Open Sans", sans-serif',taskMargin:50,activationWidth:10,textPlacement:"fo",actorColours:["#8FBC8F","#7CFC00","#00FFFF","#20B2AA","#B0E0E6","#FFFFE0"],sectionFills:["#191970","#8B008B","#4B0082","#2F4F4F","#800000","#8B4513","#00008B"],sectionColours:["#fff"],disableMulticolor:!1},gitGraph:{useMaxWidth:!0,titleTopMargin:25,diagramPadding:8,nodeLabel:{width:75,height:100,x:-25,y:0},mainBranchName:"main",mainBranchOrder:0,showCommitLabel:!0,showBranches:!0,rotateCommitLabel:!0,parallelCommits:!1,arrowMarkerAbsolute:!1},c4:{useMaxWidth:!0,diagramMarginX:50,diagramMarginY:10,c4ShapeMargin:50,c4ShapePadding:20,width:216,height:60,boxMargin:10,c4ShapeInRow:4,nextLinePaddingX:0,c4BoundaryInRow:2,personFontSize:14,personFontFamily:'"Open Sans", sans-serif',personFontWeight:"normal",external_personFontSize:14,external_personFontFamily:'"Open Sans", sans-serif',external_personFontWeight:"normal",systemFontSize:14,systemFontFamily:'"Open Sans", sans-serif',systemFontWeight:"normal",external_systemFontSize:14,external_systemFontFamily:'"Open Sans", sans-serif',external_systemFontWeight:"normal",system_dbFontSize:14,system_dbFontFamily:'"Open Sans", sans-serif',system_dbFontWeight:"normal",external_system_dbFontSize:14,external_system_dbFontFamily:'"Open Sans", sans-serif',external_system_dbFontWeight:"normal",system_queueFontSize:14,system_queueFontFamily:'"Open Sans", sans-serif',system_queueFontWeight:"normal",external_system_queueFontSize:14,external_system_queueFontFamily:'"Open Sans", sans-serif',external_system_queueFontWeight:"normal",boundaryFontSize:14,boundaryFontFamily:'"Open Sans", sans-serif',boundaryFontWeight:"normal",messageFontSize:12,messageFontFamily:'"Open Sans", sans-serif',messageFontWeight:"normal",containerFontSize:14,containerFontFamily:'"Open Sans", sans-serif',containerFontWeight:"normal",external_containerFontSize:14,external_containerFontFamily:'"Open Sans", sans-serif',external_containerFontWeight:"normal",container_dbFontSize:14,container_dbFontFamily:'"Open Sans", sans-serif',container_dbFontWeight:"normal",external_container_dbFontSize:14,external_container_dbFontFamily:'"Open Sans", sans-serif',external_container_dbFontWeight:"normal",container_queueFontSize:14,container_queueFontFamily:'"Open Sans", sans-serif',container_queueFontWeight:"normal",external_container_queueFontSize:14,external_container_queueFontFamily:'"Open Sans", sans-serif',external_container_queueFontWeight:"normal",componentFontSize:14,componentFontFamily:'"Open Sans", sans-serif',componentFontWeight:"normal",external_componentFontSize:14,external_componentFontFamily:'"Open Sans", sans-serif',external_componentFontWeight:"normal",component_dbFontSize:14,component_dbFontFamily:'"Open Sans", sans-serif',component_dbFontWeight:"normal",external_component_dbFontSize:14,external_component_dbFontFamily:'"Open Sans", sans-serif',external_component_dbFontWeight:"normal",component_queueFontSize:14,component_queueFontFamily:'"Open Sans", sans-serif',component_queueFontWeight:"normal",external_component_queueFontSize:14,external_component_queueFontFamily:'"Open Sans", sans-serif',external_component_queueFontWeight:"normal",wrap:!0,wrapPadding:10,person_bg_color:"#08427B",person_border_color:"#073B6F",external_person_bg_color:"#686868",external_person_border_color:"#8A8A8A",system_bg_color:"#1168BD",system_border_color:"#3C7FC0",system_db_bg_color:"#1168BD",system_db_border_color:"#3C7FC0",system_queue_bg_color:"#1168BD",system_queue_border_color:"#3C7FC0",external_system_bg_color:"#999999",external_system_border_color:"#8A8A8A",external_system_db_bg_color:"#999999",external_system_db_border_color:"#8A8A8A",external_system_queue_bg_color:"#999999",external_system_queue_border_color:"#8A8A8A",container_bg_color:"#438DD5",container_border_color:"#3C7FC0",container_db_bg_color:"#438DD5",container_db_border_color:"#3C7FC0",container_queue_bg_color:"#438DD5",container_queue_border_color:"#3C7FC0",external_container_bg_color:"#B3B3B3",external_container_border_color:"#A6A6A6",external_container_db_bg_color:"#B3B3B3",external_container_db_border_color:"#A6A6A6",external_container_queue_bg_color:"#B3B3B3",external_container_queue_border_color:"#A6A6A6",component_bg_color:"#85BBF0",component_border_color:"#78A8D8",component_db_bg_color:"#85BBF0",component_db_border_color:"#78A8D8",component_queue_bg_color:"#85BBF0",component_queue_border_color:"#78A8D8",external_component_bg_color:"#CCCCCC",external_component_border_color:"#BFBFBF",external_component_db_bg_color:"#CCCCCC",external_component_db_border_color:"#BFBFBF",external_component_queue_bg_color:"#CCCCCC",external_component_queue_border_color:"#BFBFBF"},sankey:{useMaxWidth:!0,width:600,height:400,linkColor:"gradient",nodeAlignment:"justify",showValues:!0,prefix:"",suffix:""},block:{useMaxWidth:!0,padding:8},packet:{useMaxWidth:!0,rowHeight:32,bitWidth:32,bitsPerRow:32,showBits:!0,paddingX:5,paddingY:5},architecture:{useMaxWidth:!0,padding:40,iconSize:80,fontSize:16},theme:"default",look:"classic",handDrawnSeed:0,layout:"dagre",maxTextSize:5e4,maxEdges:500,darkMode:!1,fontFamily:'"trebuchet ms", verdana, arial, sans-serif;',logLevel:5,securityLevel:"strict",startOnLoad:!0,arrowMarkerAbsolute:!1,secure:["secure","securityLevel","startOnLoad","maxTextSize","suppressErrorRendering","maxEdges"],legacyMathML:!1,forceLegacyMathML:!1,deterministicIds:!1,fontSize:16,markdownAutoWrap:!0,suppressErrorRendering:!1}});var $z,Vz,Uz,mr,sl=R(()=>{"use strict";Kb();Gz();$z={...tu,deterministicIDSeed:void 0,elk:{mergeEdges:!1,nodePlacementStrategy:"SIMPLE"},themeCSS:void 0,themeVariables:Co.default.getThemeVariables(),sequence:{...tu.sequence,messageFont:o(function(){return{fontFamily:this.messageFontFamily,fontSize:this.messageFontSize,fontWeight:this.messageFontWeight}},"messageFont"),noteFont:o(function(){return{fontFamily:this.noteFontFamily,fontSize:this.noteFontSize,fontWeight:this.noteFontWeight}},"noteFont"),actorFont:o(function(){return{fontFamily:this.actorFontFamily,fontSize:this.actorFontSize,fontWeight:this.actorFontWeight}},"actorFont")},gantt:{...tu.gantt,tickInterval:void 0,useWidth:void 0},c4:{...tu.c4,useWidth:void 0,personFont:o(function(){return{fontFamily:this.personFontFamily,fontSize:this.personFontSize,fontWeight:this.personFontWeight}},"personFont"),external_personFont:o(function(){return{fontFamily:this.external_personFontFamily,fontSize:this.external_personFontSize,fontWeight:this.external_personFontWeight}},"external_personFont"),systemFont:o(function(){return{fontFamily:this.systemFontFamily,fontSize:this.systemFontSize,fontWeight:this.systemFontWeight}},"systemFont"),external_systemFont:o(function(){return{fontFamily:this.external_systemFontFamily,fontSize:this.external_systemFontSize,fontWeight:this.external_systemFontWeight}},"external_systemFont"),system_dbFont:o(function(){return{fontFamily:this.system_dbFontFamily,fontSize:this.system_dbFontSize,fontWeight:this.system_dbFontWeight}},"system_dbFont"),external_system_dbFont:o(function(){return{fontFamily:this.external_system_dbFontFamily,fontSize:this.external_system_dbFontSize,fontWeight:this.external_system_dbFontWeight}},"external_system_dbFont"),system_queueFont:o(function(){return{fontFamily:this.system_queueFontFamily,fontSize:this.system_queueFontSize,fontWeight:this.system_queueFontWeight}},"system_queueFont"),external_system_queueFont:o(function(){return{fontFamily:this.external_system_queueFontFamily,fontSize:this.external_system_queueFontSize,fontWeight:this.external_system_queueFontWeight}},"external_system_queueFont"),containerFont:o(function(){return{fontFamily:this.containerFontFamily,fontSize:this.containerFontSize,fontWeight:this.containerFontWeight}},"containerFont"),external_containerFont:o(function(){return{fontFamily:this.external_containerFontFamily,fontSize:this.external_containerFontSize,fontWeight:this.external_containerFontWeight}},"external_containerFont"),container_dbFont:o(function(){return{fontFamily:this.container_dbFontFamily,fontSize:this.container_dbFontSize,fontWeight:this.container_dbFontWeight}},"container_dbFont"),external_container_dbFont:o(function(){return{fontFamily:this.external_container_dbFontFamily,fontSize:this.external_container_dbFontSize,fontWeight:this.external_container_dbFontWeight}},"external_container_dbFont"),container_queueFont:o(function(){return{fontFamily:this.container_queueFontFamily,fontSize:this.container_queueFontSize,fontWeight:this.container_queueFontWeight}},"container_queueFont"),external_container_queueFont:o(function(){return{fontFamily:this.external_container_queueFontFamily,fontSize:this.external_container_queueFontSize,fontWeight:this.external_container_queueFontWeight}},"external_container_queueFont"),componentFont:o(function(){return{fontFamily:this.componentFontFamily,fontSize:this.componentFontSize,fontWeight:this.componentFontWeight}},"componentFont"),external_componentFont:o(function(){return{fontFamily:this.external_componentFontFamily,fontSize:this.external_componentFontSize,fontWeight:this.external_componentFontWeight}},"external_componentFont"),component_dbFont:o(function(){return{fontFamily:this.component_dbFontFamily,fontSize:this.component_dbFontSize,fontWeight:this.component_dbFontWeight}},"component_dbFont"),external_component_dbFont:o(function(){return{fontFamily:this.external_component_dbFontFamily,fontSize:this.external_component_dbFontSize,fontWeight:this.external_component_dbFontWeight}},"external_component_dbFont"),component_queueFont:o(function(){return{fontFamily:this.component_queueFontFamily,fontSize:this.component_queueFontSize,fontWeight:this.component_queueFontWeight}},"component_queueFont"),external_component_queueFont:o(function(){return{fontFamily:this.external_component_queueFontFamily,fontSize:this.external_component_queueFontSize,fontWeight:this.external_component_queueFontWeight}},"external_component_queueFont"),boundaryFont:o(function(){return{fontFamily:this.boundaryFontFamily,fontSize:this.boundaryFontSize,fontWeight:this.boundaryFontWeight}},"boundaryFont"),messageFont:o(function(){return{fontFamily:this.messageFontFamily,fontSize:this.messageFontSize,fontWeight:this.messageFontWeight}},"messageFont")},pie:{...tu.pie,useWidth:984},xyChart:{...tu.xyChart,useWidth:void 0},requirement:{...tu.requirement,useWidth:void 0},packet:{...tu.packet}},Vz=o((t,e="")=>Object.keys(t).reduce((r,n)=>Array.isArray(t[n])?r:typeof t[n]=="object"&&t[n]!==null?[...r,e+n,...Vz(t[n],"")]:[...r,e+n],[]),"keyify"),Uz=new Set(Vz($z,"")),mr=$z});var fp,_2e,r7=R(()=>{"use strict";sl();ut();fp=o(t=>{if(V.debug("sanitizeDirective called with",t),!(typeof t!="object"||t==null)){if(Array.isArray(t)){t.forEach(e=>fp(e));return}for(let e of Object.keys(t)){if(V.debug("Checking key",e),e.startsWith("__")||e.includes("proto")||e.includes("constr")||!Uz.has(e)||t[e]==null){V.debug("sanitize deleting key: ",e),delete t[e];continue}if(typeof t[e]=="object"){V.debug("sanitizing object",e),fp(t[e]);continue}let r=["themeCSS","fontFamily","altFontFamily"];for(let n of r)e.includes(n)&&(V.debug("sanitizing css option",e),t[e]=_2e(t[e]))}if(t.themeVariables)for(let e of Object.keys(t.themeVariables)){let r=t.themeVariables[e];r?.match&&!r.match(/^[\d "#%(),.;A-Za-z]+$/)&&(t.themeVariables[e]="")}V.debug("After sanitization",t)}},"sanitizeDirective"),_2e=o(t=>{let e=0,r=0;for(let n of t){if(e{"use strict";cp();ut();Kb();sl();r7();uh=Object.freeze(mr),fs=On({},uh),dp=[],K1=On({},uh),Qb=o((t,e)=>{let r=On({},t),n={};for(let i of e)Xz(i),n=On(n,i);if(r=On(r,n),n.theme&&n.theme in Co){let i=On({},Yz),a=On(i.themeVariables||{},n.themeVariables);r.theme&&r.theme in Co&&(r.themeVariables=Co[r.theme].getThemeVariables(a))}return K1=r,Kz(K1),K1},"updateCurrentConfig"),n7=o(t=>(fs=On({},uh),fs=On(fs,t),t.theme&&Co[t.theme]&&(fs.themeVariables=Co[t.theme].getThemeVariables(t.themeVariables)),Qb(fs,dp),fs),"setSiteConfig"),Wz=o(t=>{Yz=On({},t)},"saveConfigFromInitialize"),qz=o(t=>(fs=On(fs,t),Qb(fs,dp),fs),"updateSiteConfig"),i7=o(()=>On({},fs),"getSiteConfig"),Zb=o(t=>(Kz(t),On(K1,t),Or()),"setConfig"),Or=o(()=>On({},K1),"getConfig"),Xz=o(t=>{t&&(["secure",...fs.secure??[]].forEach(e=>{Object.hasOwn(t,e)&&(V.debug(`Denied attempt to modify a secure key ${e}`,t[e]),delete t[e])}),Object.keys(t).forEach(e=>{e.startsWith("__")&&delete t[e]}),Object.keys(t).forEach(e=>{typeof t[e]=="string"&&(t[e].includes("<")||t[e].includes(">")||t[e].includes("url(data:"))&&delete t[e],typeof t[e]=="object"&&Xz(t[e])}))},"sanitize"),jz=o(t=>{fp(t),t.fontFamily&&!t.themeVariables?.fontFamily&&(t.themeVariables={...t.themeVariables,fontFamily:t.fontFamily}),dp.push(t),Qb(fs,dp)},"addDirective"),Q1=o((t=fs)=>{dp=[],Qb(t,dp)},"reset"),L2e={LAZY_LOAD_DEPRECATED:"The configuration options lazyLoadedDiagrams and loadExternalDiagramsAtStartup are deprecated. Please use registerExternalDiagrams instead."},Hz={},D2e=o(t=>{Hz[t]||(V.warn(L2e[t]),Hz[t]=!0)},"issueWarning"),Kz=o(t=>{t&&(t.lazyLoadedDiagrams||t.loadExternalDiagramsAtStartup)&&D2e("LAZY_LOAD_DEPRECATED")},"checkConfig")});var o7=gi((a7,s7)=>{"use strict";(function(t,e){typeof a7=="object"&&typeof s7<"u"?s7.exports=e():typeof define=="function"&&define.amd?define(e):(t=typeof globalThis<"u"?globalThis:t||self,t.DOMPurify=e())})(a7,function(){"use strict";let{entries:t,setPrototypeOf:e,isFrozen:r,getPrototypeOf:n,getOwnPropertyDescriptor:i}=Object,{freeze:a,seal:s,create:l}=Object,{apply:u,construct:h}=typeof Reflect<"u"&&Reflect;a||(a=o(function(Se){return Se},"freeze")),s||(s=o(function(Se){return Se},"seal")),u||(u=o(function(Se,Ue,Pe){return Se.apply(Ue,Pe)},"apply")),h||(h=o(function(Se,Ue){return new Se(...Ue)},"construct"));let f=E(Array.prototype.forEach),d=E(Array.prototype.pop),p=E(Array.prototype.push),m=E(String.prototype.toLowerCase),g=E(String.prototype.toString),y=E(String.prototype.match),v=E(String.prototype.replace),x=E(String.prototype.indexOf),b=E(String.prototype.trim),w=E(Object.prototype.hasOwnProperty),S=E(RegExp.prototype.test),T=_(TypeError);function E(Ie){return function(Se){for(var Ue=arguments.length,Pe=new Array(Ue>1?Ue-1:0),_e=1;_e2&&arguments[2]!==void 0?arguments[2]:m;e&&e(Ie,null);let Pe=Se.length;for(;Pe--;){let _e=Se[Pe];if(typeof _e=="string"){let me=Ue(_e);me!==_e&&(r(Se)||(Se[Pe]=me),_e=me)}Ie[_e]=!0}return Ie}o(A,"addToSet");function L(Ie){for(let Se=0;Se/gm),ie=s(/\${[\w\W]*}/gm),j=s(/^data-[\-\w.\u00B7-\uFFFF]/),J=s(/^aria-[\-\w]+$/),Z=s(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),H=s(/^(?:\w+script|data):/i),q=s(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),K=s(/^html$/i),se=s(/^[a-z][.\w]*(-[.\w]+)+$/i);var ce=Object.freeze({__proto__:null,MUSTACHE_EXPR:Q,ERB_EXPR:X,TMPLIT_EXPR:ie,DATA_ATTR:j,ARIA_ATTR:J,IS_ALLOWED_URI:Z,IS_SCRIPT_OR_DATA:H,ATTR_WHITESPACE:q,DOCTYPE_NAME:K,CUSTOM_ELEMENT:se});let ue={element:1,attribute:2,text:3,cdataSection:4,entityReference:5,entityNode:6,progressingInstruction:7,comment:8,document:9,documentType:10,documentFragment:11,notation:12},te=o(function(){return typeof window>"u"?null:window},"getGlobal"),De=o(function(Se,Ue){if(typeof Se!="object"||typeof Se.createPolicy!="function")return null;let Pe=null,_e="data-tt-policy-suffix";Ue&&Ue.hasAttribute(_e)&&(Pe=Ue.getAttribute(_e));let me="dompurify"+(Pe?"#"+Pe:"");try{return Se.createPolicy(me,{createHTML(W){return W},createScriptURL(W){return W}})}catch{return console.warn("TrustedTypes policy "+me+" could not be created."),null}},"_createTrustedTypesPolicy");function oe(){let Ie=arguments.length>0&&arguments[0]!==void 0?arguments[0]:te(),Se=o(Ft=>oe(Ft),"DOMPurify");if(Se.version="3.1.6",Se.removed=[],!Ie||!Ie.document||Ie.document.nodeType!==ue.document)return Se.isSupported=!1,Se;let{document:Ue}=Ie,Pe=Ue,_e=Pe.currentScript,{DocumentFragment:me,HTMLTemplateElement:W,Node:fe,Element:ge,NodeFilter:re,NamedNodeMap:he=Ie.NamedNodeMap||Ie.MozNamedAttrMap,HTMLFormElement:ne,DOMParser:ae,trustedTypes:we}=Ie,Te=ge.prototype,Ce=N(Te,"cloneNode"),Ae=N(Te,"remove"),Ge=N(Te,"nextSibling"),Me=N(Te,"childNodes"),ye=N(Te,"parentNode");if(typeof W=="function"){let Ft=Ue.createElement("template");Ft.content&&Ft.content.ownerDocument&&(Ue=Ft.content.ownerDocument)}let He,ze="",{implementation:Ze,createNodeIterator:gt,createDocumentFragment:yt,getElementsByTagName:tt}=Ue,{importNode:Ye}=Pe,Je={};Se.isSupported=typeof t=="function"&&typeof ye=="function"&&Ze&&Ze.createHTMLDocument!==void 0;let{MUSTACHE_EXPR:Ve,ERB_EXPR:je,TMPLIT_EXPR:kt,DATA_ATTR:at,ARIA_ATTR:xt,IS_SCRIPT_OR_DATA:it,ATTR_WHITESPACE:dt,CUSTOM_ELEMENT:lt}=ce,{IS_ALLOWED_URI:It}=ce,mt=null,St=A({},[...k,...I,...C,...D,...F]),gr=null,xn=A({},[...B,...$,...z,...Y]),jt=Object.seal(l(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),rn=null,Er=null,Kn=!0,hn=!0,Qn=!1,on=!0,Rn=!1,Ha=!0,_a=!1,To=!1,qi=!1,ht=!1,At=!1,$t=!1,rt=!0,Ot=!1,pe="user-content-",ur=!0,be=!1,Ir={},Xc=null,M1=A({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]),_b=null,I1=A({},["audio","video","img","source","image","track"]),O1=null,ci=A({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),ko="http://www.w3.org/1998/Math/MathML",ih="http://www.w3.org/2000/svg",Us="http://www.w3.org/1999/xhtml",ah=Us,Lb=!1,P1=null,sa=A({},[ko,ih,Us],g),jc=null,Kc=["application/xhtml+xml","text/html"],us="text/html",_i=null,Wl=null,sh=Ue.createElement("form"),zf=o(function(Re){return Re instanceof RegExp||Re instanceof Function},"isRegexOrFunction"),Hs=o(function(){let Re=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};if(!(Wl&&Wl===Re)){if((!Re||typeof Re!="object")&&(Re={}),Re=M(Re),jc=Kc.indexOf(Re.PARSER_MEDIA_TYPE)===-1?us:Re.PARSER_MEDIA_TYPE,_i=jc==="application/xhtml+xml"?g:m,mt=w(Re,"ALLOWED_TAGS")?A({},Re.ALLOWED_TAGS,_i):St,gr=w(Re,"ALLOWED_ATTR")?A({},Re.ALLOWED_ATTR,_i):xn,P1=w(Re,"ALLOWED_NAMESPACES")?A({},Re.ALLOWED_NAMESPACES,g):sa,O1=w(Re,"ADD_URI_SAFE_ATTR")?A(M(ci),Re.ADD_URI_SAFE_ATTR,_i):ci,_b=w(Re,"ADD_DATA_URI_TAGS")?A(M(I1),Re.ADD_DATA_URI_TAGS,_i):I1,Xc=w(Re,"FORBID_CONTENTS")?A({},Re.FORBID_CONTENTS,_i):M1,rn=w(Re,"FORBID_TAGS")?A({},Re.FORBID_TAGS,_i):{},Er=w(Re,"FORBID_ATTR")?A({},Re.FORBID_ATTR,_i):{},Ir=w(Re,"USE_PROFILES")?Re.USE_PROFILES:!1,Kn=Re.ALLOW_ARIA_ATTR!==!1,hn=Re.ALLOW_DATA_ATTR!==!1,Qn=Re.ALLOW_UNKNOWN_PROTOCOLS||!1,on=Re.ALLOW_SELF_CLOSE_IN_ATTR!==!1,Rn=Re.SAFE_FOR_TEMPLATES||!1,Ha=Re.SAFE_FOR_XML!==!1,_a=Re.WHOLE_DOCUMENT||!1,ht=Re.RETURN_DOM||!1,At=Re.RETURN_DOM_FRAGMENT||!1,$t=Re.RETURN_TRUSTED_TYPE||!1,qi=Re.FORCE_BODY||!1,rt=Re.SANITIZE_DOM!==!1,Ot=Re.SANITIZE_NAMED_PROPS||!1,ur=Re.KEEP_CONTENT!==!1,be=Re.IN_PLACE||!1,It=Re.ALLOWED_URI_REGEXP||Z,ah=Re.NAMESPACE||Us,jt=Re.CUSTOM_ELEMENT_HANDLING||{},Re.CUSTOM_ELEMENT_HANDLING&&zf(Re.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(jt.tagNameCheck=Re.CUSTOM_ELEMENT_HANDLING.tagNameCheck),Re.CUSTOM_ELEMENT_HANDLING&&zf(Re.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(jt.attributeNameCheck=Re.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),Re.CUSTOM_ELEMENT_HANDLING&&typeof Re.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements=="boolean"&&(jt.allowCustomizedBuiltInElements=Re.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),Rn&&(hn=!1),At&&(ht=!0),Ir&&(mt=A({},F),gr=[],Ir.html===!0&&(A(mt,k),A(gr,B)),Ir.svg===!0&&(A(mt,I),A(gr,$),A(gr,Y)),Ir.svgFilters===!0&&(A(mt,C),A(gr,$),A(gr,Y)),Ir.mathMl===!0&&(A(mt,D),A(gr,z),A(gr,Y))),Re.ADD_TAGS&&(mt===St&&(mt=M(mt)),A(mt,Re.ADD_TAGS,_i)),Re.ADD_ATTR&&(gr===xn&&(gr=M(gr)),A(gr,Re.ADD_ATTR,_i)),Re.ADD_URI_SAFE_ATTR&&A(O1,Re.ADD_URI_SAFE_ATTR,_i),Re.FORBID_CONTENTS&&(Xc===M1&&(Xc=M(Xc)),A(Xc,Re.FORBID_CONTENTS,_i)),ur&&(mt["#text"]=!0),_a&&A(mt,["html","head","body"]),mt.table&&(A(mt,["tbody"]),delete rn.tbody),Re.TRUSTED_TYPES_POLICY){if(typeof Re.TRUSTED_TYPES_POLICY.createHTML!="function")throw T('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if(typeof Re.TRUSTED_TYPES_POLICY.createScriptURL!="function")throw T('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');He=Re.TRUSTED_TYPES_POLICY,ze=He.createHTML("")}else He===void 0&&(He=De(we,_e)),He!==null&&typeof ze=="string"&&(ze=He.createHTML(""));a&&a(Re),Wl=Re}},"_parseConfig"),B1=A({},["mi","mo","mn","ms","mtext"]),Gf=A({},["foreignobject","annotation-xml"]),F1=A({},["title","style","font","a","script"]),La=A({},[...I,...C,...O]),vF=A({},[...D,...P]),Ive=o(function(Re){let st=ye(Re);(!st||!st.tagName)&&(st={namespaceURI:ah,tagName:"template"});let Rt=m(Re.tagName),bn=m(st.tagName);return P1[Re.namespaceURI]?Re.namespaceURI===ih?st.namespaceURI===Us?Rt==="svg":st.namespaceURI===ko?Rt==="svg"&&(bn==="annotation-xml"||B1[bn]):!!La[Rt]:Re.namespaceURI===ko?st.namespaceURI===Us?Rt==="math":st.namespaceURI===ih?Rt==="math"&&Gf[bn]:!!vF[Rt]:Re.namespaceURI===Us?st.namespaceURI===ih&&!Gf[bn]||st.namespaceURI===ko&&!B1[bn]?!1:!vF[Rt]&&(F1[Rt]||!La[Rt]):!!(jc==="application/xhtml+xml"&&P1[Re.namespaceURI]):!1},"_checkValidNamespace"),ql=o(function(Re){p(Se.removed,{element:Re});try{ye(Re).removeChild(Re)}catch{Ae(Re)}},"_forceRemove"),Db=o(function(Re,st){try{p(Se.removed,{attribute:st.getAttributeNode(Re),from:st})}catch{p(Se.removed,{attribute:null,from:st})}if(st.removeAttribute(Re),Re==="is"&&!gr[Re])if(ht||At)try{ql(st)}catch{}else try{st.setAttribute(Re,"")}catch{}},"_removeAttribute"),xF=o(function(Re){let st=null,Rt=null;if(qi)Re=""+Re;else{let oa=y(Re,/^[\r\n\t ]+/);Rt=oa&&oa[0]}jc==="application/xhtml+xml"&&ah===Us&&(Re=''+Re+"");let bn=He?He.createHTML(Re):Re;if(ah===Us)try{st=new ae().parseFromString(bn,jc)}catch{}if(!st||!st.documentElement){st=Ze.createDocument(ah,"template",null);try{st.documentElement.innerHTML=Lb?ze:bn}catch{}}let Da=st.body||st.documentElement;return Re&&Rt&&Da.insertBefore(Ue.createTextNode(Rt),Da.childNodes[0]||null),ah===Us?tt.call(st,_a?"html":"body")[0]:_a?st.documentElement:Da},"_initDocument"),bF=o(function(Re){return gt.call(Re.ownerDocument||Re,Re,re.SHOW_ELEMENT|re.SHOW_COMMENT|re.SHOW_TEXT|re.SHOW_PROCESSING_INSTRUCTION|re.SHOW_CDATA_SECTION,null)},"_createNodeIterator"),wF=o(function(Re){return Re instanceof ne&&(typeof Re.nodeName!="string"||typeof Re.textContent!="string"||typeof Re.removeChild!="function"||!(Re.attributes instanceof he)||typeof Re.removeAttribute!="function"||typeof Re.setAttribute!="function"||typeof Re.namespaceURI!="string"||typeof Re.insertBefore!="function"||typeof Re.hasChildNodes!="function")},"_isClobbered"),TF=o(function(Re){return typeof fe=="function"&&Re instanceof fe},"_isNode"),Qc=o(function(Re,st,Rt){Je[Re]&&f(Je[Re],bn=>{bn.call(Se,st,Rt,Wl)})},"_executeHook"),kF=o(function(Re){let st=null;if(Qc("beforeSanitizeElements",Re,null),wF(Re))return ql(Re),!0;let Rt=_i(Re.nodeName);if(Qc("uponSanitizeElement",Re,{tagName:Rt,allowedTags:mt}),Re.hasChildNodes()&&!TF(Re.firstElementChild)&&S(/<[/\w]/g,Re.innerHTML)&&S(/<[/\w]/g,Re.textContent)||Re.nodeType===ue.progressingInstruction||Ha&&Re.nodeType===ue.comment&&S(/<[/\w]/g,Re.data))return ql(Re),!0;if(!mt[Rt]||rn[Rt]){if(!rn[Rt]&&CF(Rt)&&(jt.tagNameCheck instanceof RegExp&&S(jt.tagNameCheck,Rt)||jt.tagNameCheck instanceof Function&&jt.tagNameCheck(Rt)))return!1;if(ur&&!Xc[Rt]){let bn=ye(Re)||Re.parentNode,Da=Me(Re)||Re.childNodes;if(Da&&bn){let oa=Da.length;for(let hs=oa-1;hs>=0;--hs){let Xl=Ce(Da[hs],!0);Xl.__removalCount=(Re.__removalCount||0)+1,bn.insertBefore(Xl,Ge(Re))}}}return ql(Re),!0}return Re instanceof ge&&!Ive(Re)||(Rt==="noscript"||Rt==="noembed"||Rt==="noframes")&&S(/<\/no(script|embed|frames)/i,Re.innerHTML)?(ql(Re),!0):(Rn&&Re.nodeType===ue.text&&(st=Re.textContent,f([Ve,je,kt],bn=>{st=v(st,bn," ")}),Re.textContent!==st&&(p(Se.removed,{element:Re.cloneNode()}),Re.textContent=st)),Qc("afterSanitizeElements",Re,null),!1)},"_sanitizeElements"),EF=o(function(Re,st,Rt){if(rt&&(st==="id"||st==="name")&&(Rt in Ue||Rt in sh))return!1;if(!(hn&&!Er[st]&&S(at,st))){if(!(Kn&&S(xt,st))){if(!gr[st]||Er[st]){if(!(CF(Re)&&(jt.tagNameCheck instanceof RegExp&&S(jt.tagNameCheck,Re)||jt.tagNameCheck instanceof Function&&jt.tagNameCheck(Re))&&(jt.attributeNameCheck instanceof RegExp&&S(jt.attributeNameCheck,st)||jt.attributeNameCheck instanceof Function&&jt.attributeNameCheck(st))||st==="is"&&jt.allowCustomizedBuiltInElements&&(jt.tagNameCheck instanceof RegExp&&S(jt.tagNameCheck,Rt)||jt.tagNameCheck instanceof Function&&jt.tagNameCheck(Rt))))return!1}else if(!O1[st]){if(!S(It,v(Rt,dt,""))){if(!((st==="src"||st==="xlink:href"||st==="href")&&Re!=="script"&&x(Rt,"data:")===0&&_b[Re])){if(!(Qn&&!S(it,v(Rt,dt,"")))){if(Rt)return!1}}}}}}return!0},"_isValidAttribute"),CF=o(function(Re){return Re!=="annotation-xml"&&y(Re,lt)},"_isBasicCustomElement"),SF=o(function(Re){Qc("beforeSanitizeAttributes",Re,null);let{attributes:st}=Re;if(!st)return;let Rt={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:gr},bn=st.length;for(;bn--;){let Da=st[bn],{name:oa,namespaceURI:hs,value:Xl}=Da,z1=_i(oa),Ya=oa==="value"?Xl:b(Xl);if(Rt.attrName=z1,Rt.attrValue=Ya,Rt.keepAttr=!0,Rt.forceKeepAttr=void 0,Qc("uponSanitizeAttribute",Re,Rt),Ya=Rt.attrValue,Ha&&S(/((--!?|])>)|<\/(style|title)/i,Ya)){Db(oa,Re);continue}if(Rt.forceKeepAttr||(Db(oa,Re),!Rt.keepAttr))continue;if(!on&&S(/\/>/i,Ya)){Db(oa,Re);continue}Rn&&f([Ve,je,kt],_F=>{Ya=v(Ya,_F," ")});let AF=_i(Re.nodeName);if(EF(AF,z1,Ya)){if(Ot&&(z1==="id"||z1==="name")&&(Db(oa,Re),Ya=pe+Ya),He&&typeof we=="object"&&typeof we.getAttributeType=="function"&&!hs)switch(we.getAttributeType(AF,z1)){case"TrustedHTML":{Ya=He.createHTML(Ya);break}case"TrustedScriptURL":{Ya=He.createScriptURL(Ya);break}}try{hs?Re.setAttributeNS(hs,oa,Ya):Re.setAttribute(oa,Ya),wF(Re)?ql(Re):d(Se.removed)}catch{}}}Qc("afterSanitizeAttributes",Re,null)},"_sanitizeAttributes"),Ove=o(function Ft(Re){let st=null,Rt=bF(Re);for(Qc("beforeSanitizeShadowDOM",Re,null);st=Rt.nextNode();)Qc("uponSanitizeShadowNode",st,null),!kF(st)&&(st.content instanceof me&&Ft(st.content),SF(st));Qc("afterSanitizeShadowDOM",Re,null)},"_sanitizeShadowDOM");return Se.sanitize=function(Ft){let Re=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},st=null,Rt=null,bn=null,Da=null;if(Lb=!Ft,Lb&&(Ft=""),typeof Ft!="string"&&!TF(Ft))if(typeof Ft.toString=="function"){if(Ft=Ft.toString(),typeof Ft!="string")throw T("dirty is not a string, aborting")}else throw T("toString is not a function");if(!Se.isSupported)return Ft;if(To||Hs(Re),Se.removed=[],typeof Ft=="string"&&(be=!1),be){if(Ft.nodeName){let Xl=_i(Ft.nodeName);if(!mt[Xl]||rn[Xl])throw T("root node is forbidden and cannot be sanitized in-place")}}else if(Ft instanceof fe)st=xF(""),Rt=st.ownerDocument.importNode(Ft,!0),Rt.nodeType===ue.element&&Rt.nodeName==="BODY"||Rt.nodeName==="HTML"?st=Rt:st.appendChild(Rt);else{if(!ht&&!Rn&&!_a&&Ft.indexOf("<")===-1)return He&&$t?He.createHTML(Ft):Ft;if(st=xF(Ft),!st)return ht?null:$t?ze:""}st&&qi&&ql(st.firstChild);let oa=bF(be?Ft:st);for(;bn=oa.nextNode();)kF(bn)||(bn.content instanceof me&&Ove(bn.content),SF(bn));if(be)return Ft;if(ht){if(At)for(Da=yt.call(st.ownerDocument);st.firstChild;)Da.appendChild(st.firstChild);else Da=st;return(gr.shadowroot||gr.shadowrootmode)&&(Da=Ye.call(Pe,Da,!0)),Da}let hs=_a?st.outerHTML:st.innerHTML;return _a&&mt["!doctype"]&&st.ownerDocument&&st.ownerDocument.doctype&&st.ownerDocument.doctype.name&&S(K,st.ownerDocument.doctype.name)&&(hs=" +`+hs),Rn&&f([Ve,je,kt],Xl=>{hs=v(hs,Xl," ")}),He&&$t?He.createHTML(hs):hs},Se.setConfig=function(){let Ft=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};Hs(Ft),To=!0},Se.clearConfig=function(){Wl=null,To=!1},Se.isValidAttribute=function(Ft,Re,st){Wl||Hs({});let Rt=_i(Ft),bn=_i(Re);return EF(Rt,bn,st)},Se.addHook=function(Ft,Re){typeof Re=="function"&&(Je[Ft]=Je[Ft]||[],p(Je[Ft],Re))},Se.removeHook=function(Ft){if(Je[Ft])return d(Je[Ft])},Se.removeHooks=function(Ft){Je[Ft]&&(Je[Ft]=[])},Se.removeAllHooks=function(){Je={}},Se}o(oe,"createDOMPurify");var ke=oe();return ke})});var k$={};hr(k$,{default:()=>Ebe});function B2e(t){return String(t).replace(P2e,e=>O2e[e])}function $2e(t){if(t.default)return t.default;var e=t.type,r=Array.isArray(e)?e[0]:e;if(typeof r!="string")return r.enum[0];switch(r){case"boolean":return!1;case"string":return"";case"number":return 0;case"object":return{}}}function X2e(t){for(var e=0;e=i[0]&&t<=i[1])return r.name}return null}function LG(t){for(var e=0;e=h4[e]&&t<=h4[e+1])return!0;return!1}function axe(t,e){Zl[t]=e}function M7(t,e,r){if(!Zl[e])throw new Error("Font metrics not found for font: "+e+".");var n=t.charCodeAt(0),i=Zl[e][n];if(!i&&t[0]in Zz&&(n=Zz[t[0]].charCodeAt(0),i=Zl[e][n]),!i&&r==="text"&&LG(n)&&(i=Zl[e][77]),i)return{depth:i[0],height:i[1],italic:i[2],skew:i[3],width:i[4]}}function sxe(t){var e;if(t>=5?e=0:t>=3?e=1:e=2,!l7[e]){var r=l7[e]={cssEmPerMu:Jb.quad[e]/18};for(var n in Jb)Jb.hasOwnProperty(n)&&(r[n]=Jb[n][e])}return l7[e]}function tG(t){if(t instanceof ms)return t;throw new Error("Expected symbolNode but got "+String(t)+".")}function uxe(t){if(t instanceof jf)return t;throw new Error("Expected span but got "+String(t)+".")}function G(t,e,r,n,i,a){wn[t][i]={font:e,group:r,replace:n},a&&n&&(wn[t][n]=wn[t][i])}function vt(t){for(var{type:e,names:r,props:n,handler:i,htmlBuilder:a,mathmlBuilder:s}=t,l={type:e,numArgs:n.numArgs,argTypes:n.argTypes,allowedInArgument:!!n.allowedInArgument,allowedInText:!!n.allowedInText,allowedInMath:n.allowedInMath===void 0?!0:n.allowedInMath,numOptionalArgs:n.numOptionalArgs||0,infix:!!n.infix,primitive:!!n.primitive,handler:i},u=0;u0&&(a.push(s4(s,e)),s=[]),a.push(n[l]));s.length>0&&a.push(s4(s,e));var h;r?(h=s4(Ri(r,e,!0)),h.classes=["tag"],a.push(h)):i&&a.push(i);var f=su(["katex-html"],a);if(f.setAttribute("aria-hidden","true"),h){var d=h.children[0];d.style.height=ct(f.height+f.depth),f.depth&&(d.style.verticalAlign=ct(-f.depth))}return f}function $G(t){return new Xf(t)}function sG(t,e,r,n,i){var a=gs(t,r),s;a.length===1&&a[0]instanceof ps&&Vt.contains(["mrow","mtable"],a[0].type)?s=a[0]:s=new et.MathNode("mrow",a);var l=new et.MathNode("annotation",[new et.TextNode(e)]);l.setAttribute("encoding","application/x-tex");var u=new et.MathNode("semantics",[s,l]),h=new et.MathNode("math",[u]);h.setAttribute("xmlns","http://www.w3.org/1998/Math/MathML"),n&&h.setAttribute("display","block");var f=i?"katex":"katex-mathml";return Be.makeSpan([f],[h])}function ir(t,e){if(!t||t.type!==e)throw new Error("Expected node of type "+e+", but got "+(t?"node of type "+t.type:String(t)));return t}function B7(t){var e=T4(t);if(!e)throw new Error("Expected node of symbol group type, but got "+(t?"node of type "+t.type:String(t)));return e}function T4(t){return t&&(t.type==="atom"||fxe.hasOwnProperty(t.type))?t:null}function YG(t,e){var r=Ri(t.body,e,!0);return Vxe([t.mclass],r,e)}function WG(t,e){var r,n=gs(t.body,e);return t.mclass==="minner"?r=new et.MathNode("mpadded",n):t.mclass==="mord"?t.isCharacterBox?(r=n[0],r.type="mi"):r=new et.MathNode("mi",n):(t.isCharacterBox?(r=n[0],r.type="mo"):r=new et.MathNode("mo",n),t.mclass==="mbin"?(r.attributes.lspace="0.22em",r.attributes.rspace="0.22em"):t.mclass==="mpunct"?(r.attributes.lspace="0em",r.attributes.rspace="0.17em"):t.mclass==="mopen"||t.mclass==="mclose"?(r.attributes.lspace="0em",r.attributes.rspace="0em"):t.mclass==="minner"&&(r.attributes.lspace="0.0556em",r.attributes.width="+0.1111em")),r}function Yxe(t,e,r){var n=Uxe[t];switch(n){case"\\\\cdrightarrow":case"\\\\cdleftarrow":return r.callFunction(n,[e[0]],[e[1]]);case"\\uparrow":case"\\downarrow":{var i=r.callFunction("\\\\cdleft",[e[0]],[]),a={type:"atom",text:n,mode:"math",family:"rel"},s=r.callFunction("\\Big",[a],[]),l=r.callFunction("\\\\cdright",[e[1]],[]),u={type:"ordgroup",mode:"math",body:[i,s,l]};return r.callFunction("\\\\cdparent",[u],[])}case"\\\\cdlongequal":return r.callFunction("\\\\cdlongequal",[],[]);case"\\Vert":{var h={type:"textord",text:"\\Vert",mode:"math"};return r.callFunction("\\Big",[h],[])}default:return{type:"textord",text:" ",mode:"math"}}}function Wxe(t){var e=[];for(t.gullet.beginGroup(),t.gullet.macros.set("\\cr","\\\\\\relax"),t.gullet.beginGroup();;){e.push(t.parseExpression(!1,"\\\\")),t.gullet.endGroup(),t.gullet.beginGroup();var r=t.fetch().text;if(r==="&"||r==="\\\\")t.consume();else if(r==="\\end"){e[e.length-1].length===0&&e.pop();break}else throw new nt("Expected \\\\ or \\cr or \\end",t.nextToken)}for(var n=[],i=[n],a=0;a-1))if("<>AV".indexOf(h)>-1)for(var d=0;d<2;d++){for(var p=!0,m=u+1;mAV=|." after @',s[u]);var g=Yxe(h,f,t),y={type:"styling",body:[g],mode:"math",style:"display"};n.push(y),l=oG()}a%2===0?n.push(l):n.shift(),n=[],i.push(n)}t.gullet.endGroup(),t.gullet.endGroup();var v=new Array(i[0].length).fill({type:"align",align:"c",pregap:.25,postgap:.25});return{type:"array",mode:"math",body:i,arraystretch:1,addJot:!0,rowGaps:[null],cols:v,colSeparationType:"CD",hLinesBeforeRow:new Array(i.length+1).fill([])}}function E4(t,e){var r=T4(t);if(r&&Vt.contains(abe,r.text))return r;throw r?new nt("Invalid delimiter '"+r.text+"' after '"+e.funcName+"'",t):new nt("Invalid delimiter type '"+t.type+"'",t)}function uG(t){if(!t.body)throw new Error("Bug: The leftright ParseNode wasn't fully parsed.")}function ec(t){for(var{type:e,names:r,props:n,handler:i,htmlBuilder:a,mathmlBuilder:s}=t,l={type:e,numArgs:n.numArgs||0,allowedInText:!1,numOptionalArgs:0,handler:i},u=0;u1||!f)&&y.pop(),x.length{"use strict";Xs=class t{static{o(this,"SourceLocation")}constructor(e,r,n){this.lexer=void 0,this.start=void 0,this.end=void 0,this.lexer=e,this.start=r,this.end=n}static range(e,r){return r?!e||!e.loc||!r.loc||e.loc.lexer!==r.loc.lexer?null:new t(e.loc.lexer,e.loc.start,r.loc.end):e&&e.loc}},Ao=class t{static{o(this,"Token")}constructor(e,r){this.text=void 0,this.loc=void 0,this.noexpand=void 0,this.treatAsRelax=void 0,this.text=e,this.loc=r}range(e,r){return new t(r,Xs.range(this,e))}},nt=class t{static{o(this,"ParseError")}constructor(e,r){this.name=void 0,this.position=void 0,this.length=void 0,this.rawMessage=void 0;var n="KaTeX parse error: "+e,i,a,s=r&&r.loc;if(s&&s.start<=s.end){var l=s.lexer.input;i=s.start,a=s.end,i===l.length?n+=" at end of input: ":n+=" at position "+(i+1)+": ";var u=l.slice(i,a).replace(/[^]/g,"$&\u0332"),h;i>15?h="\u2026"+l.slice(i-15,i):h=l.slice(0,i);var f;a+15":">","<":"<",'"':""","'":"'"},P2e=/[&><"']/g;o(B2e,"escape");_G=o(function t(e){return e.type==="ordgroup"||e.type==="color"?e.body.length===1?t(e.body[0]):e:e.type==="font"?t(e.body):e},"getBaseElem"),F2e=o(function(e){var r=_G(e);return r.type==="mathord"||r.type==="textord"||r.type==="atom"},"isCharacterBox"),z2e=o(function(e){if(!e)throw new Error("Expected non-null, but got "+String(e));return e},"assert"),G2e=o(function(e){var r=/^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(e);return r?r[2]!==":"||!/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(r[1])?null:r[1].toLowerCase():"_relative"},"protocolFromUrl"),Vt={contains:R2e,deflt:N2e,escape:B2e,hyphenate:I2e,getBaseElem:_G,isCharacterBox:F2e,protocolFromUrl:G2e},u4={displayMode:{type:"boolean",description:"Render math in display mode, which puts the math in display style (so \\int and \\sum are large, for example), and centers the math on the page on its own line.",cli:"-d, --display-mode"},output:{type:{enum:["htmlAndMathml","html","mathml"]},description:"Determines the markup language of the output.",cli:"-F, --format "},leqno:{type:"boolean",description:"Render display math in leqno style (left-justified tags)."},fleqn:{type:"boolean",description:"Render display math flush left."},throwOnError:{type:"boolean",default:!0,cli:"-t, --no-throw-on-error",cliDescription:"Render errors (in the color given by --error-color) instead of throwing a ParseError exception when encountering an error."},errorColor:{type:"string",default:"#cc0000",cli:"-c, --error-color ",cliDescription:"A color string given in the format 'rgb' or 'rrggbb' (no #). This option determines the color of errors rendered by the -t option.",cliProcessor:o(t=>"#"+t,"cliProcessor")},macros:{type:"object",cli:"-m, --macro ",cliDescription:"Define custom macro of the form '\\foo:expansion' (use multiple -m arguments for multiple macros).",cliDefault:[],cliProcessor:o((t,e)=>(e.push(t),e),"cliProcessor")},minRuleThickness:{type:"number",description:"Specifies a minimum thickness, in ems, for fraction lines, `\\sqrt` top lines, `{array}` vertical lines, `\\hline`, `\\hdashline`, `\\underline`, `\\overline`, and the borders of `\\fbox`, `\\boxed`, and `\\fcolorbox`.",processor:o(t=>Math.max(0,t),"processor"),cli:"--min-rule-thickness ",cliProcessor:parseFloat},colorIsTextColor:{type:"boolean",description:"Makes \\color behave like LaTeX's 2-argument \\textcolor, instead of LaTeX's one-argument \\color mode change.",cli:"-b, --color-is-text-color"},strict:{type:[{enum:["warn","ignore","error"]},"boolean","function"],description:"Turn on strict / LaTeX faithfulness mode, which throws an error if the input uses features that are not supported by LaTeX.",cli:"-S, --strict",cliDefault:!1},trust:{type:["boolean","function"],description:"Trust the input, enabling all HTML features such as \\url.",cli:"-T, --trust"},maxSize:{type:"number",default:1/0,description:"If non-zero, all user-specified sizes, e.g. in \\rule{500em}{500em}, will be capped to maxSize ems. Otherwise, elements and spaces can be arbitrarily large",processor:o(t=>Math.max(0,t),"processor"),cli:"-s, --max-size ",cliProcessor:parseInt},maxExpand:{type:"number",default:1e3,description:"Limit the number of macro expansions to the specified number, to prevent e.g. infinite macro loops. If set to Infinity, the macro expander will try to fully expand as in LaTeX.",processor:o(t=>Math.max(0,t),"processor"),cli:"-e, --max-expand ",cliProcessor:o(t=>t==="Infinity"?1/0:parseInt(t),"cliProcessor")},globalGroup:{type:"boolean",cli:!1}};o($2e,"getDefaultValue");ry=class{static{o(this,"Settings")}constructor(e){this.displayMode=void 0,this.output=void 0,this.leqno=void 0,this.fleqn=void 0,this.throwOnError=void 0,this.errorColor=void 0,this.macros=void 0,this.minRuleThickness=void 0,this.colorIsTextColor=void 0,this.strict=void 0,this.trust=void 0,this.maxSize=void 0,this.maxExpand=void 0,this.globalGroup=void 0,e=e||{};for(var r in u4)if(u4.hasOwnProperty(r)){var n=u4[r];this[r]=e[r]!==void 0?n.processor?n.processor(e[r]):e[r]:$2e(n)}}reportNonstrict(e,r,n){var i=this.strict;if(typeof i=="function"&&(i=i(e,r,n)),!(!i||i==="ignore")){if(i===!0||i==="error")throw new nt("LaTeX-incompatible input and strict mode is set to 'error': "+(r+" ["+e+"]"),n);i==="warn"?typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+(r+" ["+e+"]")):typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to "+("unrecognized '"+i+"': "+r+" ["+e+"]"))}}useStrictBehavior(e,r,n){var i=this.strict;if(typeof i=="function")try{i=i(e,r,n)}catch{i="error"}return!i||i==="ignore"?!1:i===!0||i==="error"?!0:i==="warn"?(typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+(r+" ["+e+"]")),!1):(typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to "+("unrecognized '"+i+"': "+r+" ["+e+"]")),!1)}isTrusted(e){if(e.url&&!e.protocol){var r=Vt.protocolFromUrl(e.url);if(r==null)return!1;e.protocol=r}var n=typeof this.trust=="function"?this.trust(e):this.trust;return!!n}},Kl=class{static{o(this,"Style")}constructor(e,r,n){this.id=void 0,this.size=void 0,this.cramped=void 0,this.id=e,this.size=r,this.cramped=n}sup(){return Ql[V2e[this.id]]}sub(){return Ql[U2e[this.id]]}fracNum(){return Ql[H2e[this.id]]}fracDen(){return Ql[Y2e[this.id]]}cramp(){return Ql[W2e[this.id]]}text(){return Ql[q2e[this.id]]}isTight(){return this.size>=2}},N7=0,f4=1,gp=2,iu=3,ny=4,So=5,yp=6,qa=7,Ql=[new Kl(N7,0,!1),new Kl(f4,0,!0),new Kl(gp,1,!1),new Kl(iu,1,!0),new Kl(ny,2,!1),new Kl(So,2,!0),new Kl(yp,3,!1),new Kl(qa,3,!0)],V2e=[ny,So,ny,So,yp,qa,yp,qa],U2e=[So,So,So,So,qa,qa,qa,qa],H2e=[gp,iu,ny,So,yp,qa,yp,qa],Y2e=[iu,iu,So,So,qa,qa,qa,qa],W2e=[f4,f4,iu,iu,So,So,qa,qa],q2e=[N7,f4,gp,iu,gp,iu,gp,iu],Ht={DISPLAY:Ql[N7],TEXT:Ql[gp],SCRIPT:Ql[ny],SCRIPTSCRIPT:Ql[yp]},b7=[{name:"latin",blocks:[[256,591],[768,879]]},{name:"cyrillic",blocks:[[1024,1279]]},{name:"armenian",blocks:[[1328,1423]]},{name:"brahmic",blocks:[[2304,4255]]},{name:"georgian",blocks:[[4256,4351]]},{name:"cjk",blocks:[[12288,12543],[19968,40879],[65280,65376]]},{name:"hangul",blocks:[[44032,55215]]}];o(X2e,"scriptFromCodepoint");h4=[];b7.forEach(t=>t.blocks.forEach(e=>h4.push(...e)));o(LG,"supportedCodepoint");mp=80,j2e=o(function(e,r){return"M95,"+(622+e+r)+` +c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14 +c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54 +c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10 +s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429 +c69,-144,104.5,-217.7,106.5,-221 +l`+e/2.075+" -"+e+` +c5.3,-9.3,12,-14,20,-14 +H400000v`+(40+e)+`H845.2724 +s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7 +c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z +M`+(834+e)+" "+r+"h400000v"+(40+e)+"h-400000z"},"sqrtMain"),K2e=o(function(e,r){return"M263,"+(601+e+r)+`c0.7,0,18,39.7,52,119 +c34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120 +c340,-704.7,510.7,-1060.3,512,-1067 +l`+e/2.084+" -"+e+` +c4.7,-7.3,11,-11,19,-11 +H40000v`+(40+e)+`H1012.3 +s-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232 +c-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1 +s-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26 +c-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z +M`+(1001+e)+" "+r+"h400000v"+(40+e)+"h-400000z"},"sqrtSize1"),Q2e=o(function(e,r){return"M983 "+(10+e+r)+` +l`+e/3.13+" -"+e+` +c4,-6.7,10,-10,18,-10 H400000v`+(40+e)+` +H1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7 +s-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744 +c-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30 +c26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722 +c56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5 +c53.7,-170.3,84.5,-266.8,92.5,-289.5z +M`+(1001+e)+" "+r+"h400000v"+(40+e)+"h-400000z"},"sqrtSize2"),Z2e=o(function(e,r){return"M424,"+(2398+e+r)+` +c-1.3,-0.7,-38.5,-172,-111.5,-514c-73,-342,-109.8,-513.3,-110.5,-514 +c0,-2,-10.7,14.3,-32,49c-4.7,7.3,-9.8,15.7,-15.5,25c-5.7,9.3,-9.8,16,-12.5,20 +s-5,7,-5,7c-4,-3.3,-8.3,-7.7,-13,-13s-13,-13,-13,-13s76,-122,76,-122s77,-121,77,-121 +s209,968,209,968c0,-2,84.7,-361.7,254,-1079c169.3,-717.3,254.7,-1077.7,256,-1081 +l`+e/4.223+" -"+e+`c4,-6.7,10,-10,18,-10 H400000 +v`+(40+e)+`H1014.6 +s-87.3,378.7,-272.6,1166c-185.3,787.3,-279.3,1182.3,-282,1185 +c-2,6,-10,9,-24,9 +c-8,0,-12,-0.7,-12,-2z M`+(1001+e)+" "+r+` +h400000v`+(40+e)+"h-400000z"},"sqrtSize3"),J2e=o(function(e,r){return"M473,"+(2713+e+r)+` +c339.3,-1799.3,509.3,-2700,510,-2702 l`+e/5.298+" -"+e+` +c3.3,-7.3,9.3,-11,18,-11 H400000v`+(40+e)+`H1017.7 +s-90.5,478,-276.2,1466c-185.7,988,-279.5,1483,-281.5,1485c-2,6,-10,9,-24,9 +c-8,0,-12,-0.7,-12,-2c0,-1.3,-5.3,-32,-16,-92c-50.7,-293.3,-119.7,-693.3,-207,-1200 +c0,-1.3,-5.3,8.7,-16,30c-10.7,21.3,-21.3,42.7,-32,64s-16,33,-16,33s-26,-26,-26,-26 +s76,-153,76,-153s77,-151,77,-151c0.7,0.7,35.7,202,105,604c67.3,400.7,102,602.7,104, +606zM`+(1001+e)+" "+r+"h400000v"+(40+e)+"H1017.7z"},"sqrtSize4"),exe=o(function(e){var r=e/2;return"M400000 "+e+" H0 L"+r+" 0 l65 45 L145 "+(e-80)+" H400000z"},"phasePath"),txe=o(function(e,r,n){var i=n-54-r-e;return"M702 "+(e+r)+"H400000"+(40+e)+` +H742v`+i+`l-4 4-4 4c-.667.7 -2 1.5-4 2.5s-4.167 1.833-6.5 2.5-5.5 1-9.5 1 +h-12l-28-84c-16.667-52-96.667 -294.333-240-727l-212 -643 -85 170 +c-4-3.333-8.333-7.667-13 -13l-13-13l77-155 77-156c66 199.333 139 419.667 +219 661 l218 661zM702 `+r+"H400000v"+(40+e)+"H742z"},"sqrtTall"),rxe=o(function(e,r,n){r=1e3*r;var i="";switch(e){case"sqrtMain":i=j2e(r,mp);break;case"sqrtSize1":i=K2e(r,mp);break;case"sqrtSize2":i=Q2e(r,mp);break;case"sqrtSize3":i=Z2e(r,mp);break;case"sqrtSize4":i=J2e(r,mp);break;case"sqrtTall":i=txe(r,mp,n)}return i},"sqrtPath"),nxe=o(function(e,r){switch(e){case"\u239C":return"M291 0 H417 V"+r+" H291z M291 0 H417 V"+r+" H291z";case"\u2223":return"M145 0 H188 V"+r+" H145z M145 0 H188 V"+r+" H145z";case"\u2225":return"M145 0 H188 V"+r+" H145z M145 0 H188 V"+r+" H145z"+("M367 0 H410 V"+r+" H367z M367 0 H410 V"+r+" H367z");case"\u239F":return"M457 0 H583 V"+r+" H457z M457 0 H583 V"+r+" H457z";case"\u23A2":return"M319 0 H403 V"+r+" H319z M319 0 H403 V"+r+" H319z";case"\u23A5":return"M263 0 H347 V"+r+" H263z M263 0 H347 V"+r+" H263z";case"\u23AA":return"M384 0 H504 V"+r+" H384z M384 0 H504 V"+r+" H384z";case"\u23D0":return"M312 0 H355 V"+r+" H312z M312 0 H355 V"+r+" H312z";case"\u2016":return"M257 0 H300 V"+r+" H257z M257 0 H300 V"+r+" H257z"+("M478 0 H521 V"+r+" H478z M478 0 H521 V"+r+" H478z");default:return""}},"innerPath"),Qz={doubleleftarrow:`M262 157 +l10-10c34-36 62.7-77 86-123 3.3-8 5-13.3 5-16 0-5.3-6.7-8-20-8-7.3 + 0-12.2.5-14.5 1.5-2.3 1-4.8 4.5-7.5 10.5-49.3 97.3-121.7 169.3-217 216-28 + 14-57.3 25-88 33-6.7 2-11 3.8-13 5.5-2 1.7-3 4.2-3 7.5s1 5.8 3 7.5 +c2 1.7 6.3 3.5 13 5.5 68 17.3 128.2 47.8 180.5 91.5 52.3 43.7 93.8 96.2 124.5 + 157.5 9.3 8 15.3 12.3 18 13h6c12-.7 18-4 18-10 0-2-1.7-7-5-15-23.3-46-52-87 +-86-123l-10-10h399738v-40H218c328 0 0 0 0 0l-10-8c-26.7-20-65.7-43-117-69 2.7 +-2 6-3.7 10-5 36.7-16 72.3-37.3 107-64l10-8h399782v-40z +m8 0v40h399730v-40zm0 194v40h399730v-40z`,doublerightarrow:`M399738 392l +-10 10c-34 36-62.7 77-86 123-3.3 8-5 13.3-5 16 0 5.3 6.7 8 20 8 7.3 0 12.2-.5 + 14.5-1.5 2.3-1 4.8-4.5 7.5-10.5 49.3-97.3 121.7-169.3 217-216 28-14 57.3-25 88 +-33 6.7-2 11-3.8 13-5.5 2-1.7 3-4.2 3-7.5s-1-5.8-3-7.5c-2-1.7-6.3-3.5-13-5.5-68 +-17.3-128.2-47.8-180.5-91.5-52.3-43.7-93.8-96.2-124.5-157.5-9.3-8-15.3-12.3-18 +-13h-6c-12 .7-18 4-18 10 0 2 1.7 7 5 15 23.3 46 52 87 86 123l10 10H0v40h399782 +c-328 0 0 0 0 0l10 8c26.7 20 65.7 43 117 69-2.7 2-6 3.7-10 5-36.7 16-72.3 37.3 +-107 64l-10 8H0v40zM0 157v40h399730v-40zm0 194v40h399730v-40z`,leftarrow:`M400000 241H110l3-3c68.7-52.7 113.7-120 + 135-202 4-14.7 6-23 6-25 0-7.3-7-11-21-11-8 0-13.2.8-15.5 2.5-2.3 1.7-4.2 5.8 +-5.5 12.5-1.3 4.7-2.7 10.3-4 17-12 48.7-34.8 92-68.5 130S65.3 228.3 18 247 +c-10 4-16 7.7-18 11 0 8.7 6 14.3 18 17 47.3 18.7 87.8 47 121.5 85S196 441.3 208 + 490c.7 2 1.3 5 2 9s1.2 6.7 1.5 8c.3 1.3 1 3.3 2 6s2.2 4.5 3.5 5.5c1.3 1 3.3 + 1.8 6 2.5s6 1 10 1c14 0 21-3.7 21-11 0-2-2-10.3-6-25-20-79.3-65-146.7-135-202 + l-3-3h399890zM100 241v40h399900v-40z`,leftbrace:`M6 548l-6-6v-35l6-11c56-104 135.3-181.3 238-232 57.3-28.7 117 +-45 179-50h399577v120H403c-43.3 7-81 15-113 26-100.7 33-179.7 91-237 174-2.7 + 5-6 9-10 13-.7 1-7.3 1-20 1H6z`,leftbraceunder:`M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13 + 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688 + 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7 +-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z`,leftgroup:`M400000 80 +H435C64 80 168.3 229.4 21 260c-5.9 1.2-18 0-18 0-2 0-3-1-3-3v-38C76 61 257 0 + 435 0h399565z`,leftgroupunder:`M400000 262 +H435C64 262 168.3 112.6 21 82c-5.9-1.2-18 0-18 0-2 0-3 1-3 3v38c76 158 257 219 + 435 219h399565z`,leftharpoon:`M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3 +-3.3 10.2-9.5 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5 +-18.3 3-21-1.3-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7 +-196 228-6.7 4.7-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40z`,leftharpoonplus:`M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3-3.3 10.2-9.5 + 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5-18.3 3-21-1.3 +-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7-196 228-6.7 4.7 +-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40zM0 435v40h400000v-40z +m0 0v40h400000v-40z`,leftharpoondown:`M7 241c-4 4-6.333 8.667-7 14 0 5.333.667 9 2 11s5.333 + 5.333 12 10c90.667 54 156 130 196 228 3.333 10.667 6.333 16.333 9 17 2 .667 5 + 1 9 1h5c10.667 0 16.667-2 18-6 2-2.667 1-9.667-3-21-32-87.333-82.667-157.667 +-152-211l-3-3h399907v-40zM93 281 H400000 v-40L7 241z`,leftharpoondownplus:`M7 435c-4 4-6.3 8.7-7 14 0 5.3.7 9 2 11s5.3 5.3 12 + 10c90.7 54 156 130 196 228 3.3 10.7 6.3 16.3 9 17 2 .7 5 1 9 1h5c10.7 0 16.7 +-2 18-6 2-2.7 1-9.7-3-21-32-87.3-82.7-157.7-152-211l-3-3h399907v-40H7zm93 0 +v40h399900v-40zM0 241v40h399900v-40zm0 0v40h399900v-40z`,lefthook:`M400000 281 H103s-33-11.2-61-33.5S0 197.3 0 164s14.2-61.2 42.5 +-83.5C70.8 58.2 104 47 142 47 c16.7 0 25 6.7 25 20 0 12-8.7 18.7-26 20-40 3.3 +-68.7 15.7-86 37-10 12-15 25.3-15 40 0 22.7 9.8 40.7 29.5 54 19.7 13.3 43.5 21 + 71.5 23h399859zM103 281v-40h399897v40z`,leftlinesegment:`M40 281 V428 H0 V94 H40 V241 H400000 v40z +M40 281 V428 H0 V94 H40 V241 H400000 v40z`,leftmapsto:`M40 281 V448H0V74H40V241H400000v40z +M40 281 V448H0V74H40V241H400000v40z`,leftToFrom:`M0 147h400000v40H0zm0 214c68 40 115.7 95.7 143 167h22c15.3 0 23 +-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69-70-101l-7-8h399905v-40H95l7-8 +c28.7-32 52-65.7 70-101 10.7-23.3 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 265.3 + 68 321 0 361zm0-174v-40h399900v40zm100 154v40h399900v-40z`,longequal:`M0 50 h400000 v40H0z m0 194h40000v40H0z +M0 50 h400000 v40H0z m0 194h40000v40H0z`,midbrace:`M200428 334 +c-100.7-8.3-195.3-44-280-108-55.3-42-101.7-93-139-153l-9-14c-2.7 4-5.7 8.7-9 14 +-53.3 86.7-123.7 153-211 199-66.7 36-137.3 56.3-212 62H0V214h199568c178.3-11.7 + 311.7-78.3 403-201 6-8 9.7-12 11-12 .7-.7 6.7-1 18-1s17.3.3 18 1c1.3 0 5 4 11 + 12 44.7 59.3 101.3 106.3 170 141s145.3 54.3 229 60h199572v120z`,midbraceunder:`M199572 214 +c100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14 + 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3 + 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0 +-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z`,oiintSize1:`M512.6 71.6c272.6 0 320.3 106.8 320.3 178.2 0 70.8-47.7 177.6 +-320.3 177.6S193.1 320.6 193.1 249.8c0-71.4 46.9-178.2 319.5-178.2z +m368.1 178.2c0-86.4-60.9-215.4-368.1-215.4-306.4 0-367.3 129-367.3 215.4 0 85.8 +60.9 214.8 367.3 214.8 307.2 0 368.1-129 368.1-214.8z`,oiintSize2:`M757.8 100.1c384.7 0 451.1 137.6 451.1 230 0 91.3-66.4 228.8 +-451.1 228.8-386.3 0-452.7-137.5-452.7-228.8 0-92.4 66.4-230 452.7-230z +m502.4 230c0-111.2-82.4-277.2-502.4-277.2s-504 166-504 277.2 +c0 110 84 276 504 276s502.4-166 502.4-276z`,oiiintSize1:`M681.4 71.6c408.9 0 480.5 106.8 480.5 178.2 0 70.8-71.6 177.6 +-480.5 177.6S202.1 320.6 202.1 249.8c0-71.4 70.5-178.2 479.3-178.2z +m525.8 178.2c0-86.4-86.8-215.4-525.7-215.4-437.9 0-524.7 129-524.7 215.4 0 +85.8 86.8 214.8 524.7 214.8 438.9 0 525.7-129 525.7-214.8z`,oiiintSize2:`M1021.2 53c603.6 0 707.8 165.8 707.8 277.2 0 110-104.2 275.8 +-707.8 275.8-606 0-710.2-165.8-710.2-275.8C311 218.8 415.2 53 1021.2 53z +m770.4 277.1c0-131.2-126.4-327.6-770.5-327.6S248.4 198.9 248.4 330.1 +c0 130 128.8 326.4 772.7 326.4s770.5-196.4 770.5-326.4z`,rightarrow:`M0 241v40h399891c-47.3 35.3-84 78-110 128 +-16.7 32-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 + 11 8 0 13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 + 39-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85 +-40.5-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5 +-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67 + 151.7 139 205zm0 0v40h399900v-40z`,rightbrace:`M400000 542l +-6 6h-17c-12.7 0-19.3-.3-20-1-4-4-7.3-8.3-10-13-35.3-51.3-80.8-93.8-136.5-127.5 +s-117.2-55.8-184.5-66.5c-.7 0-2-.3-4-1-18.7-2.7-76-4.3-172-5H0V214h399571l6 1 +c124.7 8 235 61.7 331 161 31.3 33.3 59.7 72.7 85 118l7 13v35z`,rightbraceunder:`M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3 + 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237 +-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z`,rightgroup:`M0 80h399565c371 0 266.7 149.4 414 180 5.9 1.2 18 0 18 0 2 0 + 3-1 3-3v-38c-76-158-257-219-435-219H0z`,rightgroupunder:`M0 262h399565c371 0 266.7-149.4 414-180 5.9-1.2 18 0 18 + 0 2 0 3 1 3 3v38c-76 158-257 219-435 219H0z`,rightharpoon:`M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3 +-3.7-15.3-11-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2 +-10.7 0-16.7 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58 + 69.2 92 94.5zm0 0v40h399900v-40z`,rightharpoonplus:`M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3-3.7-15.3-11 +-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2-10.7 0-16.7 + 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58 69.2 92 94.5z +m0 0v40h399900v-40z m100 194v40h399900v-40zm0 0v40h399900v-40z`,rightharpoondown:`M399747 511c0 7.3 6.7 11 20 11 8 0 13-.8 15-2.5s4.7-6.8 + 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3 8.5-5.8 9.5 +-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3-64.7 57-92 95 +-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 241v40h399900v-40z`,rightharpoondownplus:`M399747 705c0 7.3 6.7 11 20 11 8 0 13-.8 + 15-2.5s4.7-6.8 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3 + 8.5-5.8 9.5-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3 +-64.7 57-92 95-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 435v40h399900v-40z +m0-194v40h400000v-40zm0 0v40h400000v-40z`,righthook:`M399859 241c-764 0 0 0 0 0 40-3.3 68.7-15.7 86-37 10-12 15-25.3 + 15-40 0-22.7-9.8-40.7-29.5-54-19.7-13.3-43.5-21-71.5-23-17.3-1.3-26-8-26-20 0 +-13.3 8.7-20 26-20 38 0 71 11.2 99 33.5 0 0 7 5.6 21 16.7 14 11.2 21 33.5 21 + 66.8s-14 61.2-42 83.5c-28 22.3-61 33.5-99 33.5L0 241z M0 281v-40h399859v40z`,rightlinesegment:`M399960 241 V94 h40 V428 h-40 V281 H0 v-40z +M399960 241 V94 h40 V428 h-40 V281 H0 v-40z`,rightToFrom:`M400000 167c-70.7-42-118-97.7-142-167h-23c-15.3 0-23 .3-23 + 1 0 1.3 5.3 13.7 16 37 18 35.3 41.3 69 70 101l7 8H0v40h399905l-7 8c-28.7 32 +-52 65.7-70 101-10.7 23.3-16 35.7-16 37 0 .7 7.7 1 23 1h23c24-69.3 71.3-125 142 +-167z M100 147v40h399900v-40zM0 341v40h399900v-40z`,twoheadleftarrow:`M0 167c68 40 + 115.7 95.7 143 167h22c15.3 0 23-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69 +-70-101l-7-8h125l9 7c50.7 39.3 85 86 103 140h46c0-4.7-6.3-18.7-19-42-18-35.3 +-40-67.3-66-96l-9-9h399716v-40H284l9-9c26-28.7 48-60.7 66-96 12.7-23.333 19 +-37.333 19-42h-46c-18 54-52.3 100.7-103 140l-9 7H95l7-8c28.7-32 52-65.7 70-101 + 10.7-23.333 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 71.3 68 127 0 167z`,twoheadrightarrow:`M400000 167 +c-68-40-115.7-95.7-143-167h-22c-15.3 0-23 .3-23 1 0 1.3 5.3 13.7 16 37 18 35.3 + 41.3 69 70 101l7 8h-125l-9-7c-50.7-39.3-85-86-103-140h-46c0 4.7 6.3 18.7 19 42 + 18 35.3 40 67.3 66 96l9 9H0v40h399716l-9 9c-26 28.7-48 60.7-66 96-12.7 23.333 +-19 37.333-19 42h46c18-54 52.3-100.7 103-140l9-7h125l-7 8c-28.7 32-52 65.7-70 + 101-10.7 23.333-16 35.7-16 37 0 .7 7.7 1 23 1h22c27.3-71.3 75-127 143-167z`,tilde1:`M200 55.538c-77 0-168 73.953-177 73.953-3 0-7 +-2.175-9-5.437L2 97c-1-2-2-4-2-6 0-4 2-7 5-9l20-12C116 12 171 0 207 0c86 0 + 114 68 191 68 78 0 168-68 177-68 4 0 7 2 9 5l12 19c1 2.175 2 4.35 2 6.525 0 + 4.35-2 7.613-5 9.788l-19 13.05c-92 63.077-116.937 75.308-183 76.128 +-68.267.847-113-73.952-191-73.952z`,tilde2:`M344 55.266c-142 0-300.638 81.316-311.5 86.418 +-8.01 3.762-22.5 10.91-23.5 5.562L1 120c-1-2-1-3-1-4 0-5 3-9 8-10l18.4-9C160.9 + 31.9 283 0 358 0c148 0 188 122 331 122s314-97 326-97c4 0 8 2 10 7l7 21.114 +c1 2.14 1 3.21 1 4.28 0 5.347-3 9.626-7 10.696l-22.3 12.622C852.6 158.372 751 + 181.476 676 181.476c-149 0-189-126.21-332-126.21z`,tilde3:`M786 59C457 59 32 175.242 13 175.242c-6 0-10-3.457 +-11-10.37L.15 138c-1-7 3-12 10-13l19.2-6.4C378.4 40.7 634.3 0 804.3 0c337 0 + 411.8 157 746.8 157 328 0 754-112 773-112 5 0 10 3 11 9l1 14.075c1 8.066-.697 + 16.595-6.697 17.492l-21.052 7.31c-367.9 98.146-609.15 122.696-778.15 122.696 + -338 0-409-156.573-744-156.573z`,tilde4:`M786 58C457 58 32 177.487 13 177.487c-6 0-10-3.345 +-11-10.035L.15 143c-1-7 3-12 10-13l22-6.7C381.2 35 637.15 0 807.15 0c337 0 409 + 177 744 177 328 0 754-127 773-127 5 0 10 3 11 9l1 14.794c1 7.805-3 13.38-9 + 14.495l-20.7 5.574c-366.85 99.79-607.3 139.372-776.3 139.372-338 0-409 + -175.236-744-175.236z`,vec:`M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5 +3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11 +10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63 +-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1 +-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59 +H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359 +c-16-25.333-24-45-24-59z`,widehat1:`M529 0h5l519 115c5 1 9 5 9 10 0 1-1 2-1 3l-4 22 +c-1 5-5 9-11 9h-2L532 67 19 159h-2c-5 0-9-4-11-9l-5-22c-1-6 2-12 8-13z`,widehat2:`M1181 0h2l1171 176c6 0 10 5 10 11l-2 23c-1 6-5 10 +-11 10h-1L1182 67 15 220h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z`,widehat3:`M1181 0h2l1171 236c6 0 10 5 10 11l-2 23c-1 6-5 10 +-11 10h-1L1182 67 15 280h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z`,widehat4:`M1181 0h2l1171 296c6 0 10 5 10 11l-2 23c-1 6-5 10 +-11 10h-1L1182 67 15 340h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z`,widecheck1:`M529,159h5l519,-115c5,-1,9,-5,9,-10c0,-1,-1,-2,-1,-3l-4,-22c-1, +-5,-5,-9,-11,-9h-2l-512,92l-513,-92h-2c-5,0,-9,4,-11,9l-5,22c-1,6,2,12,8,13z`,widecheck2:`M1181,220h2l1171,-176c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10, +-11,-10h-1l-1168,153l-1167,-153h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z`,widecheck3:`M1181,280h2l1171,-236c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10, +-11,-10h-1l-1168,213l-1167,-213h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z`,widecheck4:`M1181,340h2l1171,-296c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10, +-11,-10h-1l-1168,273l-1167,-273h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z`,baraboveleftarrow:`M400000 620h-399890l3 -3c68.7 -52.7 113.7 -120 135 -202 +c4 -14.7 6 -23 6 -25c0 -7.3 -7 -11 -21 -11c-8 0 -13.2 0.8 -15.5 2.5 +c-2.3 1.7 -4.2 5.8 -5.5 12.5c-1.3 4.7 -2.7 10.3 -4 17c-12 48.7 -34.8 92 -68.5 130 +s-74.2 66.3 -121.5 85c-10 4 -16 7.7 -18 11c0 8.7 6 14.3 18 17c47.3 18.7 87.8 47 +121.5 85s56.5 81.3 68.5 130c0.7 2 1.3 5 2 9s1.2 6.7 1.5 8c0.3 1.3 1 3.3 2 6 +s2.2 4.5 3.5 5.5c1.3 1 3.3 1.8 6 2.5s6 1 10 1c14 0 21 -3.7 21 -11 +c0 -2 -2 -10.3 -6 -25c-20 -79.3 -65 -146.7 -135 -202l-3 -3h399890z +M100 620v40h399900v-40z M0 241v40h399900v-40zM0 241v40h399900v-40z`,rightarrowabovebar:`M0 241v40h399891c-47.3 35.3-84 78-110 128-16.7 32 +-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 11 8 0 +13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 39 +-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85-40.5 +-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5 +-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67 +151.7 139 205zm96 379h399894v40H0zm0 0h399904v40H0z`,baraboveshortleftharpoon:`M507,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11 +c1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17 +c2,0.7,5,1,9,1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21 +c-32,-87.3,-82.7,-157.7,-152,-211c0,0,-3,-3,-3,-3l399351,0l0,-40 +c-398570,0,-399437,0,-399437,0z M593 435 v40 H399500 v-40z +M0 281 v-40 H399908 v40z M0 281 v-40 H399908 v40z`,rightharpoonaboveshortbar:`M0,241 l0,40c399126,0,399993,0,399993,0 +c4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199, +-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6 +c-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z +M0 241 v40 H399908 v-40z M0 475 v-40 H399500 v40z M0 475 v-40 H399500 v40z`,shortbaraboveleftharpoon:`M7,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11 +c1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17c2,0.7,5,1,9, +1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21c-32,-87.3,-82.7,-157.7, +-152,-211c0,0,-3,-3,-3,-3l399907,0l0,-40c-399126,0,-399993,0,-399993,0z +M93 435 v40 H400000 v-40z M500 241 v40 H400000 v-40z M500 241 v40 H400000 v-40z`,shortrightharpoonabovebar:`M53,241l0,40c398570,0,399437,0,399437,0 +c4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199, +-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6 +c-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z +M500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z`},ixe=o(function(e,r){switch(e){case"lbrack":return"M403 1759 V84 H666 V0 H319 V1759 v"+r+` v1759 h347 v-84 +H403z M403 1759 V0 H319 V1759 v`+r+" v1759 h84z";case"rbrack":return"M347 1759 V0 H0 V84 H263 V1759 v"+r+` v1759 H0 v84 H347z +M347 1759 V0 H263 V1759 v`+r+" v1759 h84z";case"vert":return"M145 15 v585 v"+r+` v585 c2.667,10,9.667,15,21,15 +c10,0,16.667,-5,20,-15 v-585 v`+-r+` v-585 c-2.667,-10,-9.667,-15,-21,-15 +c-10,0,-16.667,5,-20,15z M188 15 H145 v585 v`+r+" v585 h43z";case"doublevert":return"M145 15 v585 v"+r+` v585 c2.667,10,9.667,15,21,15 +c10,0,16.667,-5,20,-15 v-585 v`+-r+` v-585 c-2.667,-10,-9.667,-15,-21,-15 +c-10,0,-16.667,5,-20,15z M188 15 H145 v585 v`+r+` v585 h43z +M367 15 v585 v`+r+` v585 c2.667,10,9.667,15,21,15 +c10,0,16.667,-5,20,-15 v-585 v`+-r+` v-585 c-2.667,-10,-9.667,-15,-21,-15 +c-10,0,-16.667,5,-20,15z M410 15 H367 v585 v`+r+" v585 h43z";case"lfloor":return"M319 602 V0 H403 V602 v"+r+` v1715 h263 v84 H319z +MM319 602 V0 H403 V602 v`+r+" v1715 H319z";case"rfloor":return"M319 602 V0 H403 V602 v"+r+` v1799 H0 v-84 H319z +MM319 602 V0 H403 V602 v`+r+" v1715 H319z";case"lceil":return"M403 1759 V84 H666 V0 H319 V1759 v"+r+` v602 h84z +M403 1759 V0 H319 V1759 v`+r+" v602 h84z";case"rceil":return"M347 1759 V0 H0 V84 H263 V1759 v"+r+` v602 h84z +M347 1759 V0 h-84 V1759 v`+r+" v602 h84z";case"lparen":return`M863,9c0,-2,-2,-5,-6,-9c0,0,-17,0,-17,0c-12.7,0,-19.3,0.3,-20,1 +c-5.3,5.3,-10.3,11,-15,17c-242.7,294.7,-395.3,682,-458,1162c-21.3,163.3,-33.3,349, +-36,557 l0,`+(r+84)+`c0.2,6,0,26,0,60c2,159.3,10,310.7,24,454c53.3,528,210, +949.7,470,1265c4.7,6,9.7,11.7,15,17c0.7,0.7,7,1,19,1c0,0,18,0,18,0c4,-4,6,-7,6,-9 +c0,-2.7,-3.3,-8.7,-10,-18c-135.3,-192.7,-235.5,-414.3,-300.5,-665c-65,-250.7,-102.5, +-544.7,-112.5,-882c-2,-104,-3,-167,-3,-189 +l0,-`+(r+92)+`c0,-162.7,5.7,-314,17,-454c20.7,-272,63.7,-513,129,-723c65.3, +-210,155.3,-396.3,270,-559c6.7,-9.3,10,-15.3,10,-18z`;case"rparen":return`M76,0c-16.7,0,-25,3,-25,9c0,2,2,6.3,6,13c21.3,28.7,42.3,60.3, +63,95c96.7,156.7,172.8,332.5,228.5,527.5c55.7,195,92.8,416.5,111.5,664.5 +c11.3,139.3,17,290.7,17,454c0,28,1.7,43,3.3,45l0,`+(r+9)+` +c-3,4,-3.3,16.7,-3.3,38c0,162,-5.7,313.7,-17,455c-18.7,248,-55.8,469.3,-111.5,664 +c-55.7,194.7,-131.8,370.3,-228.5,527c-20.7,34.7,-41.7,66.3,-63,95c-2,3.3,-4,7,-6,11 +c0,7.3,5.7,11,17,11c0,0,11,0,11,0c9.3,0,14.3,-0.3,15,-1c5.3,-5.3,10.3,-11,15,-17 +c242.7,-294.7,395.3,-681.7,458,-1161c21.3,-164.7,33.3,-350.7,36,-558 +l0,-`+(r+144)+`c-2,-159.3,-10,-310.7,-24,-454c-53.3,-528,-210,-949.7, +-470,-1265c-4.7,-6,-9.7,-11.7,-15,-17c-0.7,-0.7,-6.7,-1,-18,-1z`;default:throw new Error("Unknown stretchy delimiter.")}},"tallDelim"),Xf=class{static{o(this,"DocumentFragment")}constructor(e){this.children=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.children=e,this.classes=[],this.height=0,this.depth=0,this.maxFontSize=0,this.style={}}hasClass(e){return Vt.contains(this.classes,e)}toNode(){for(var e=document.createDocumentFragment(),r=0;rr.toText(),"toText");return this.children.map(e).join("")}},Zl={"AMS-Regular":{32:[0,0,0,0,.25],65:[0,.68889,0,0,.72222],66:[0,.68889,0,0,.66667],67:[0,.68889,0,0,.72222],68:[0,.68889,0,0,.72222],69:[0,.68889,0,0,.66667],70:[0,.68889,0,0,.61111],71:[0,.68889,0,0,.77778],72:[0,.68889,0,0,.77778],73:[0,.68889,0,0,.38889],74:[.16667,.68889,0,0,.5],75:[0,.68889,0,0,.77778],76:[0,.68889,0,0,.66667],77:[0,.68889,0,0,.94445],78:[0,.68889,0,0,.72222],79:[.16667,.68889,0,0,.77778],80:[0,.68889,0,0,.61111],81:[.16667,.68889,0,0,.77778],82:[0,.68889,0,0,.72222],83:[0,.68889,0,0,.55556],84:[0,.68889,0,0,.66667],85:[0,.68889,0,0,.72222],86:[0,.68889,0,0,.72222],87:[0,.68889,0,0,1],88:[0,.68889,0,0,.72222],89:[0,.68889,0,0,.72222],90:[0,.68889,0,0,.66667],107:[0,.68889,0,0,.55556],160:[0,0,0,0,.25],165:[0,.675,.025,0,.75],174:[.15559,.69224,0,0,.94666],240:[0,.68889,0,0,.55556],295:[0,.68889,0,0,.54028],710:[0,.825,0,0,2.33334],732:[0,.9,0,0,2.33334],770:[0,.825,0,0,2.33334],771:[0,.9,0,0,2.33334],989:[.08167,.58167,0,0,.77778],1008:[0,.43056,.04028,0,.66667],8245:[0,.54986,0,0,.275],8463:[0,.68889,0,0,.54028],8487:[0,.68889,0,0,.72222],8498:[0,.68889,0,0,.55556],8502:[0,.68889,0,0,.66667],8503:[0,.68889,0,0,.44445],8504:[0,.68889,0,0,.66667],8513:[0,.68889,0,0,.63889],8592:[-.03598,.46402,0,0,.5],8594:[-.03598,.46402,0,0,.5],8602:[-.13313,.36687,0,0,1],8603:[-.13313,.36687,0,0,1],8606:[.01354,.52239,0,0,1],8608:[.01354,.52239,0,0,1],8610:[.01354,.52239,0,0,1.11111],8611:[.01354,.52239,0,0,1.11111],8619:[0,.54986,0,0,1],8620:[0,.54986,0,0,1],8621:[-.13313,.37788,0,0,1.38889],8622:[-.13313,.36687,0,0,1],8624:[0,.69224,0,0,.5],8625:[0,.69224,0,0,.5],8630:[0,.43056,0,0,1],8631:[0,.43056,0,0,1],8634:[.08198,.58198,0,0,.77778],8635:[.08198,.58198,0,0,.77778],8638:[.19444,.69224,0,0,.41667],8639:[.19444,.69224,0,0,.41667],8642:[.19444,.69224,0,0,.41667],8643:[.19444,.69224,0,0,.41667],8644:[.1808,.675,0,0,1],8646:[.1808,.675,0,0,1],8647:[.1808,.675,0,0,1],8648:[.19444,.69224,0,0,.83334],8649:[.1808,.675,0,0,1],8650:[.19444,.69224,0,0,.83334],8651:[.01354,.52239,0,0,1],8652:[.01354,.52239,0,0,1],8653:[-.13313,.36687,0,0,1],8654:[-.13313,.36687,0,0,1],8655:[-.13313,.36687,0,0,1],8666:[.13667,.63667,0,0,1],8667:[.13667,.63667,0,0,1],8669:[-.13313,.37788,0,0,1],8672:[-.064,.437,0,0,1.334],8674:[-.064,.437,0,0,1.334],8705:[0,.825,0,0,.5],8708:[0,.68889,0,0,.55556],8709:[.08167,.58167,0,0,.77778],8717:[0,.43056,0,0,.42917],8722:[-.03598,.46402,0,0,.5],8724:[.08198,.69224,0,0,.77778],8726:[.08167,.58167,0,0,.77778],8733:[0,.69224,0,0,.77778],8736:[0,.69224,0,0,.72222],8737:[0,.69224,0,0,.72222],8738:[.03517,.52239,0,0,.72222],8739:[.08167,.58167,0,0,.22222],8740:[.25142,.74111,0,0,.27778],8741:[.08167,.58167,0,0,.38889],8742:[.25142,.74111,0,0,.5],8756:[0,.69224,0,0,.66667],8757:[0,.69224,0,0,.66667],8764:[-.13313,.36687,0,0,.77778],8765:[-.13313,.37788,0,0,.77778],8769:[-.13313,.36687,0,0,.77778],8770:[-.03625,.46375,0,0,.77778],8774:[.30274,.79383,0,0,.77778],8776:[-.01688,.48312,0,0,.77778],8778:[.08167,.58167,0,0,.77778],8782:[.06062,.54986,0,0,.77778],8783:[.06062,.54986,0,0,.77778],8785:[.08198,.58198,0,0,.77778],8786:[.08198,.58198,0,0,.77778],8787:[.08198,.58198,0,0,.77778],8790:[0,.69224,0,0,.77778],8791:[.22958,.72958,0,0,.77778],8796:[.08198,.91667,0,0,.77778],8806:[.25583,.75583,0,0,.77778],8807:[.25583,.75583,0,0,.77778],8808:[.25142,.75726,0,0,.77778],8809:[.25142,.75726,0,0,.77778],8812:[.25583,.75583,0,0,.5],8814:[.20576,.70576,0,0,.77778],8815:[.20576,.70576,0,0,.77778],8816:[.30274,.79383,0,0,.77778],8817:[.30274,.79383,0,0,.77778],8818:[.22958,.72958,0,0,.77778],8819:[.22958,.72958,0,0,.77778],8822:[.1808,.675,0,0,.77778],8823:[.1808,.675,0,0,.77778],8828:[.13667,.63667,0,0,.77778],8829:[.13667,.63667,0,0,.77778],8830:[.22958,.72958,0,0,.77778],8831:[.22958,.72958,0,0,.77778],8832:[.20576,.70576,0,0,.77778],8833:[.20576,.70576,0,0,.77778],8840:[.30274,.79383,0,0,.77778],8841:[.30274,.79383,0,0,.77778],8842:[.13597,.63597,0,0,.77778],8843:[.13597,.63597,0,0,.77778],8847:[.03517,.54986,0,0,.77778],8848:[.03517,.54986,0,0,.77778],8858:[.08198,.58198,0,0,.77778],8859:[.08198,.58198,0,0,.77778],8861:[.08198,.58198,0,0,.77778],8862:[0,.675,0,0,.77778],8863:[0,.675,0,0,.77778],8864:[0,.675,0,0,.77778],8865:[0,.675,0,0,.77778],8872:[0,.69224,0,0,.61111],8873:[0,.69224,0,0,.72222],8874:[0,.69224,0,0,.88889],8876:[0,.68889,0,0,.61111],8877:[0,.68889,0,0,.61111],8878:[0,.68889,0,0,.72222],8879:[0,.68889,0,0,.72222],8882:[.03517,.54986,0,0,.77778],8883:[.03517,.54986,0,0,.77778],8884:[.13667,.63667,0,0,.77778],8885:[.13667,.63667,0,0,.77778],8888:[0,.54986,0,0,1.11111],8890:[.19444,.43056,0,0,.55556],8891:[.19444,.69224,0,0,.61111],8892:[.19444,.69224,0,0,.61111],8901:[0,.54986,0,0,.27778],8903:[.08167,.58167,0,0,.77778],8905:[.08167,.58167,0,0,.77778],8906:[.08167,.58167,0,0,.77778],8907:[0,.69224,0,0,.77778],8908:[0,.69224,0,0,.77778],8909:[-.03598,.46402,0,0,.77778],8910:[0,.54986,0,0,.76042],8911:[0,.54986,0,0,.76042],8912:[.03517,.54986,0,0,.77778],8913:[.03517,.54986,0,0,.77778],8914:[0,.54986,0,0,.66667],8915:[0,.54986,0,0,.66667],8916:[0,.69224,0,0,.66667],8918:[.0391,.5391,0,0,.77778],8919:[.0391,.5391,0,0,.77778],8920:[.03517,.54986,0,0,1.33334],8921:[.03517,.54986,0,0,1.33334],8922:[.38569,.88569,0,0,.77778],8923:[.38569,.88569,0,0,.77778],8926:[.13667,.63667,0,0,.77778],8927:[.13667,.63667,0,0,.77778],8928:[.30274,.79383,0,0,.77778],8929:[.30274,.79383,0,0,.77778],8934:[.23222,.74111,0,0,.77778],8935:[.23222,.74111,0,0,.77778],8936:[.23222,.74111,0,0,.77778],8937:[.23222,.74111,0,0,.77778],8938:[.20576,.70576,0,0,.77778],8939:[.20576,.70576,0,0,.77778],8940:[.30274,.79383,0,0,.77778],8941:[.30274,.79383,0,0,.77778],8994:[.19444,.69224,0,0,.77778],8995:[.19444,.69224,0,0,.77778],9416:[.15559,.69224,0,0,.90222],9484:[0,.69224,0,0,.5],9488:[0,.69224,0,0,.5],9492:[0,.37788,0,0,.5],9496:[0,.37788,0,0,.5],9585:[.19444,.68889,0,0,.88889],9586:[.19444,.74111,0,0,.88889],9632:[0,.675,0,0,.77778],9633:[0,.675,0,0,.77778],9650:[0,.54986,0,0,.72222],9651:[0,.54986,0,0,.72222],9654:[.03517,.54986,0,0,.77778],9660:[0,.54986,0,0,.72222],9661:[0,.54986,0,0,.72222],9664:[.03517,.54986,0,0,.77778],9674:[.11111,.69224,0,0,.66667],9733:[.19444,.69224,0,0,.94445],10003:[0,.69224,0,0,.83334],10016:[0,.69224,0,0,.83334],10731:[.11111,.69224,0,0,.66667],10846:[.19444,.75583,0,0,.61111],10877:[.13667,.63667,0,0,.77778],10878:[.13667,.63667,0,0,.77778],10885:[.25583,.75583,0,0,.77778],10886:[.25583,.75583,0,0,.77778],10887:[.13597,.63597,0,0,.77778],10888:[.13597,.63597,0,0,.77778],10889:[.26167,.75726,0,0,.77778],10890:[.26167,.75726,0,0,.77778],10891:[.48256,.98256,0,0,.77778],10892:[.48256,.98256,0,0,.77778],10901:[.13667,.63667,0,0,.77778],10902:[.13667,.63667,0,0,.77778],10933:[.25142,.75726,0,0,.77778],10934:[.25142,.75726,0,0,.77778],10935:[.26167,.75726,0,0,.77778],10936:[.26167,.75726,0,0,.77778],10937:[.26167,.75726,0,0,.77778],10938:[.26167,.75726,0,0,.77778],10949:[.25583,.75583,0,0,.77778],10950:[.25583,.75583,0,0,.77778],10955:[.28481,.79383,0,0,.77778],10956:[.28481,.79383,0,0,.77778],57350:[.08167,.58167,0,0,.22222],57351:[.08167,.58167,0,0,.38889],57352:[.08167,.58167,0,0,.77778],57353:[0,.43056,.04028,0,.66667],57356:[.25142,.75726,0,0,.77778],57357:[.25142,.75726,0,0,.77778],57358:[.41951,.91951,0,0,.77778],57359:[.30274,.79383,0,0,.77778],57360:[.30274,.79383,0,0,.77778],57361:[.41951,.91951,0,0,.77778],57366:[.25142,.75726,0,0,.77778],57367:[.25142,.75726,0,0,.77778],57368:[.25142,.75726,0,0,.77778],57369:[.25142,.75726,0,0,.77778],57370:[.13597,.63597,0,0,.77778],57371:[.13597,.63597,0,0,.77778]},"Caligraphic-Regular":{32:[0,0,0,0,.25],65:[0,.68333,0,.19445,.79847],66:[0,.68333,.03041,.13889,.65681],67:[0,.68333,.05834,.13889,.52653],68:[0,.68333,.02778,.08334,.77139],69:[0,.68333,.08944,.11111,.52778],70:[0,.68333,.09931,.11111,.71875],71:[.09722,.68333,.0593,.11111,.59487],72:[0,.68333,.00965,.11111,.84452],73:[0,.68333,.07382,0,.54452],74:[.09722,.68333,.18472,.16667,.67778],75:[0,.68333,.01445,.05556,.76195],76:[0,.68333,0,.13889,.68972],77:[0,.68333,0,.13889,1.2009],78:[0,.68333,.14736,.08334,.82049],79:[0,.68333,.02778,.11111,.79611],80:[0,.68333,.08222,.08334,.69556],81:[.09722,.68333,0,.11111,.81667],82:[0,.68333,0,.08334,.8475],83:[0,.68333,.075,.13889,.60556],84:[0,.68333,.25417,0,.54464],85:[0,.68333,.09931,.08334,.62583],86:[0,.68333,.08222,0,.61278],87:[0,.68333,.08222,.08334,.98778],88:[0,.68333,.14643,.13889,.7133],89:[.09722,.68333,.08222,.08334,.66834],90:[0,.68333,.07944,.13889,.72473],160:[0,0,0,0,.25]},"Fraktur-Regular":{32:[0,0,0,0,.25],33:[0,.69141,0,0,.29574],34:[0,.69141,0,0,.21471],38:[0,.69141,0,0,.73786],39:[0,.69141,0,0,.21201],40:[.24982,.74947,0,0,.38865],41:[.24982,.74947,0,0,.38865],42:[0,.62119,0,0,.27764],43:[.08319,.58283,0,0,.75623],44:[0,.10803,0,0,.27764],45:[.08319,.58283,0,0,.75623],46:[0,.10803,0,0,.27764],47:[.24982,.74947,0,0,.50181],48:[0,.47534,0,0,.50181],49:[0,.47534,0,0,.50181],50:[0,.47534,0,0,.50181],51:[.18906,.47534,0,0,.50181],52:[.18906,.47534,0,0,.50181],53:[.18906,.47534,0,0,.50181],54:[0,.69141,0,0,.50181],55:[.18906,.47534,0,0,.50181],56:[0,.69141,0,0,.50181],57:[.18906,.47534,0,0,.50181],58:[0,.47534,0,0,.21606],59:[.12604,.47534,0,0,.21606],61:[-.13099,.36866,0,0,.75623],63:[0,.69141,0,0,.36245],65:[0,.69141,0,0,.7176],66:[0,.69141,0,0,.88397],67:[0,.69141,0,0,.61254],68:[0,.69141,0,0,.83158],69:[0,.69141,0,0,.66278],70:[.12604,.69141,0,0,.61119],71:[0,.69141,0,0,.78539],72:[.06302,.69141,0,0,.7203],73:[0,.69141,0,0,.55448],74:[.12604,.69141,0,0,.55231],75:[0,.69141,0,0,.66845],76:[0,.69141,0,0,.66602],77:[0,.69141,0,0,1.04953],78:[0,.69141,0,0,.83212],79:[0,.69141,0,0,.82699],80:[.18906,.69141,0,0,.82753],81:[.03781,.69141,0,0,.82699],82:[0,.69141,0,0,.82807],83:[0,.69141,0,0,.82861],84:[0,.69141,0,0,.66899],85:[0,.69141,0,0,.64576],86:[0,.69141,0,0,.83131],87:[0,.69141,0,0,1.04602],88:[0,.69141,0,0,.71922],89:[.18906,.69141,0,0,.83293],90:[.12604,.69141,0,0,.60201],91:[.24982,.74947,0,0,.27764],93:[.24982,.74947,0,0,.27764],94:[0,.69141,0,0,.49965],97:[0,.47534,0,0,.50046],98:[0,.69141,0,0,.51315],99:[0,.47534,0,0,.38946],100:[0,.62119,0,0,.49857],101:[0,.47534,0,0,.40053],102:[.18906,.69141,0,0,.32626],103:[.18906,.47534,0,0,.5037],104:[.18906,.69141,0,0,.52126],105:[0,.69141,0,0,.27899],106:[0,.69141,0,0,.28088],107:[0,.69141,0,0,.38946],108:[0,.69141,0,0,.27953],109:[0,.47534,0,0,.76676],110:[0,.47534,0,0,.52666],111:[0,.47534,0,0,.48885],112:[.18906,.52396,0,0,.50046],113:[.18906,.47534,0,0,.48912],114:[0,.47534,0,0,.38919],115:[0,.47534,0,0,.44266],116:[0,.62119,0,0,.33301],117:[0,.47534,0,0,.5172],118:[0,.52396,0,0,.5118],119:[0,.52396,0,0,.77351],120:[.18906,.47534,0,0,.38865],121:[.18906,.47534,0,0,.49884],122:[.18906,.47534,0,0,.39054],160:[0,0,0,0,.25],8216:[0,.69141,0,0,.21471],8217:[0,.69141,0,0,.21471],58112:[0,.62119,0,0,.49749],58113:[0,.62119,0,0,.4983],58114:[.18906,.69141,0,0,.33328],58115:[.18906,.69141,0,0,.32923],58116:[.18906,.47534,0,0,.50343],58117:[0,.69141,0,0,.33301],58118:[0,.62119,0,0,.33409],58119:[0,.47534,0,0,.50073]},"Main-Bold":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.35],34:[0,.69444,0,0,.60278],35:[.19444,.69444,0,0,.95833],36:[.05556,.75,0,0,.575],37:[.05556,.75,0,0,.95833],38:[0,.69444,0,0,.89444],39:[0,.69444,0,0,.31944],40:[.25,.75,0,0,.44722],41:[.25,.75,0,0,.44722],42:[0,.75,0,0,.575],43:[.13333,.63333,0,0,.89444],44:[.19444,.15556,0,0,.31944],45:[0,.44444,0,0,.38333],46:[0,.15556,0,0,.31944],47:[.25,.75,0,0,.575],48:[0,.64444,0,0,.575],49:[0,.64444,0,0,.575],50:[0,.64444,0,0,.575],51:[0,.64444,0,0,.575],52:[0,.64444,0,0,.575],53:[0,.64444,0,0,.575],54:[0,.64444,0,0,.575],55:[0,.64444,0,0,.575],56:[0,.64444,0,0,.575],57:[0,.64444,0,0,.575],58:[0,.44444,0,0,.31944],59:[.19444,.44444,0,0,.31944],60:[.08556,.58556,0,0,.89444],61:[-.10889,.39111,0,0,.89444],62:[.08556,.58556,0,0,.89444],63:[0,.69444,0,0,.54305],64:[0,.69444,0,0,.89444],65:[0,.68611,0,0,.86944],66:[0,.68611,0,0,.81805],67:[0,.68611,0,0,.83055],68:[0,.68611,0,0,.88194],69:[0,.68611,0,0,.75555],70:[0,.68611,0,0,.72361],71:[0,.68611,0,0,.90416],72:[0,.68611,0,0,.9],73:[0,.68611,0,0,.43611],74:[0,.68611,0,0,.59444],75:[0,.68611,0,0,.90138],76:[0,.68611,0,0,.69166],77:[0,.68611,0,0,1.09166],78:[0,.68611,0,0,.9],79:[0,.68611,0,0,.86388],80:[0,.68611,0,0,.78611],81:[.19444,.68611,0,0,.86388],82:[0,.68611,0,0,.8625],83:[0,.68611,0,0,.63889],84:[0,.68611,0,0,.8],85:[0,.68611,0,0,.88472],86:[0,.68611,.01597,0,.86944],87:[0,.68611,.01597,0,1.18888],88:[0,.68611,0,0,.86944],89:[0,.68611,.02875,0,.86944],90:[0,.68611,0,0,.70277],91:[.25,.75,0,0,.31944],92:[.25,.75,0,0,.575],93:[.25,.75,0,0,.31944],94:[0,.69444,0,0,.575],95:[.31,.13444,.03194,0,.575],97:[0,.44444,0,0,.55902],98:[0,.69444,0,0,.63889],99:[0,.44444,0,0,.51111],100:[0,.69444,0,0,.63889],101:[0,.44444,0,0,.52708],102:[0,.69444,.10903,0,.35139],103:[.19444,.44444,.01597,0,.575],104:[0,.69444,0,0,.63889],105:[0,.69444,0,0,.31944],106:[.19444,.69444,0,0,.35139],107:[0,.69444,0,0,.60694],108:[0,.69444,0,0,.31944],109:[0,.44444,0,0,.95833],110:[0,.44444,0,0,.63889],111:[0,.44444,0,0,.575],112:[.19444,.44444,0,0,.63889],113:[.19444,.44444,0,0,.60694],114:[0,.44444,0,0,.47361],115:[0,.44444,0,0,.45361],116:[0,.63492,0,0,.44722],117:[0,.44444,0,0,.63889],118:[0,.44444,.01597,0,.60694],119:[0,.44444,.01597,0,.83055],120:[0,.44444,0,0,.60694],121:[.19444,.44444,.01597,0,.60694],122:[0,.44444,0,0,.51111],123:[.25,.75,0,0,.575],124:[.25,.75,0,0,.31944],125:[.25,.75,0,0,.575],126:[.35,.34444,0,0,.575],160:[0,0,0,0,.25],163:[0,.69444,0,0,.86853],168:[0,.69444,0,0,.575],172:[0,.44444,0,0,.76666],176:[0,.69444,0,0,.86944],177:[.13333,.63333,0,0,.89444],184:[.17014,0,0,0,.51111],198:[0,.68611,0,0,1.04166],215:[.13333,.63333,0,0,.89444],216:[.04861,.73472,0,0,.89444],223:[0,.69444,0,0,.59722],230:[0,.44444,0,0,.83055],247:[.13333,.63333,0,0,.89444],248:[.09722,.54167,0,0,.575],305:[0,.44444,0,0,.31944],338:[0,.68611,0,0,1.16944],339:[0,.44444,0,0,.89444],567:[.19444,.44444,0,0,.35139],710:[0,.69444,0,0,.575],711:[0,.63194,0,0,.575],713:[0,.59611,0,0,.575],714:[0,.69444,0,0,.575],715:[0,.69444,0,0,.575],728:[0,.69444,0,0,.575],729:[0,.69444,0,0,.31944],730:[0,.69444,0,0,.86944],732:[0,.69444,0,0,.575],733:[0,.69444,0,0,.575],915:[0,.68611,0,0,.69166],916:[0,.68611,0,0,.95833],920:[0,.68611,0,0,.89444],923:[0,.68611,0,0,.80555],926:[0,.68611,0,0,.76666],928:[0,.68611,0,0,.9],931:[0,.68611,0,0,.83055],933:[0,.68611,0,0,.89444],934:[0,.68611,0,0,.83055],936:[0,.68611,0,0,.89444],937:[0,.68611,0,0,.83055],8211:[0,.44444,.03194,0,.575],8212:[0,.44444,.03194,0,1.14999],8216:[0,.69444,0,0,.31944],8217:[0,.69444,0,0,.31944],8220:[0,.69444,0,0,.60278],8221:[0,.69444,0,0,.60278],8224:[.19444,.69444,0,0,.51111],8225:[.19444,.69444,0,0,.51111],8242:[0,.55556,0,0,.34444],8407:[0,.72444,.15486,0,.575],8463:[0,.69444,0,0,.66759],8465:[0,.69444,0,0,.83055],8467:[0,.69444,0,0,.47361],8472:[.19444,.44444,0,0,.74027],8476:[0,.69444,0,0,.83055],8501:[0,.69444,0,0,.70277],8592:[-.10889,.39111,0,0,1.14999],8593:[.19444,.69444,0,0,.575],8594:[-.10889,.39111,0,0,1.14999],8595:[.19444,.69444,0,0,.575],8596:[-.10889,.39111,0,0,1.14999],8597:[.25,.75,0,0,.575],8598:[.19444,.69444,0,0,1.14999],8599:[.19444,.69444,0,0,1.14999],8600:[.19444,.69444,0,0,1.14999],8601:[.19444,.69444,0,0,1.14999],8636:[-.10889,.39111,0,0,1.14999],8637:[-.10889,.39111,0,0,1.14999],8640:[-.10889,.39111,0,0,1.14999],8641:[-.10889,.39111,0,0,1.14999],8656:[-.10889,.39111,0,0,1.14999],8657:[.19444,.69444,0,0,.70277],8658:[-.10889,.39111,0,0,1.14999],8659:[.19444,.69444,0,0,.70277],8660:[-.10889,.39111,0,0,1.14999],8661:[.25,.75,0,0,.70277],8704:[0,.69444,0,0,.63889],8706:[0,.69444,.06389,0,.62847],8707:[0,.69444,0,0,.63889],8709:[.05556,.75,0,0,.575],8711:[0,.68611,0,0,.95833],8712:[.08556,.58556,0,0,.76666],8715:[.08556,.58556,0,0,.76666],8722:[.13333,.63333,0,0,.89444],8723:[.13333,.63333,0,0,.89444],8725:[.25,.75,0,0,.575],8726:[.25,.75,0,0,.575],8727:[-.02778,.47222,0,0,.575],8728:[-.02639,.47361,0,0,.575],8729:[-.02639,.47361,0,0,.575],8730:[.18,.82,0,0,.95833],8733:[0,.44444,0,0,.89444],8734:[0,.44444,0,0,1.14999],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.31944],8741:[.25,.75,0,0,.575],8743:[0,.55556,0,0,.76666],8744:[0,.55556,0,0,.76666],8745:[0,.55556,0,0,.76666],8746:[0,.55556,0,0,.76666],8747:[.19444,.69444,.12778,0,.56875],8764:[-.10889,.39111,0,0,.89444],8768:[.19444,.69444,0,0,.31944],8771:[.00222,.50222,0,0,.89444],8773:[.027,.638,0,0,.894],8776:[.02444,.52444,0,0,.89444],8781:[.00222,.50222,0,0,.89444],8801:[.00222,.50222,0,0,.89444],8804:[.19667,.69667,0,0,.89444],8805:[.19667,.69667,0,0,.89444],8810:[.08556,.58556,0,0,1.14999],8811:[.08556,.58556,0,0,1.14999],8826:[.08556,.58556,0,0,.89444],8827:[.08556,.58556,0,0,.89444],8834:[.08556,.58556,0,0,.89444],8835:[.08556,.58556,0,0,.89444],8838:[.19667,.69667,0,0,.89444],8839:[.19667,.69667,0,0,.89444],8846:[0,.55556,0,0,.76666],8849:[.19667,.69667,0,0,.89444],8850:[.19667,.69667,0,0,.89444],8851:[0,.55556,0,0,.76666],8852:[0,.55556,0,0,.76666],8853:[.13333,.63333,0,0,.89444],8854:[.13333,.63333,0,0,.89444],8855:[.13333,.63333,0,0,.89444],8856:[.13333,.63333,0,0,.89444],8857:[.13333,.63333,0,0,.89444],8866:[0,.69444,0,0,.70277],8867:[0,.69444,0,0,.70277],8868:[0,.69444,0,0,.89444],8869:[0,.69444,0,0,.89444],8900:[-.02639,.47361,0,0,.575],8901:[-.02639,.47361,0,0,.31944],8902:[-.02778,.47222,0,0,.575],8968:[.25,.75,0,0,.51111],8969:[.25,.75,0,0,.51111],8970:[.25,.75,0,0,.51111],8971:[.25,.75,0,0,.51111],8994:[-.13889,.36111,0,0,1.14999],8995:[-.13889,.36111,0,0,1.14999],9651:[.19444,.69444,0,0,1.02222],9657:[-.02778,.47222,0,0,.575],9661:[.19444,.69444,0,0,1.02222],9667:[-.02778,.47222,0,0,.575],9711:[.19444,.69444,0,0,1.14999],9824:[.12963,.69444,0,0,.89444],9825:[.12963,.69444,0,0,.89444],9826:[.12963,.69444,0,0,.89444],9827:[.12963,.69444,0,0,.89444],9837:[0,.75,0,0,.44722],9838:[.19444,.69444,0,0,.44722],9839:[.19444,.69444,0,0,.44722],10216:[.25,.75,0,0,.44722],10217:[.25,.75,0,0,.44722],10815:[0,.68611,0,0,.9],10927:[.19667,.69667,0,0,.89444],10928:[.19667,.69667,0,0,.89444],57376:[.19444,.69444,0,0,0]},"Main-BoldItalic":{32:[0,0,0,0,.25],33:[0,.69444,.11417,0,.38611],34:[0,.69444,.07939,0,.62055],35:[.19444,.69444,.06833,0,.94444],37:[.05556,.75,.12861,0,.94444],38:[0,.69444,.08528,0,.88555],39:[0,.69444,.12945,0,.35555],40:[.25,.75,.15806,0,.47333],41:[.25,.75,.03306,0,.47333],42:[0,.75,.14333,0,.59111],43:[.10333,.60333,.03306,0,.88555],44:[.19444,.14722,0,0,.35555],45:[0,.44444,.02611,0,.41444],46:[0,.14722,0,0,.35555],47:[.25,.75,.15806,0,.59111],48:[0,.64444,.13167,0,.59111],49:[0,.64444,.13167,0,.59111],50:[0,.64444,.13167,0,.59111],51:[0,.64444,.13167,0,.59111],52:[.19444,.64444,.13167,0,.59111],53:[0,.64444,.13167,0,.59111],54:[0,.64444,.13167,0,.59111],55:[.19444,.64444,.13167,0,.59111],56:[0,.64444,.13167,0,.59111],57:[0,.64444,.13167,0,.59111],58:[0,.44444,.06695,0,.35555],59:[.19444,.44444,.06695,0,.35555],61:[-.10889,.39111,.06833,0,.88555],63:[0,.69444,.11472,0,.59111],64:[0,.69444,.09208,0,.88555],65:[0,.68611,0,0,.86555],66:[0,.68611,.0992,0,.81666],67:[0,.68611,.14208,0,.82666],68:[0,.68611,.09062,0,.87555],69:[0,.68611,.11431,0,.75666],70:[0,.68611,.12903,0,.72722],71:[0,.68611,.07347,0,.89527],72:[0,.68611,.17208,0,.8961],73:[0,.68611,.15681,0,.47166],74:[0,.68611,.145,0,.61055],75:[0,.68611,.14208,0,.89499],76:[0,.68611,0,0,.69777],77:[0,.68611,.17208,0,1.07277],78:[0,.68611,.17208,0,.8961],79:[0,.68611,.09062,0,.85499],80:[0,.68611,.0992,0,.78721],81:[.19444,.68611,.09062,0,.85499],82:[0,.68611,.02559,0,.85944],83:[0,.68611,.11264,0,.64999],84:[0,.68611,.12903,0,.7961],85:[0,.68611,.17208,0,.88083],86:[0,.68611,.18625,0,.86555],87:[0,.68611,.18625,0,1.15999],88:[0,.68611,.15681,0,.86555],89:[0,.68611,.19803,0,.86555],90:[0,.68611,.14208,0,.70888],91:[.25,.75,.1875,0,.35611],93:[.25,.75,.09972,0,.35611],94:[0,.69444,.06709,0,.59111],95:[.31,.13444,.09811,0,.59111],97:[0,.44444,.09426,0,.59111],98:[0,.69444,.07861,0,.53222],99:[0,.44444,.05222,0,.53222],100:[0,.69444,.10861,0,.59111],101:[0,.44444,.085,0,.53222],102:[.19444,.69444,.21778,0,.4],103:[.19444,.44444,.105,0,.53222],104:[0,.69444,.09426,0,.59111],105:[0,.69326,.11387,0,.35555],106:[.19444,.69326,.1672,0,.35555],107:[0,.69444,.11111,0,.53222],108:[0,.69444,.10861,0,.29666],109:[0,.44444,.09426,0,.94444],110:[0,.44444,.09426,0,.64999],111:[0,.44444,.07861,0,.59111],112:[.19444,.44444,.07861,0,.59111],113:[.19444,.44444,.105,0,.53222],114:[0,.44444,.11111,0,.50167],115:[0,.44444,.08167,0,.48694],116:[0,.63492,.09639,0,.385],117:[0,.44444,.09426,0,.62055],118:[0,.44444,.11111,0,.53222],119:[0,.44444,.11111,0,.76777],120:[0,.44444,.12583,0,.56055],121:[.19444,.44444,.105,0,.56166],122:[0,.44444,.13889,0,.49055],126:[.35,.34444,.11472,0,.59111],160:[0,0,0,0,.25],168:[0,.69444,.11473,0,.59111],176:[0,.69444,0,0,.94888],184:[.17014,0,0,0,.53222],198:[0,.68611,.11431,0,1.02277],216:[.04861,.73472,.09062,0,.88555],223:[.19444,.69444,.09736,0,.665],230:[0,.44444,.085,0,.82666],248:[.09722,.54167,.09458,0,.59111],305:[0,.44444,.09426,0,.35555],338:[0,.68611,.11431,0,1.14054],339:[0,.44444,.085,0,.82666],567:[.19444,.44444,.04611,0,.385],710:[0,.69444,.06709,0,.59111],711:[0,.63194,.08271,0,.59111],713:[0,.59444,.10444,0,.59111],714:[0,.69444,.08528,0,.59111],715:[0,.69444,0,0,.59111],728:[0,.69444,.10333,0,.59111],729:[0,.69444,.12945,0,.35555],730:[0,.69444,0,0,.94888],732:[0,.69444,.11472,0,.59111],733:[0,.69444,.11472,0,.59111],915:[0,.68611,.12903,0,.69777],916:[0,.68611,0,0,.94444],920:[0,.68611,.09062,0,.88555],923:[0,.68611,0,0,.80666],926:[0,.68611,.15092,0,.76777],928:[0,.68611,.17208,0,.8961],931:[0,.68611,.11431,0,.82666],933:[0,.68611,.10778,0,.88555],934:[0,.68611,.05632,0,.82666],936:[0,.68611,.10778,0,.88555],937:[0,.68611,.0992,0,.82666],8211:[0,.44444,.09811,0,.59111],8212:[0,.44444,.09811,0,1.18221],8216:[0,.69444,.12945,0,.35555],8217:[0,.69444,.12945,0,.35555],8220:[0,.69444,.16772,0,.62055],8221:[0,.69444,.07939,0,.62055]},"Main-Italic":{32:[0,0,0,0,.25],33:[0,.69444,.12417,0,.30667],34:[0,.69444,.06961,0,.51444],35:[.19444,.69444,.06616,0,.81777],37:[.05556,.75,.13639,0,.81777],38:[0,.69444,.09694,0,.76666],39:[0,.69444,.12417,0,.30667],40:[.25,.75,.16194,0,.40889],41:[.25,.75,.03694,0,.40889],42:[0,.75,.14917,0,.51111],43:[.05667,.56167,.03694,0,.76666],44:[.19444,.10556,0,0,.30667],45:[0,.43056,.02826,0,.35778],46:[0,.10556,0,0,.30667],47:[.25,.75,.16194,0,.51111],48:[0,.64444,.13556,0,.51111],49:[0,.64444,.13556,0,.51111],50:[0,.64444,.13556,0,.51111],51:[0,.64444,.13556,0,.51111],52:[.19444,.64444,.13556,0,.51111],53:[0,.64444,.13556,0,.51111],54:[0,.64444,.13556,0,.51111],55:[.19444,.64444,.13556,0,.51111],56:[0,.64444,.13556,0,.51111],57:[0,.64444,.13556,0,.51111],58:[0,.43056,.0582,0,.30667],59:[.19444,.43056,.0582,0,.30667],61:[-.13313,.36687,.06616,0,.76666],63:[0,.69444,.1225,0,.51111],64:[0,.69444,.09597,0,.76666],65:[0,.68333,0,0,.74333],66:[0,.68333,.10257,0,.70389],67:[0,.68333,.14528,0,.71555],68:[0,.68333,.09403,0,.755],69:[0,.68333,.12028,0,.67833],70:[0,.68333,.13305,0,.65277],71:[0,.68333,.08722,0,.77361],72:[0,.68333,.16389,0,.74333],73:[0,.68333,.15806,0,.38555],74:[0,.68333,.14028,0,.525],75:[0,.68333,.14528,0,.76888],76:[0,.68333,0,0,.62722],77:[0,.68333,.16389,0,.89666],78:[0,.68333,.16389,0,.74333],79:[0,.68333,.09403,0,.76666],80:[0,.68333,.10257,0,.67833],81:[.19444,.68333,.09403,0,.76666],82:[0,.68333,.03868,0,.72944],83:[0,.68333,.11972,0,.56222],84:[0,.68333,.13305,0,.71555],85:[0,.68333,.16389,0,.74333],86:[0,.68333,.18361,0,.74333],87:[0,.68333,.18361,0,.99888],88:[0,.68333,.15806,0,.74333],89:[0,.68333,.19383,0,.74333],90:[0,.68333,.14528,0,.61333],91:[.25,.75,.1875,0,.30667],93:[.25,.75,.10528,0,.30667],94:[0,.69444,.06646,0,.51111],95:[.31,.12056,.09208,0,.51111],97:[0,.43056,.07671,0,.51111],98:[0,.69444,.06312,0,.46],99:[0,.43056,.05653,0,.46],100:[0,.69444,.10333,0,.51111],101:[0,.43056,.07514,0,.46],102:[.19444,.69444,.21194,0,.30667],103:[.19444,.43056,.08847,0,.46],104:[0,.69444,.07671,0,.51111],105:[0,.65536,.1019,0,.30667],106:[.19444,.65536,.14467,0,.30667],107:[0,.69444,.10764,0,.46],108:[0,.69444,.10333,0,.25555],109:[0,.43056,.07671,0,.81777],110:[0,.43056,.07671,0,.56222],111:[0,.43056,.06312,0,.51111],112:[.19444,.43056,.06312,0,.51111],113:[.19444,.43056,.08847,0,.46],114:[0,.43056,.10764,0,.42166],115:[0,.43056,.08208,0,.40889],116:[0,.61508,.09486,0,.33222],117:[0,.43056,.07671,0,.53666],118:[0,.43056,.10764,0,.46],119:[0,.43056,.10764,0,.66444],120:[0,.43056,.12042,0,.46389],121:[.19444,.43056,.08847,0,.48555],122:[0,.43056,.12292,0,.40889],126:[.35,.31786,.11585,0,.51111],160:[0,0,0,0,.25],168:[0,.66786,.10474,0,.51111],176:[0,.69444,0,0,.83129],184:[.17014,0,0,0,.46],198:[0,.68333,.12028,0,.88277],216:[.04861,.73194,.09403,0,.76666],223:[.19444,.69444,.10514,0,.53666],230:[0,.43056,.07514,0,.71555],248:[.09722,.52778,.09194,0,.51111],338:[0,.68333,.12028,0,.98499],339:[0,.43056,.07514,0,.71555],710:[0,.69444,.06646,0,.51111],711:[0,.62847,.08295,0,.51111],713:[0,.56167,.10333,0,.51111],714:[0,.69444,.09694,0,.51111],715:[0,.69444,0,0,.51111],728:[0,.69444,.10806,0,.51111],729:[0,.66786,.11752,0,.30667],730:[0,.69444,0,0,.83129],732:[0,.66786,.11585,0,.51111],733:[0,.69444,.1225,0,.51111],915:[0,.68333,.13305,0,.62722],916:[0,.68333,0,0,.81777],920:[0,.68333,.09403,0,.76666],923:[0,.68333,0,0,.69222],926:[0,.68333,.15294,0,.66444],928:[0,.68333,.16389,0,.74333],931:[0,.68333,.12028,0,.71555],933:[0,.68333,.11111,0,.76666],934:[0,.68333,.05986,0,.71555],936:[0,.68333,.11111,0,.76666],937:[0,.68333,.10257,0,.71555],8211:[0,.43056,.09208,0,.51111],8212:[0,.43056,.09208,0,1.02222],8216:[0,.69444,.12417,0,.30667],8217:[0,.69444,.12417,0,.30667],8220:[0,.69444,.1685,0,.51444],8221:[0,.69444,.06961,0,.51444],8463:[0,.68889,0,0,.54028]},"Main-Regular":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.27778],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.77778],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.19444,.10556,0,0,.27778],45:[0,.43056,0,0,.33333],46:[0,.10556,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.64444,0,0,.5],49:[0,.64444,0,0,.5],50:[0,.64444,0,0,.5],51:[0,.64444,0,0,.5],52:[0,.64444,0,0,.5],53:[0,.64444,0,0,.5],54:[0,.64444,0,0,.5],55:[0,.64444,0,0,.5],56:[0,.64444,0,0,.5],57:[0,.64444,0,0,.5],58:[0,.43056,0,0,.27778],59:[.19444,.43056,0,0,.27778],60:[.0391,.5391,0,0,.77778],61:[-.13313,.36687,0,0,.77778],62:[.0391,.5391,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.77778],65:[0,.68333,0,0,.75],66:[0,.68333,0,0,.70834],67:[0,.68333,0,0,.72222],68:[0,.68333,0,0,.76389],69:[0,.68333,0,0,.68056],70:[0,.68333,0,0,.65278],71:[0,.68333,0,0,.78472],72:[0,.68333,0,0,.75],73:[0,.68333,0,0,.36111],74:[0,.68333,0,0,.51389],75:[0,.68333,0,0,.77778],76:[0,.68333,0,0,.625],77:[0,.68333,0,0,.91667],78:[0,.68333,0,0,.75],79:[0,.68333,0,0,.77778],80:[0,.68333,0,0,.68056],81:[.19444,.68333,0,0,.77778],82:[0,.68333,0,0,.73611],83:[0,.68333,0,0,.55556],84:[0,.68333,0,0,.72222],85:[0,.68333,0,0,.75],86:[0,.68333,.01389,0,.75],87:[0,.68333,.01389,0,1.02778],88:[0,.68333,0,0,.75],89:[0,.68333,.025,0,.75],90:[0,.68333,0,0,.61111],91:[.25,.75,0,0,.27778],92:[.25,.75,0,0,.5],93:[.25,.75,0,0,.27778],94:[0,.69444,0,0,.5],95:[.31,.12056,.02778,0,.5],97:[0,.43056,0,0,.5],98:[0,.69444,0,0,.55556],99:[0,.43056,0,0,.44445],100:[0,.69444,0,0,.55556],101:[0,.43056,0,0,.44445],102:[0,.69444,.07778,0,.30556],103:[.19444,.43056,.01389,0,.5],104:[0,.69444,0,0,.55556],105:[0,.66786,0,0,.27778],106:[.19444,.66786,0,0,.30556],107:[0,.69444,0,0,.52778],108:[0,.69444,0,0,.27778],109:[0,.43056,0,0,.83334],110:[0,.43056,0,0,.55556],111:[0,.43056,0,0,.5],112:[.19444,.43056,0,0,.55556],113:[.19444,.43056,0,0,.52778],114:[0,.43056,0,0,.39167],115:[0,.43056,0,0,.39445],116:[0,.61508,0,0,.38889],117:[0,.43056,0,0,.55556],118:[0,.43056,.01389,0,.52778],119:[0,.43056,.01389,0,.72222],120:[0,.43056,0,0,.52778],121:[.19444,.43056,.01389,0,.52778],122:[0,.43056,0,0,.44445],123:[.25,.75,0,0,.5],124:[.25,.75,0,0,.27778],125:[.25,.75,0,0,.5],126:[.35,.31786,0,0,.5],160:[0,0,0,0,.25],163:[0,.69444,0,0,.76909],167:[.19444,.69444,0,0,.44445],168:[0,.66786,0,0,.5],172:[0,.43056,0,0,.66667],176:[0,.69444,0,0,.75],177:[.08333,.58333,0,0,.77778],182:[.19444,.69444,0,0,.61111],184:[.17014,0,0,0,.44445],198:[0,.68333,0,0,.90278],215:[.08333,.58333,0,0,.77778],216:[.04861,.73194,0,0,.77778],223:[0,.69444,0,0,.5],230:[0,.43056,0,0,.72222],247:[.08333,.58333,0,0,.77778],248:[.09722,.52778,0,0,.5],305:[0,.43056,0,0,.27778],338:[0,.68333,0,0,1.01389],339:[0,.43056,0,0,.77778],567:[.19444,.43056,0,0,.30556],710:[0,.69444,0,0,.5],711:[0,.62847,0,0,.5],713:[0,.56778,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.66786,0,0,.27778],730:[0,.69444,0,0,.75],732:[0,.66786,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.68333,0,0,.625],916:[0,.68333,0,0,.83334],920:[0,.68333,0,0,.77778],923:[0,.68333,0,0,.69445],926:[0,.68333,0,0,.66667],928:[0,.68333,0,0,.75],931:[0,.68333,0,0,.72222],933:[0,.68333,0,0,.77778],934:[0,.68333,0,0,.72222],936:[0,.68333,0,0,.77778],937:[0,.68333,0,0,.72222],8211:[0,.43056,.02778,0,.5],8212:[0,.43056,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5],8224:[.19444,.69444,0,0,.44445],8225:[.19444,.69444,0,0,.44445],8230:[0,.123,0,0,1.172],8242:[0,.55556,0,0,.275],8407:[0,.71444,.15382,0,.5],8463:[0,.68889,0,0,.54028],8465:[0,.69444,0,0,.72222],8467:[0,.69444,0,.11111,.41667],8472:[.19444,.43056,0,.11111,.63646],8476:[0,.69444,0,0,.72222],8501:[0,.69444,0,0,.61111],8592:[-.13313,.36687,0,0,1],8593:[.19444,.69444,0,0,.5],8594:[-.13313,.36687,0,0,1],8595:[.19444,.69444,0,0,.5],8596:[-.13313,.36687,0,0,1],8597:[.25,.75,0,0,.5],8598:[.19444,.69444,0,0,1],8599:[.19444,.69444,0,0,1],8600:[.19444,.69444,0,0,1],8601:[.19444,.69444,0,0,1],8614:[.011,.511,0,0,1],8617:[.011,.511,0,0,1.126],8618:[.011,.511,0,0,1.126],8636:[-.13313,.36687,0,0,1],8637:[-.13313,.36687,0,0,1],8640:[-.13313,.36687,0,0,1],8641:[-.13313,.36687,0,0,1],8652:[.011,.671,0,0,1],8656:[-.13313,.36687,0,0,1],8657:[.19444,.69444,0,0,.61111],8658:[-.13313,.36687,0,0,1],8659:[.19444,.69444,0,0,.61111],8660:[-.13313,.36687,0,0,1],8661:[.25,.75,0,0,.61111],8704:[0,.69444,0,0,.55556],8706:[0,.69444,.05556,.08334,.5309],8707:[0,.69444,0,0,.55556],8709:[.05556,.75,0,0,.5],8711:[0,.68333,0,0,.83334],8712:[.0391,.5391,0,0,.66667],8715:[.0391,.5391,0,0,.66667],8722:[.08333,.58333,0,0,.77778],8723:[.08333,.58333,0,0,.77778],8725:[.25,.75,0,0,.5],8726:[.25,.75,0,0,.5],8727:[-.03472,.46528,0,0,.5],8728:[-.05555,.44445,0,0,.5],8729:[-.05555,.44445,0,0,.5],8730:[.2,.8,0,0,.83334],8733:[0,.43056,0,0,.77778],8734:[0,.43056,0,0,1],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.27778],8741:[.25,.75,0,0,.5],8743:[0,.55556,0,0,.66667],8744:[0,.55556,0,0,.66667],8745:[0,.55556,0,0,.66667],8746:[0,.55556,0,0,.66667],8747:[.19444,.69444,.11111,0,.41667],8764:[-.13313,.36687,0,0,.77778],8768:[.19444,.69444,0,0,.27778],8771:[-.03625,.46375,0,0,.77778],8773:[-.022,.589,0,0,.778],8776:[-.01688,.48312,0,0,.77778],8781:[-.03625,.46375,0,0,.77778],8784:[-.133,.673,0,0,.778],8801:[-.03625,.46375,0,0,.77778],8804:[.13597,.63597,0,0,.77778],8805:[.13597,.63597,0,0,.77778],8810:[.0391,.5391,0,0,1],8811:[.0391,.5391,0,0,1],8826:[.0391,.5391,0,0,.77778],8827:[.0391,.5391,0,0,.77778],8834:[.0391,.5391,0,0,.77778],8835:[.0391,.5391,0,0,.77778],8838:[.13597,.63597,0,0,.77778],8839:[.13597,.63597,0,0,.77778],8846:[0,.55556,0,0,.66667],8849:[.13597,.63597,0,0,.77778],8850:[.13597,.63597,0,0,.77778],8851:[0,.55556,0,0,.66667],8852:[0,.55556,0,0,.66667],8853:[.08333,.58333,0,0,.77778],8854:[.08333,.58333,0,0,.77778],8855:[.08333,.58333,0,0,.77778],8856:[.08333,.58333,0,0,.77778],8857:[.08333,.58333,0,0,.77778],8866:[0,.69444,0,0,.61111],8867:[0,.69444,0,0,.61111],8868:[0,.69444,0,0,.77778],8869:[0,.69444,0,0,.77778],8872:[.249,.75,0,0,.867],8900:[-.05555,.44445,0,0,.5],8901:[-.05555,.44445,0,0,.27778],8902:[-.03472,.46528,0,0,.5],8904:[.005,.505,0,0,.9],8942:[.03,.903,0,0,.278],8943:[-.19,.313,0,0,1.172],8945:[-.1,.823,0,0,1.282],8968:[.25,.75,0,0,.44445],8969:[.25,.75,0,0,.44445],8970:[.25,.75,0,0,.44445],8971:[.25,.75,0,0,.44445],8994:[-.14236,.35764,0,0,1],8995:[-.14236,.35764,0,0,1],9136:[.244,.744,0,0,.412],9137:[.244,.745,0,0,.412],9651:[.19444,.69444,0,0,.88889],9657:[-.03472,.46528,0,0,.5],9661:[.19444,.69444,0,0,.88889],9667:[-.03472,.46528,0,0,.5],9711:[.19444,.69444,0,0,1],9824:[.12963,.69444,0,0,.77778],9825:[.12963,.69444,0,0,.77778],9826:[.12963,.69444,0,0,.77778],9827:[.12963,.69444,0,0,.77778],9837:[0,.75,0,0,.38889],9838:[.19444,.69444,0,0,.38889],9839:[.19444,.69444,0,0,.38889],10216:[.25,.75,0,0,.38889],10217:[.25,.75,0,0,.38889],10222:[.244,.744,0,0,.412],10223:[.244,.745,0,0,.412],10229:[.011,.511,0,0,1.609],10230:[.011,.511,0,0,1.638],10231:[.011,.511,0,0,1.859],10232:[.024,.525,0,0,1.609],10233:[.024,.525,0,0,1.638],10234:[.024,.525,0,0,1.858],10236:[.011,.511,0,0,1.638],10815:[0,.68333,0,0,.75],10927:[.13597,.63597,0,0,.77778],10928:[.13597,.63597,0,0,.77778],57376:[.19444,.69444,0,0,0]},"Math-BoldItalic":{32:[0,0,0,0,.25],48:[0,.44444,0,0,.575],49:[0,.44444,0,0,.575],50:[0,.44444,0,0,.575],51:[.19444,.44444,0,0,.575],52:[.19444,.44444,0,0,.575],53:[.19444,.44444,0,0,.575],54:[0,.64444,0,0,.575],55:[.19444,.44444,0,0,.575],56:[0,.64444,0,0,.575],57:[.19444,.44444,0,0,.575],65:[0,.68611,0,0,.86944],66:[0,.68611,.04835,0,.8664],67:[0,.68611,.06979,0,.81694],68:[0,.68611,.03194,0,.93812],69:[0,.68611,.05451,0,.81007],70:[0,.68611,.15972,0,.68889],71:[0,.68611,0,0,.88673],72:[0,.68611,.08229,0,.98229],73:[0,.68611,.07778,0,.51111],74:[0,.68611,.10069,0,.63125],75:[0,.68611,.06979,0,.97118],76:[0,.68611,0,0,.75555],77:[0,.68611,.11424,0,1.14201],78:[0,.68611,.11424,0,.95034],79:[0,.68611,.03194,0,.83666],80:[0,.68611,.15972,0,.72309],81:[.19444,.68611,0,0,.86861],82:[0,.68611,.00421,0,.87235],83:[0,.68611,.05382,0,.69271],84:[0,.68611,.15972,0,.63663],85:[0,.68611,.11424,0,.80027],86:[0,.68611,.25555,0,.67778],87:[0,.68611,.15972,0,1.09305],88:[0,.68611,.07778,0,.94722],89:[0,.68611,.25555,0,.67458],90:[0,.68611,.06979,0,.77257],97:[0,.44444,0,0,.63287],98:[0,.69444,0,0,.52083],99:[0,.44444,0,0,.51342],100:[0,.69444,0,0,.60972],101:[0,.44444,0,0,.55361],102:[.19444,.69444,.11042,0,.56806],103:[.19444,.44444,.03704,0,.5449],104:[0,.69444,0,0,.66759],105:[0,.69326,0,0,.4048],106:[.19444,.69326,.0622,0,.47083],107:[0,.69444,.01852,0,.6037],108:[0,.69444,.0088,0,.34815],109:[0,.44444,0,0,1.0324],110:[0,.44444,0,0,.71296],111:[0,.44444,0,0,.58472],112:[.19444,.44444,0,0,.60092],113:[.19444,.44444,.03704,0,.54213],114:[0,.44444,.03194,0,.5287],115:[0,.44444,0,0,.53125],116:[0,.63492,0,0,.41528],117:[0,.44444,0,0,.68102],118:[0,.44444,.03704,0,.56666],119:[0,.44444,.02778,0,.83148],120:[0,.44444,0,0,.65903],121:[.19444,.44444,.03704,0,.59028],122:[0,.44444,.04213,0,.55509],160:[0,0,0,0,.25],915:[0,.68611,.15972,0,.65694],916:[0,.68611,0,0,.95833],920:[0,.68611,.03194,0,.86722],923:[0,.68611,0,0,.80555],926:[0,.68611,.07458,0,.84125],928:[0,.68611,.08229,0,.98229],931:[0,.68611,.05451,0,.88507],933:[0,.68611,.15972,0,.67083],934:[0,.68611,0,0,.76666],936:[0,.68611,.11653,0,.71402],937:[0,.68611,.04835,0,.8789],945:[0,.44444,0,0,.76064],946:[.19444,.69444,.03403,0,.65972],947:[.19444,.44444,.06389,0,.59003],948:[0,.69444,.03819,0,.52222],949:[0,.44444,0,0,.52882],950:[.19444,.69444,.06215,0,.50833],951:[.19444,.44444,.03704,0,.6],952:[0,.69444,.03194,0,.5618],953:[0,.44444,0,0,.41204],954:[0,.44444,0,0,.66759],955:[0,.69444,0,0,.67083],956:[.19444,.44444,0,0,.70787],957:[0,.44444,.06898,0,.57685],958:[.19444,.69444,.03021,0,.50833],959:[0,.44444,0,0,.58472],960:[0,.44444,.03704,0,.68241],961:[.19444,.44444,0,0,.6118],962:[.09722,.44444,.07917,0,.42361],963:[0,.44444,.03704,0,.68588],964:[0,.44444,.13472,0,.52083],965:[0,.44444,.03704,0,.63055],966:[.19444,.44444,0,0,.74722],967:[.19444,.44444,0,0,.71805],968:[.19444,.69444,.03704,0,.75833],969:[0,.44444,.03704,0,.71782],977:[0,.69444,0,0,.69155],981:[.19444,.69444,0,0,.7125],982:[0,.44444,.03194,0,.975],1009:[.19444,.44444,0,0,.6118],1013:[0,.44444,0,0,.48333],57649:[0,.44444,0,0,.39352],57911:[.19444,.44444,0,0,.43889]},"Math-Italic":{32:[0,0,0,0,.25],48:[0,.43056,0,0,.5],49:[0,.43056,0,0,.5],50:[0,.43056,0,0,.5],51:[.19444,.43056,0,0,.5],52:[.19444,.43056,0,0,.5],53:[.19444,.43056,0,0,.5],54:[0,.64444,0,0,.5],55:[.19444,.43056,0,0,.5],56:[0,.64444,0,0,.5],57:[.19444,.43056,0,0,.5],65:[0,.68333,0,.13889,.75],66:[0,.68333,.05017,.08334,.75851],67:[0,.68333,.07153,.08334,.71472],68:[0,.68333,.02778,.05556,.82792],69:[0,.68333,.05764,.08334,.7382],70:[0,.68333,.13889,.08334,.64306],71:[0,.68333,0,.08334,.78625],72:[0,.68333,.08125,.05556,.83125],73:[0,.68333,.07847,.11111,.43958],74:[0,.68333,.09618,.16667,.55451],75:[0,.68333,.07153,.05556,.84931],76:[0,.68333,0,.02778,.68056],77:[0,.68333,.10903,.08334,.97014],78:[0,.68333,.10903,.08334,.80347],79:[0,.68333,.02778,.08334,.76278],80:[0,.68333,.13889,.08334,.64201],81:[.19444,.68333,0,.08334,.79056],82:[0,.68333,.00773,.08334,.75929],83:[0,.68333,.05764,.08334,.6132],84:[0,.68333,.13889,.08334,.58438],85:[0,.68333,.10903,.02778,.68278],86:[0,.68333,.22222,0,.58333],87:[0,.68333,.13889,0,.94445],88:[0,.68333,.07847,.08334,.82847],89:[0,.68333,.22222,0,.58056],90:[0,.68333,.07153,.08334,.68264],97:[0,.43056,0,0,.52859],98:[0,.69444,0,0,.42917],99:[0,.43056,0,.05556,.43276],100:[0,.69444,0,.16667,.52049],101:[0,.43056,0,.05556,.46563],102:[.19444,.69444,.10764,.16667,.48959],103:[.19444,.43056,.03588,.02778,.47697],104:[0,.69444,0,0,.57616],105:[0,.65952,0,0,.34451],106:[.19444,.65952,.05724,0,.41181],107:[0,.69444,.03148,0,.5206],108:[0,.69444,.01968,.08334,.29838],109:[0,.43056,0,0,.87801],110:[0,.43056,0,0,.60023],111:[0,.43056,0,.05556,.48472],112:[.19444,.43056,0,.08334,.50313],113:[.19444,.43056,.03588,.08334,.44641],114:[0,.43056,.02778,.05556,.45116],115:[0,.43056,0,.05556,.46875],116:[0,.61508,0,.08334,.36111],117:[0,.43056,0,.02778,.57246],118:[0,.43056,.03588,.02778,.48472],119:[0,.43056,.02691,.08334,.71592],120:[0,.43056,0,.02778,.57153],121:[.19444,.43056,.03588,.05556,.49028],122:[0,.43056,.04398,.05556,.46505],160:[0,0,0,0,.25],915:[0,.68333,.13889,.08334,.61528],916:[0,.68333,0,.16667,.83334],920:[0,.68333,.02778,.08334,.76278],923:[0,.68333,0,.16667,.69445],926:[0,.68333,.07569,.08334,.74236],928:[0,.68333,.08125,.05556,.83125],931:[0,.68333,.05764,.08334,.77986],933:[0,.68333,.13889,.05556,.58333],934:[0,.68333,0,.08334,.66667],936:[0,.68333,.11,.05556,.61222],937:[0,.68333,.05017,.08334,.7724],945:[0,.43056,.0037,.02778,.6397],946:[.19444,.69444,.05278,.08334,.56563],947:[.19444,.43056,.05556,0,.51773],948:[0,.69444,.03785,.05556,.44444],949:[0,.43056,0,.08334,.46632],950:[.19444,.69444,.07378,.08334,.4375],951:[.19444,.43056,.03588,.05556,.49653],952:[0,.69444,.02778,.08334,.46944],953:[0,.43056,0,.05556,.35394],954:[0,.43056,0,0,.57616],955:[0,.69444,0,0,.58334],956:[.19444,.43056,0,.02778,.60255],957:[0,.43056,.06366,.02778,.49398],958:[.19444,.69444,.04601,.11111,.4375],959:[0,.43056,0,.05556,.48472],960:[0,.43056,.03588,0,.57003],961:[.19444,.43056,0,.08334,.51702],962:[.09722,.43056,.07986,.08334,.36285],963:[0,.43056,.03588,0,.57141],964:[0,.43056,.1132,.02778,.43715],965:[0,.43056,.03588,.02778,.54028],966:[.19444,.43056,0,.08334,.65417],967:[.19444,.43056,0,.05556,.62569],968:[.19444,.69444,.03588,.11111,.65139],969:[0,.43056,.03588,0,.62245],977:[0,.69444,0,.08334,.59144],981:[.19444,.69444,0,.08334,.59583],982:[0,.43056,.02778,0,.82813],1009:[.19444,.43056,0,.08334,.51702],1013:[0,.43056,0,.05556,.4059],57649:[0,.43056,0,.02778,.32246],57911:[.19444,.43056,0,.08334,.38403]},"SansSerif-Bold":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.36667],34:[0,.69444,0,0,.55834],35:[.19444,.69444,0,0,.91667],36:[.05556,.75,0,0,.55],37:[.05556,.75,0,0,1.02912],38:[0,.69444,0,0,.83056],39:[0,.69444,0,0,.30556],40:[.25,.75,0,0,.42778],41:[.25,.75,0,0,.42778],42:[0,.75,0,0,.55],43:[.11667,.61667,0,0,.85556],44:[.10556,.13056,0,0,.30556],45:[0,.45833,0,0,.36667],46:[0,.13056,0,0,.30556],47:[.25,.75,0,0,.55],48:[0,.69444,0,0,.55],49:[0,.69444,0,0,.55],50:[0,.69444,0,0,.55],51:[0,.69444,0,0,.55],52:[0,.69444,0,0,.55],53:[0,.69444,0,0,.55],54:[0,.69444,0,0,.55],55:[0,.69444,0,0,.55],56:[0,.69444,0,0,.55],57:[0,.69444,0,0,.55],58:[0,.45833,0,0,.30556],59:[.10556,.45833,0,0,.30556],61:[-.09375,.40625,0,0,.85556],63:[0,.69444,0,0,.51945],64:[0,.69444,0,0,.73334],65:[0,.69444,0,0,.73334],66:[0,.69444,0,0,.73334],67:[0,.69444,0,0,.70278],68:[0,.69444,0,0,.79445],69:[0,.69444,0,0,.64167],70:[0,.69444,0,0,.61111],71:[0,.69444,0,0,.73334],72:[0,.69444,0,0,.79445],73:[0,.69444,0,0,.33056],74:[0,.69444,0,0,.51945],75:[0,.69444,0,0,.76389],76:[0,.69444,0,0,.58056],77:[0,.69444,0,0,.97778],78:[0,.69444,0,0,.79445],79:[0,.69444,0,0,.79445],80:[0,.69444,0,0,.70278],81:[.10556,.69444,0,0,.79445],82:[0,.69444,0,0,.70278],83:[0,.69444,0,0,.61111],84:[0,.69444,0,0,.73334],85:[0,.69444,0,0,.76389],86:[0,.69444,.01528,0,.73334],87:[0,.69444,.01528,0,1.03889],88:[0,.69444,0,0,.73334],89:[0,.69444,.0275,0,.73334],90:[0,.69444,0,0,.67223],91:[.25,.75,0,0,.34306],93:[.25,.75,0,0,.34306],94:[0,.69444,0,0,.55],95:[.35,.10833,.03056,0,.55],97:[0,.45833,0,0,.525],98:[0,.69444,0,0,.56111],99:[0,.45833,0,0,.48889],100:[0,.69444,0,0,.56111],101:[0,.45833,0,0,.51111],102:[0,.69444,.07639,0,.33611],103:[.19444,.45833,.01528,0,.55],104:[0,.69444,0,0,.56111],105:[0,.69444,0,0,.25556],106:[.19444,.69444,0,0,.28611],107:[0,.69444,0,0,.53056],108:[0,.69444,0,0,.25556],109:[0,.45833,0,0,.86667],110:[0,.45833,0,0,.56111],111:[0,.45833,0,0,.55],112:[.19444,.45833,0,0,.56111],113:[.19444,.45833,0,0,.56111],114:[0,.45833,.01528,0,.37222],115:[0,.45833,0,0,.42167],116:[0,.58929,0,0,.40417],117:[0,.45833,0,0,.56111],118:[0,.45833,.01528,0,.5],119:[0,.45833,.01528,0,.74445],120:[0,.45833,0,0,.5],121:[.19444,.45833,.01528,0,.5],122:[0,.45833,0,0,.47639],126:[.35,.34444,0,0,.55],160:[0,0,0,0,.25],168:[0,.69444,0,0,.55],176:[0,.69444,0,0,.73334],180:[0,.69444,0,0,.55],184:[.17014,0,0,0,.48889],305:[0,.45833,0,0,.25556],567:[.19444,.45833,0,0,.28611],710:[0,.69444,0,0,.55],711:[0,.63542,0,0,.55],713:[0,.63778,0,0,.55],728:[0,.69444,0,0,.55],729:[0,.69444,0,0,.30556],730:[0,.69444,0,0,.73334],732:[0,.69444,0,0,.55],733:[0,.69444,0,0,.55],915:[0,.69444,0,0,.58056],916:[0,.69444,0,0,.91667],920:[0,.69444,0,0,.85556],923:[0,.69444,0,0,.67223],926:[0,.69444,0,0,.73334],928:[0,.69444,0,0,.79445],931:[0,.69444,0,0,.79445],933:[0,.69444,0,0,.85556],934:[0,.69444,0,0,.79445],936:[0,.69444,0,0,.85556],937:[0,.69444,0,0,.79445],8211:[0,.45833,.03056,0,.55],8212:[0,.45833,.03056,0,1.10001],8216:[0,.69444,0,0,.30556],8217:[0,.69444,0,0,.30556],8220:[0,.69444,0,0,.55834],8221:[0,.69444,0,0,.55834]},"SansSerif-Italic":{32:[0,0,0,0,.25],33:[0,.69444,.05733,0,.31945],34:[0,.69444,.00316,0,.5],35:[.19444,.69444,.05087,0,.83334],36:[.05556,.75,.11156,0,.5],37:[.05556,.75,.03126,0,.83334],38:[0,.69444,.03058,0,.75834],39:[0,.69444,.07816,0,.27778],40:[.25,.75,.13164,0,.38889],41:[.25,.75,.02536,0,.38889],42:[0,.75,.11775,0,.5],43:[.08333,.58333,.02536,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,.01946,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,.13164,0,.5],48:[0,.65556,.11156,0,.5],49:[0,.65556,.11156,0,.5],50:[0,.65556,.11156,0,.5],51:[0,.65556,.11156,0,.5],52:[0,.65556,.11156,0,.5],53:[0,.65556,.11156,0,.5],54:[0,.65556,.11156,0,.5],55:[0,.65556,.11156,0,.5],56:[0,.65556,.11156,0,.5],57:[0,.65556,.11156,0,.5],58:[0,.44444,.02502,0,.27778],59:[.125,.44444,.02502,0,.27778],61:[-.13,.37,.05087,0,.77778],63:[0,.69444,.11809,0,.47222],64:[0,.69444,.07555,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,.08293,0,.66667],67:[0,.69444,.11983,0,.63889],68:[0,.69444,.07555,0,.72223],69:[0,.69444,.11983,0,.59722],70:[0,.69444,.13372,0,.56945],71:[0,.69444,.11983,0,.66667],72:[0,.69444,.08094,0,.70834],73:[0,.69444,.13372,0,.27778],74:[0,.69444,.08094,0,.47222],75:[0,.69444,.11983,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,.08094,0,.875],78:[0,.69444,.08094,0,.70834],79:[0,.69444,.07555,0,.73611],80:[0,.69444,.08293,0,.63889],81:[.125,.69444,.07555,0,.73611],82:[0,.69444,.08293,0,.64584],83:[0,.69444,.09205,0,.55556],84:[0,.69444,.13372,0,.68056],85:[0,.69444,.08094,0,.6875],86:[0,.69444,.1615,0,.66667],87:[0,.69444,.1615,0,.94445],88:[0,.69444,.13372,0,.66667],89:[0,.69444,.17261,0,.66667],90:[0,.69444,.11983,0,.61111],91:[.25,.75,.15942,0,.28889],93:[.25,.75,.08719,0,.28889],94:[0,.69444,.0799,0,.5],95:[.35,.09444,.08616,0,.5],97:[0,.44444,.00981,0,.48056],98:[0,.69444,.03057,0,.51667],99:[0,.44444,.08336,0,.44445],100:[0,.69444,.09483,0,.51667],101:[0,.44444,.06778,0,.44445],102:[0,.69444,.21705,0,.30556],103:[.19444,.44444,.10836,0,.5],104:[0,.69444,.01778,0,.51667],105:[0,.67937,.09718,0,.23889],106:[.19444,.67937,.09162,0,.26667],107:[0,.69444,.08336,0,.48889],108:[0,.69444,.09483,0,.23889],109:[0,.44444,.01778,0,.79445],110:[0,.44444,.01778,0,.51667],111:[0,.44444,.06613,0,.5],112:[.19444,.44444,.0389,0,.51667],113:[.19444,.44444,.04169,0,.51667],114:[0,.44444,.10836,0,.34167],115:[0,.44444,.0778,0,.38333],116:[0,.57143,.07225,0,.36111],117:[0,.44444,.04169,0,.51667],118:[0,.44444,.10836,0,.46111],119:[0,.44444,.10836,0,.68334],120:[0,.44444,.09169,0,.46111],121:[.19444,.44444,.10836,0,.46111],122:[0,.44444,.08752,0,.43472],126:[.35,.32659,.08826,0,.5],160:[0,0,0,0,.25],168:[0,.67937,.06385,0,.5],176:[0,.69444,0,0,.73752],184:[.17014,0,0,0,.44445],305:[0,.44444,.04169,0,.23889],567:[.19444,.44444,.04169,0,.26667],710:[0,.69444,.0799,0,.5],711:[0,.63194,.08432,0,.5],713:[0,.60889,.08776,0,.5],714:[0,.69444,.09205,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,.09483,0,.5],729:[0,.67937,.07774,0,.27778],730:[0,.69444,0,0,.73752],732:[0,.67659,.08826,0,.5],733:[0,.69444,.09205,0,.5],915:[0,.69444,.13372,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,.07555,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,.12816,0,.66667],928:[0,.69444,.08094,0,.70834],931:[0,.69444,.11983,0,.72222],933:[0,.69444,.09031,0,.77778],934:[0,.69444,.04603,0,.72222],936:[0,.69444,.09031,0,.77778],937:[0,.69444,.08293,0,.72222],8211:[0,.44444,.08616,0,.5],8212:[0,.44444,.08616,0,1],8216:[0,.69444,.07816,0,.27778],8217:[0,.69444,.07816,0,.27778],8220:[0,.69444,.14205,0,.5],8221:[0,.69444,.00316,0,.5]},"SansSerif-Regular":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.31945],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.75834],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,0,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.65556,0,0,.5],49:[0,.65556,0,0,.5],50:[0,.65556,0,0,.5],51:[0,.65556,0,0,.5],52:[0,.65556,0,0,.5],53:[0,.65556,0,0,.5],54:[0,.65556,0,0,.5],55:[0,.65556,0,0,.5],56:[0,.65556,0,0,.5],57:[0,.65556,0,0,.5],58:[0,.44444,0,0,.27778],59:[.125,.44444,0,0,.27778],61:[-.13,.37,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,0,0,.66667],67:[0,.69444,0,0,.63889],68:[0,.69444,0,0,.72223],69:[0,.69444,0,0,.59722],70:[0,.69444,0,0,.56945],71:[0,.69444,0,0,.66667],72:[0,.69444,0,0,.70834],73:[0,.69444,0,0,.27778],74:[0,.69444,0,0,.47222],75:[0,.69444,0,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,0,0,.875],78:[0,.69444,0,0,.70834],79:[0,.69444,0,0,.73611],80:[0,.69444,0,0,.63889],81:[.125,.69444,0,0,.73611],82:[0,.69444,0,0,.64584],83:[0,.69444,0,0,.55556],84:[0,.69444,0,0,.68056],85:[0,.69444,0,0,.6875],86:[0,.69444,.01389,0,.66667],87:[0,.69444,.01389,0,.94445],88:[0,.69444,0,0,.66667],89:[0,.69444,.025,0,.66667],90:[0,.69444,0,0,.61111],91:[.25,.75,0,0,.28889],93:[.25,.75,0,0,.28889],94:[0,.69444,0,0,.5],95:[.35,.09444,.02778,0,.5],97:[0,.44444,0,0,.48056],98:[0,.69444,0,0,.51667],99:[0,.44444,0,0,.44445],100:[0,.69444,0,0,.51667],101:[0,.44444,0,0,.44445],102:[0,.69444,.06944,0,.30556],103:[.19444,.44444,.01389,0,.5],104:[0,.69444,0,0,.51667],105:[0,.67937,0,0,.23889],106:[.19444,.67937,0,0,.26667],107:[0,.69444,0,0,.48889],108:[0,.69444,0,0,.23889],109:[0,.44444,0,0,.79445],110:[0,.44444,0,0,.51667],111:[0,.44444,0,0,.5],112:[.19444,.44444,0,0,.51667],113:[.19444,.44444,0,0,.51667],114:[0,.44444,.01389,0,.34167],115:[0,.44444,0,0,.38333],116:[0,.57143,0,0,.36111],117:[0,.44444,0,0,.51667],118:[0,.44444,.01389,0,.46111],119:[0,.44444,.01389,0,.68334],120:[0,.44444,0,0,.46111],121:[.19444,.44444,.01389,0,.46111],122:[0,.44444,0,0,.43472],126:[.35,.32659,0,0,.5],160:[0,0,0,0,.25],168:[0,.67937,0,0,.5],176:[0,.69444,0,0,.66667],184:[.17014,0,0,0,.44445],305:[0,.44444,0,0,.23889],567:[.19444,.44444,0,0,.26667],710:[0,.69444,0,0,.5],711:[0,.63194,0,0,.5],713:[0,.60889,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.67937,0,0,.27778],730:[0,.69444,0,0,.66667],732:[0,.67659,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.69444,0,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,0,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,0,0,.66667],928:[0,.69444,0,0,.70834],931:[0,.69444,0,0,.72222],933:[0,.69444,0,0,.77778],934:[0,.69444,0,0,.72222],936:[0,.69444,0,0,.77778],937:[0,.69444,0,0,.72222],8211:[0,.44444,.02778,0,.5],8212:[0,.44444,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5]},"Script-Regular":{32:[0,0,0,0,.25],65:[0,.7,.22925,0,.80253],66:[0,.7,.04087,0,.90757],67:[0,.7,.1689,0,.66619],68:[0,.7,.09371,0,.77443],69:[0,.7,.18583,0,.56162],70:[0,.7,.13634,0,.89544],71:[0,.7,.17322,0,.60961],72:[0,.7,.29694,0,.96919],73:[0,.7,.19189,0,.80907],74:[.27778,.7,.19189,0,1.05159],75:[0,.7,.31259,0,.91364],76:[0,.7,.19189,0,.87373],77:[0,.7,.15981,0,1.08031],78:[0,.7,.3525,0,.9015],79:[0,.7,.08078,0,.73787],80:[0,.7,.08078,0,1.01262],81:[0,.7,.03305,0,.88282],82:[0,.7,.06259,0,.85],83:[0,.7,.19189,0,.86767],84:[0,.7,.29087,0,.74697],85:[0,.7,.25815,0,.79996],86:[0,.7,.27523,0,.62204],87:[0,.7,.27523,0,.80532],88:[0,.7,.26006,0,.94445],89:[0,.7,.2939,0,.70961],90:[0,.7,.24037,0,.8212],160:[0,0,0,0,.25]},"Size1-Regular":{32:[0,0,0,0,.25],40:[.35001,.85,0,0,.45834],41:[.35001,.85,0,0,.45834],47:[.35001,.85,0,0,.57778],91:[.35001,.85,0,0,.41667],92:[.35001,.85,0,0,.57778],93:[.35001,.85,0,0,.41667],123:[.35001,.85,0,0,.58334],125:[.35001,.85,0,0,.58334],160:[0,0,0,0,.25],710:[0,.72222,0,0,.55556],732:[0,.72222,0,0,.55556],770:[0,.72222,0,0,.55556],771:[0,.72222,0,0,.55556],8214:[-99e-5,.601,0,0,.77778],8593:[1e-5,.6,0,0,.66667],8595:[1e-5,.6,0,0,.66667],8657:[1e-5,.6,0,0,.77778],8659:[1e-5,.6,0,0,.77778],8719:[.25001,.75,0,0,.94445],8720:[.25001,.75,0,0,.94445],8721:[.25001,.75,0,0,1.05556],8730:[.35001,.85,0,0,1],8739:[-.00599,.606,0,0,.33333],8741:[-.00599,.606,0,0,.55556],8747:[.30612,.805,.19445,0,.47222],8748:[.306,.805,.19445,0,.47222],8749:[.306,.805,.19445,0,.47222],8750:[.30612,.805,.19445,0,.47222],8896:[.25001,.75,0,0,.83334],8897:[.25001,.75,0,0,.83334],8898:[.25001,.75,0,0,.83334],8899:[.25001,.75,0,0,.83334],8968:[.35001,.85,0,0,.47222],8969:[.35001,.85,0,0,.47222],8970:[.35001,.85,0,0,.47222],8971:[.35001,.85,0,0,.47222],9168:[-99e-5,.601,0,0,.66667],10216:[.35001,.85,0,0,.47222],10217:[.35001,.85,0,0,.47222],10752:[.25001,.75,0,0,1.11111],10753:[.25001,.75,0,0,1.11111],10754:[.25001,.75,0,0,1.11111],10756:[.25001,.75,0,0,.83334],10758:[.25001,.75,0,0,.83334]},"Size2-Regular":{32:[0,0,0,0,.25],40:[.65002,1.15,0,0,.59722],41:[.65002,1.15,0,0,.59722],47:[.65002,1.15,0,0,.81111],91:[.65002,1.15,0,0,.47222],92:[.65002,1.15,0,0,.81111],93:[.65002,1.15,0,0,.47222],123:[.65002,1.15,0,0,.66667],125:[.65002,1.15,0,0,.66667],160:[0,0,0,0,.25],710:[0,.75,0,0,1],732:[0,.75,0,0,1],770:[0,.75,0,0,1],771:[0,.75,0,0,1],8719:[.55001,1.05,0,0,1.27778],8720:[.55001,1.05,0,0,1.27778],8721:[.55001,1.05,0,0,1.44445],8730:[.65002,1.15,0,0,1],8747:[.86225,1.36,.44445,0,.55556],8748:[.862,1.36,.44445,0,.55556],8749:[.862,1.36,.44445,0,.55556],8750:[.86225,1.36,.44445,0,.55556],8896:[.55001,1.05,0,0,1.11111],8897:[.55001,1.05,0,0,1.11111],8898:[.55001,1.05,0,0,1.11111],8899:[.55001,1.05,0,0,1.11111],8968:[.65002,1.15,0,0,.52778],8969:[.65002,1.15,0,0,.52778],8970:[.65002,1.15,0,0,.52778],8971:[.65002,1.15,0,0,.52778],10216:[.65002,1.15,0,0,.61111],10217:[.65002,1.15,0,0,.61111],10752:[.55001,1.05,0,0,1.51112],10753:[.55001,1.05,0,0,1.51112],10754:[.55001,1.05,0,0,1.51112],10756:[.55001,1.05,0,0,1.11111],10758:[.55001,1.05,0,0,1.11111]},"Size3-Regular":{32:[0,0,0,0,.25],40:[.95003,1.45,0,0,.73611],41:[.95003,1.45,0,0,.73611],47:[.95003,1.45,0,0,1.04445],91:[.95003,1.45,0,0,.52778],92:[.95003,1.45,0,0,1.04445],93:[.95003,1.45,0,0,.52778],123:[.95003,1.45,0,0,.75],125:[.95003,1.45,0,0,.75],160:[0,0,0,0,.25],710:[0,.75,0,0,1.44445],732:[0,.75,0,0,1.44445],770:[0,.75,0,0,1.44445],771:[0,.75,0,0,1.44445],8730:[.95003,1.45,0,0,1],8968:[.95003,1.45,0,0,.58334],8969:[.95003,1.45,0,0,.58334],8970:[.95003,1.45,0,0,.58334],8971:[.95003,1.45,0,0,.58334],10216:[.95003,1.45,0,0,.75],10217:[.95003,1.45,0,0,.75]},"Size4-Regular":{32:[0,0,0,0,.25],40:[1.25003,1.75,0,0,.79167],41:[1.25003,1.75,0,0,.79167],47:[1.25003,1.75,0,0,1.27778],91:[1.25003,1.75,0,0,.58334],92:[1.25003,1.75,0,0,1.27778],93:[1.25003,1.75,0,0,.58334],123:[1.25003,1.75,0,0,.80556],125:[1.25003,1.75,0,0,.80556],160:[0,0,0,0,.25],710:[0,.825,0,0,1.8889],732:[0,.825,0,0,1.8889],770:[0,.825,0,0,1.8889],771:[0,.825,0,0,1.8889],8730:[1.25003,1.75,0,0,1],8968:[1.25003,1.75,0,0,.63889],8969:[1.25003,1.75,0,0,.63889],8970:[1.25003,1.75,0,0,.63889],8971:[1.25003,1.75,0,0,.63889],9115:[.64502,1.155,0,0,.875],9116:[1e-5,.6,0,0,.875],9117:[.64502,1.155,0,0,.875],9118:[.64502,1.155,0,0,.875],9119:[1e-5,.6,0,0,.875],9120:[.64502,1.155,0,0,.875],9121:[.64502,1.155,0,0,.66667],9122:[-99e-5,.601,0,0,.66667],9123:[.64502,1.155,0,0,.66667],9124:[.64502,1.155,0,0,.66667],9125:[-99e-5,.601,0,0,.66667],9126:[.64502,1.155,0,0,.66667],9127:[1e-5,.9,0,0,.88889],9128:[.65002,1.15,0,0,.88889],9129:[.90001,0,0,0,.88889],9130:[0,.3,0,0,.88889],9131:[1e-5,.9,0,0,.88889],9132:[.65002,1.15,0,0,.88889],9133:[.90001,0,0,0,.88889],9143:[.88502,.915,0,0,1.05556],10216:[1.25003,1.75,0,0,.80556],10217:[1.25003,1.75,0,0,.80556],57344:[-.00499,.605,0,0,1.05556],57345:[-.00499,.605,0,0,1.05556],57680:[0,.12,0,0,.45],57681:[0,.12,0,0,.45],57682:[0,.12,0,0,.45],57683:[0,.12,0,0,.45]},"Typewriter-Regular":{32:[0,0,0,0,.525],33:[0,.61111,0,0,.525],34:[0,.61111,0,0,.525],35:[0,.61111,0,0,.525],36:[.08333,.69444,0,0,.525],37:[.08333,.69444,0,0,.525],38:[0,.61111,0,0,.525],39:[0,.61111,0,0,.525],40:[.08333,.69444,0,0,.525],41:[.08333,.69444,0,0,.525],42:[0,.52083,0,0,.525],43:[-.08056,.53055,0,0,.525],44:[.13889,.125,0,0,.525],45:[-.08056,.53055,0,0,.525],46:[0,.125,0,0,.525],47:[.08333,.69444,0,0,.525],48:[0,.61111,0,0,.525],49:[0,.61111,0,0,.525],50:[0,.61111,0,0,.525],51:[0,.61111,0,0,.525],52:[0,.61111,0,0,.525],53:[0,.61111,0,0,.525],54:[0,.61111,0,0,.525],55:[0,.61111,0,0,.525],56:[0,.61111,0,0,.525],57:[0,.61111,0,0,.525],58:[0,.43056,0,0,.525],59:[.13889,.43056,0,0,.525],60:[-.05556,.55556,0,0,.525],61:[-.19549,.41562,0,0,.525],62:[-.05556,.55556,0,0,.525],63:[0,.61111,0,0,.525],64:[0,.61111,0,0,.525],65:[0,.61111,0,0,.525],66:[0,.61111,0,0,.525],67:[0,.61111,0,0,.525],68:[0,.61111,0,0,.525],69:[0,.61111,0,0,.525],70:[0,.61111,0,0,.525],71:[0,.61111,0,0,.525],72:[0,.61111,0,0,.525],73:[0,.61111,0,0,.525],74:[0,.61111,0,0,.525],75:[0,.61111,0,0,.525],76:[0,.61111,0,0,.525],77:[0,.61111,0,0,.525],78:[0,.61111,0,0,.525],79:[0,.61111,0,0,.525],80:[0,.61111,0,0,.525],81:[.13889,.61111,0,0,.525],82:[0,.61111,0,0,.525],83:[0,.61111,0,0,.525],84:[0,.61111,0,0,.525],85:[0,.61111,0,0,.525],86:[0,.61111,0,0,.525],87:[0,.61111,0,0,.525],88:[0,.61111,0,0,.525],89:[0,.61111,0,0,.525],90:[0,.61111,0,0,.525],91:[.08333,.69444,0,0,.525],92:[.08333,.69444,0,0,.525],93:[.08333,.69444,0,0,.525],94:[0,.61111,0,0,.525],95:[.09514,0,0,0,.525],96:[0,.61111,0,0,.525],97:[0,.43056,0,0,.525],98:[0,.61111,0,0,.525],99:[0,.43056,0,0,.525],100:[0,.61111,0,0,.525],101:[0,.43056,0,0,.525],102:[0,.61111,0,0,.525],103:[.22222,.43056,0,0,.525],104:[0,.61111,0,0,.525],105:[0,.61111,0,0,.525],106:[.22222,.61111,0,0,.525],107:[0,.61111,0,0,.525],108:[0,.61111,0,0,.525],109:[0,.43056,0,0,.525],110:[0,.43056,0,0,.525],111:[0,.43056,0,0,.525],112:[.22222,.43056,0,0,.525],113:[.22222,.43056,0,0,.525],114:[0,.43056,0,0,.525],115:[0,.43056,0,0,.525],116:[0,.55358,0,0,.525],117:[0,.43056,0,0,.525],118:[0,.43056,0,0,.525],119:[0,.43056,0,0,.525],120:[0,.43056,0,0,.525],121:[.22222,.43056,0,0,.525],122:[0,.43056,0,0,.525],123:[.08333,.69444,0,0,.525],124:[.08333,.69444,0,0,.525],125:[.08333,.69444,0,0,.525],126:[0,.61111,0,0,.525],127:[0,.61111,0,0,.525],160:[0,0,0,0,.525],176:[0,.61111,0,0,.525],184:[.19445,0,0,0,.525],305:[0,.43056,0,0,.525],567:[.22222,.43056,0,0,.525],711:[0,.56597,0,0,.525],713:[0,.56555,0,0,.525],714:[0,.61111,0,0,.525],715:[0,.61111,0,0,.525],728:[0,.61111,0,0,.525],730:[0,.61111,0,0,.525],770:[0,.61111,0,0,.525],771:[0,.61111,0,0,.525],776:[0,.61111,0,0,.525],915:[0,.61111,0,0,.525],916:[0,.61111,0,0,.525],920:[0,.61111,0,0,.525],923:[0,.61111,0,0,.525],926:[0,.61111,0,0,.525],928:[0,.61111,0,0,.525],931:[0,.61111,0,0,.525],933:[0,.61111,0,0,.525],934:[0,.61111,0,0,.525],936:[0,.61111,0,0,.525],937:[0,.61111,0,0,.525],8216:[0,.61111,0,0,.525],8217:[0,.61111,0,0,.525],8242:[0,.61111,0,0,.525],9251:[.11111,.21944,0,0,.525]}},Jb={slant:[.25,.25,.25],space:[0,0,0],stretch:[0,0,0],shrink:[0,0,0],xHeight:[.431,.431,.431],quad:[1,1.171,1.472],extraSpace:[0,0,0],num1:[.677,.732,.925],num2:[.394,.384,.387],num3:[.444,.471,.504],denom1:[.686,.752,1.025],denom2:[.345,.344,.532],sup1:[.413,.503,.504],sup2:[.363,.431,.404],sup3:[.289,.286,.294],sub1:[.15,.143,.2],sub2:[.247,.286,.4],supDrop:[.386,.353,.494],subDrop:[.05,.071,.1],delim1:[2.39,1.7,1.98],delim2:[1.01,1.157,1.42],axisHeight:[.25,.25,.25],defaultRuleThickness:[.04,.049,.049],bigOpSpacing1:[.111,.111,.111],bigOpSpacing2:[.166,.166,.166],bigOpSpacing3:[.2,.2,.2],bigOpSpacing4:[.6,.611,.611],bigOpSpacing5:[.1,.143,.143],sqrtRuleThickness:[.04,.04,.04],ptPerEm:[10,10,10],doubleRuleSep:[.2,.2,.2],arrayRuleWidth:[.04,.04,.04],fboxsep:[.3,.3,.3],fboxrule:[.04,.04,.04]},Zz={\u00C5:"A",\u00D0:"D",\u00DE:"o",\u00E5:"a",\u00F0:"d",\u00FE:"o",\u0410:"A",\u0411:"B",\u0412:"B",\u0413:"F",\u0414:"A",\u0415:"E",\u0416:"K",\u0417:"3",\u0418:"N",\u0419:"N",\u041A:"K",\u041B:"N",\u041C:"M",\u041D:"H",\u041E:"O",\u041F:"N",\u0420:"P",\u0421:"C",\u0422:"T",\u0423:"y",\u0424:"O",\u0425:"X",\u0426:"U",\u0427:"h",\u0428:"W",\u0429:"W",\u042A:"B",\u042B:"X",\u042C:"B",\u042D:"3",\u042E:"X",\u042F:"R",\u0430:"a",\u0431:"b",\u0432:"a",\u0433:"r",\u0434:"y",\u0435:"e",\u0436:"m",\u0437:"e",\u0438:"n",\u0439:"n",\u043A:"n",\u043B:"n",\u043C:"m",\u043D:"n",\u043E:"o",\u043F:"n",\u0440:"p",\u0441:"c",\u0442:"o",\u0443:"y",\u0444:"b",\u0445:"x",\u0446:"n",\u0447:"n",\u0448:"w",\u0449:"w",\u044A:"a",\u044B:"m",\u044C:"a",\u044D:"e",\u044E:"m",\u044F:"r"};o(axe,"setFontMetrics");o(M7,"getCharacterMetrics");l7={};o(sxe,"getGlobalMetrics");oxe=[[1,1,1],[2,1,1],[3,1,1],[4,2,1],[5,2,1],[6,3,1],[7,4,2],[8,6,3],[9,7,6],[10,8,7],[11,10,9]],Jz=[.5,.6,.7,.8,.9,1,1.2,1.44,1.728,2.074,2.488],eG=o(function(e,r){return r.size<2?e:oxe[e-1][r.size-1]},"sizeAtStyle"),d4=class t{static{o(this,"Options")}constructor(e){this.style=void 0,this.color=void 0,this.size=void 0,this.textSize=void 0,this.phantom=void 0,this.font=void 0,this.fontFamily=void 0,this.fontWeight=void 0,this.fontShape=void 0,this.sizeMultiplier=void 0,this.maxSize=void 0,this.minRuleThickness=void 0,this._fontMetrics=void 0,this.style=e.style,this.color=e.color,this.size=e.size||t.BASESIZE,this.textSize=e.textSize||this.size,this.phantom=!!e.phantom,this.font=e.font||"",this.fontFamily=e.fontFamily||"",this.fontWeight=e.fontWeight||"",this.fontShape=e.fontShape||"",this.sizeMultiplier=Jz[this.size-1],this.maxSize=e.maxSize,this.minRuleThickness=e.minRuleThickness,this._fontMetrics=void 0}extend(e){var r={style:this.style,size:this.size,textSize:this.textSize,color:this.color,phantom:this.phantom,font:this.font,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontShape:this.fontShape,maxSize:this.maxSize,minRuleThickness:this.minRuleThickness};for(var n in e)e.hasOwnProperty(n)&&(r[n]=e[n]);return new t(r)}havingStyle(e){return this.style===e?this:this.extend({style:e,size:eG(this.textSize,e)})}havingCrampedStyle(){return this.havingStyle(this.style.cramp())}havingSize(e){return this.size===e&&this.textSize===e?this:this.extend({style:this.style.text(),size:e,textSize:e,sizeMultiplier:Jz[e-1]})}havingBaseStyle(e){e=e||this.style.text();var r=eG(t.BASESIZE,e);return this.size===r&&this.textSize===t.BASESIZE&&this.style===e?this:this.extend({style:e,size:r})}havingBaseSizing(){var e;switch(this.style.id){case 4:case 5:e=3;break;case 6:case 7:e=1;break;default:e=6}return this.extend({style:this.style.text(),size:e})}withColor(e){return this.extend({color:e})}withPhantom(){return this.extend({phantom:!0})}withFont(e){return this.extend({font:e})}withTextFontFamily(e){return this.extend({fontFamily:e,font:""})}withTextFontWeight(e){return this.extend({fontWeight:e,font:""})}withTextFontShape(e){return this.extend({fontShape:e,font:""})}sizingClasses(e){return e.size!==this.size?["sizing","reset-size"+e.size,"size"+this.size]:[]}baseSizingClasses(){return this.size!==t.BASESIZE?["sizing","reset-size"+this.size,"size"+t.BASESIZE]:[]}fontMetrics(){return this._fontMetrics||(this._fontMetrics=sxe(this.size)),this._fontMetrics}getColor(){return this.phantom?"transparent":this.color}};d4.BASESIZE=6;w7={pt:1,mm:7227/2540,cm:7227/254,in:72.27,bp:803/800,pc:12,dd:1238/1157,cc:14856/1157,nd:685/642,nc:1370/107,sp:1/65536,px:803/800},lxe={ex:!0,em:!0,mu:!0},DG=o(function(e){return typeof e!="string"&&(e=e.unit),e in w7||e in lxe||e==="ex"},"validUnit"),Hn=o(function(e,r){var n;if(e.unit in w7)n=w7[e.unit]/r.fontMetrics().ptPerEm/r.sizeMultiplier;else if(e.unit==="mu")n=r.fontMetrics().cssEmPerMu;else{var i;if(r.style.isTight()?i=r.havingStyle(r.style.text()):i=r,e.unit==="ex")n=i.fontMetrics().xHeight;else if(e.unit==="em")n=i.fontMetrics().quad;else throw new nt("Invalid unit: '"+e.unit+"'");i!==r&&(n*=i.sizeMultiplier/r.sizeMultiplier)}return Math.min(e.number*n,r.maxSize)},"calculateSize"),ct=o(function(e){return+e.toFixed(4)+"em"},"makeEm"),dh=o(function(e){return e.filter(r=>r).join(" ")},"createClass"),RG=o(function(e,r,n){if(this.classes=e||[],this.attributes={},this.height=0,this.depth=0,this.maxFontSize=0,this.style=n||{},r){r.style.isTight()&&this.classes.push("mtight");var i=r.getColor();i&&(this.style.color=i)}},"initNode"),NG=o(function(e){var r=document.createElement(e);r.className=dh(this.classes);for(var n in this.style)this.style.hasOwnProperty(n)&&(r.style[n]=this.style[n]);for(var i in this.attributes)this.attributes.hasOwnProperty(i)&&r.setAttribute(i,this.attributes[i]);for(var a=0;a",r},"toMarkup"),jf=class{static{o(this,"Span")}constructor(e,r,n,i){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.width=void 0,this.maxFontSize=void 0,this.style=void 0,RG.call(this,e,n,i),this.children=r||[]}setAttribute(e,r){this.attributes[e]=r}hasClass(e){return Vt.contains(this.classes,e)}toNode(){return NG.call(this,"span")}toMarkup(){return MG.call(this,"span")}},iy=class{static{o(this,"Anchor")}constructor(e,r,n,i){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,RG.call(this,r,i),this.children=n||[],this.setAttribute("href",e)}setAttribute(e,r){this.attributes[e]=r}hasClass(e){return Vt.contains(this.classes,e)}toNode(){return NG.call(this,"a")}toMarkup(){return MG.call(this,"a")}},T7=class{static{o(this,"Img")}constructor(e,r,n){this.src=void 0,this.alt=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.alt=r,this.src=e,this.classes=["mord"],this.style=n}hasClass(e){return Vt.contains(this.classes,e)}toNode(){var e=document.createElement("img");e.src=this.src,e.alt=this.alt,e.className="mord";for(var r in this.style)this.style.hasOwnProperty(r)&&(e.style[r]=this.style[r]);return e}toMarkup(){var e=''+Vt.escape(this.alt)+'0&&(r=document.createElement("span"),r.style.marginRight=ct(this.italic)),this.classes.length>0&&(r=r||document.createElement("span"),r.className=dh(this.classes));for(var n in this.style)this.style.hasOwnProperty(n)&&(r=r||document.createElement("span"),r.style[n]=this.style[n]);return r?(r.appendChild(e),r):e}toMarkup(){var e=!1,r="0&&(n+="margin-right:"+this.italic+"em;");for(var i in this.style)this.style.hasOwnProperty(i)&&(n+=Vt.hyphenate(i)+":"+this.style[i]+";");n&&(e=!0,r+=' style="'+Vt.escape(n)+'"');var a=Vt.escape(this.text);return e?(r+=">",r+=a,r+="",r):a}},ll=class{static{o(this,"SvgNode")}constructor(e,r){this.children=void 0,this.attributes=void 0,this.children=e||[],this.attributes=r||{}}toNode(){var e="http://www.w3.org/2000/svg",r=document.createElementNS(e,"svg");for(var n in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,n)&&r.setAttribute(n,this.attributes[n]);for(var i=0;i':''}},ay=class{static{o(this,"LineNode")}constructor(e){this.attributes=void 0,this.attributes=e||{}}toNode(){var e="http://www.w3.org/2000/svg",r=document.createElementNS(e,"line");for(var n in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,n)&&r.setAttribute(n,this.attributes[n]);return r}toMarkup(){var e="","\\gt",!0);G(U,ee,xe,"\u2208","\\in",!0);G(U,ee,xe,"\uE020","\\@not");G(U,ee,xe,"\u2282","\\subset",!0);G(U,ee,xe,"\u2283","\\supset",!0);G(U,ee,xe,"\u2286","\\subseteq",!0);G(U,ee,xe,"\u2287","\\supseteq",!0);G(U,ve,xe,"\u2288","\\nsubseteq",!0);G(U,ve,xe,"\u2289","\\nsupseteq",!0);G(U,ee,xe,"\u22A8","\\models");G(U,ee,xe,"\u2190","\\leftarrow",!0);G(U,ee,xe,"\u2264","\\le");G(U,ee,xe,"\u2264","\\leq",!0);G(U,ee,xe,"<","\\lt",!0);G(U,ee,xe,"\u2192","\\rightarrow",!0);G(U,ee,xe,"\u2192","\\to");G(U,ve,xe,"\u2271","\\ngeq",!0);G(U,ve,xe,"\u2270","\\nleq",!0);G(U,ee,lu,"\xA0","\\ ");G(U,ee,lu,"\xA0","\\space");G(U,ee,lu,"\xA0","\\nobreakspace");G(Qe,ee,lu,"\xA0","\\ ");G(Qe,ee,lu,"\xA0"," ");G(Qe,ee,lu,"\xA0","\\space");G(Qe,ee,lu,"\xA0","\\nobreakspace");G(U,ee,lu,null,"\\nobreak");G(U,ee,lu,null,"\\allowbreak");G(U,ee,b4,",",",");G(U,ee,b4,";",";");G(U,ve,bt,"\u22BC","\\barwedge",!0);G(U,ve,bt,"\u22BB","\\veebar",!0);G(U,ee,bt,"\u2299","\\odot",!0);G(U,ee,bt,"\u2295","\\oplus",!0);G(U,ee,bt,"\u2297","\\otimes",!0);G(U,ee,Le,"\u2202","\\partial",!0);G(U,ee,bt,"\u2298","\\oslash",!0);G(U,ve,bt,"\u229A","\\circledcirc",!0);G(U,ve,bt,"\u22A1","\\boxdot",!0);G(U,ee,bt,"\u25B3","\\bigtriangleup");G(U,ee,bt,"\u25BD","\\bigtriangledown");G(U,ee,bt,"\u2020","\\dagger");G(U,ee,bt,"\u22C4","\\diamond");G(U,ee,bt,"\u22C6","\\star");G(U,ee,bt,"\u25C3","\\triangleleft");G(U,ee,bt,"\u25B9","\\triangleright");G(U,ee,js,"{","\\{");G(Qe,ee,Le,"{","\\{");G(Qe,ee,Le,"{","\\textbraceleft");G(U,ee,Xa,"}","\\}");G(Qe,ee,Le,"}","\\}");G(Qe,ee,Le,"}","\\textbraceright");G(U,ee,js,"{","\\lbrace");G(U,ee,Xa,"}","\\rbrace");G(U,ee,js,"[","\\lbrack",!0);G(Qe,ee,Le,"[","\\lbrack",!0);G(U,ee,Xa,"]","\\rbrack",!0);G(Qe,ee,Le,"]","\\rbrack",!0);G(U,ee,js,"(","\\lparen",!0);G(U,ee,Xa,")","\\rparen",!0);G(Qe,ee,Le,"<","\\textless",!0);G(Qe,ee,Le,">","\\textgreater",!0);G(U,ee,js,"\u230A","\\lfloor",!0);G(U,ee,Xa,"\u230B","\\rfloor",!0);G(U,ee,js,"\u2308","\\lceil",!0);G(U,ee,Xa,"\u2309","\\rceil",!0);G(U,ee,Le,"\\","\\backslash");G(U,ee,Le,"\u2223","|");G(U,ee,Le,"\u2223","\\vert");G(Qe,ee,Le,"|","\\textbar",!0);G(U,ee,Le,"\u2225","\\|");G(U,ee,Le,"\u2225","\\Vert");G(Qe,ee,Le,"\u2225","\\textbardbl");G(Qe,ee,Le,"~","\\textasciitilde");G(Qe,ee,Le,"\\","\\textbackslash");G(Qe,ee,Le,"^","\\textasciicircum");G(U,ee,xe,"\u2191","\\uparrow",!0);G(U,ee,xe,"\u21D1","\\Uparrow",!0);G(U,ee,xe,"\u2193","\\downarrow",!0);G(U,ee,xe,"\u21D3","\\Downarrow",!0);G(U,ee,xe,"\u2195","\\updownarrow",!0);G(U,ee,xe,"\u21D5","\\Updownarrow",!0);G(U,ee,xi,"\u2210","\\coprod");G(U,ee,xi,"\u22C1","\\bigvee");G(U,ee,xi,"\u22C0","\\bigwedge");G(U,ee,xi,"\u2A04","\\biguplus");G(U,ee,xi,"\u22C2","\\bigcap");G(U,ee,xi,"\u22C3","\\bigcup");G(U,ee,xi,"\u222B","\\int");G(U,ee,xi,"\u222B","\\intop");G(U,ee,xi,"\u222C","\\iint");G(U,ee,xi,"\u222D","\\iiint");G(U,ee,xi,"\u220F","\\prod");G(U,ee,xi,"\u2211","\\sum");G(U,ee,xi,"\u2A02","\\bigotimes");G(U,ee,xi,"\u2A01","\\bigoplus");G(U,ee,xi,"\u2A00","\\bigodot");G(U,ee,xi,"\u222E","\\oint");G(U,ee,xi,"\u222F","\\oiint");G(U,ee,xi,"\u2230","\\oiiint");G(U,ee,xi,"\u2A06","\\bigsqcup");G(U,ee,xi,"\u222B","\\smallint");G(Qe,ee,vp,"\u2026","\\textellipsis");G(U,ee,vp,"\u2026","\\mathellipsis");G(Qe,ee,vp,"\u2026","\\ldots",!0);G(U,ee,vp,"\u2026","\\ldots",!0);G(U,ee,vp,"\u22EF","\\@cdots",!0);G(U,ee,vp,"\u22F1","\\ddots",!0);G(U,ee,Le,"\u22EE","\\varvdots");G(U,ee,Pn,"\u02CA","\\acute");G(U,ee,Pn,"\u02CB","\\grave");G(U,ee,Pn,"\xA8","\\ddot");G(U,ee,Pn,"~","\\tilde");G(U,ee,Pn,"\u02C9","\\bar");G(U,ee,Pn,"\u02D8","\\breve");G(U,ee,Pn,"\u02C7","\\check");G(U,ee,Pn,"^","\\hat");G(U,ee,Pn,"\u20D7","\\vec");G(U,ee,Pn,"\u02D9","\\dot");G(U,ee,Pn,"\u02DA","\\mathring");G(U,ee,Ut,"\uE131","\\@imath");G(U,ee,Ut,"\uE237","\\@jmath");G(U,ee,Le,"\u0131","\u0131");G(U,ee,Le,"\u0237","\u0237");G(Qe,ee,Le,"\u0131","\\i",!0);G(Qe,ee,Le,"\u0237","\\j",!0);G(Qe,ee,Le,"\xDF","\\ss",!0);G(Qe,ee,Le,"\xE6","\\ae",!0);G(Qe,ee,Le,"\u0153","\\oe",!0);G(Qe,ee,Le,"\xF8","\\o",!0);G(Qe,ee,Le,"\xC6","\\AE",!0);G(Qe,ee,Le,"\u0152","\\OE",!0);G(Qe,ee,Le,"\xD8","\\O",!0);G(Qe,ee,Pn,"\u02CA","\\'");G(Qe,ee,Pn,"\u02CB","\\`");G(Qe,ee,Pn,"\u02C6","\\^");G(Qe,ee,Pn,"\u02DC","\\~");G(Qe,ee,Pn,"\u02C9","\\=");G(Qe,ee,Pn,"\u02D8","\\u");G(Qe,ee,Pn,"\u02D9","\\.");G(Qe,ee,Pn,"\xB8","\\c");G(Qe,ee,Pn,"\u02DA","\\r");G(Qe,ee,Pn,"\u02C7","\\v");G(Qe,ee,Pn,"\xA8",'\\"');G(Qe,ee,Pn,"\u02DD","\\H");G(Qe,ee,Pn,"\u25EF","\\textcircled");IG={"--":!0,"---":!0,"``":!0,"''":!0};G(Qe,ee,Le,"\u2013","--",!0);G(Qe,ee,Le,"\u2013","\\textendash");G(Qe,ee,Le,"\u2014","---",!0);G(Qe,ee,Le,"\u2014","\\textemdash");G(Qe,ee,Le,"\u2018","`",!0);G(Qe,ee,Le,"\u2018","\\textquoteleft");G(Qe,ee,Le,"\u2019","'",!0);G(Qe,ee,Le,"\u2019","\\textquoteright");G(Qe,ee,Le,"\u201C","``",!0);G(Qe,ee,Le,"\u201C","\\textquotedblleft");G(Qe,ee,Le,"\u201D","''",!0);G(Qe,ee,Le,"\u201D","\\textquotedblright");G(U,ee,Le,"\xB0","\\degree",!0);G(Qe,ee,Le,"\xB0","\\degree");G(Qe,ee,Le,"\xB0","\\textdegree",!0);G(U,ee,Le,"\xA3","\\pounds");G(U,ee,Le,"\xA3","\\mathsterling",!0);G(Qe,ee,Le,"\xA3","\\pounds");G(Qe,ee,Le,"\xA3","\\textsterling",!0);G(U,ve,Le,"\u2720","\\maltese");G(Qe,ve,Le,"\u2720","\\maltese");rG='0123456789/@."';for(e4=0;e40)return ol(a,h,i,r,s.concat(f));if(u){var d,p;if(u==="boldsymbol"){var m=mxe(a,i,r,s,n);d=m.fontName,p=[m.fontClass]}else l?(d=BG[u].fontName,p=[u]):(d=a4(u,r.fontWeight,r.fontShape),p=[u,r.fontWeight,r.fontShape]);if(w4(a,d,i).metrics)return ol(a,d,i,r,s.concat(p));if(IG.hasOwnProperty(a)&&d.slice(0,10)==="Typewriter"){for(var g=[],y=0;y{if(dh(t.classes)!==dh(e.classes)||t.skew!==e.skew||t.maxFontSize!==e.maxFontSize)return!1;if(t.classes.length===1){var r=t.classes[0];if(r==="mbin"||r==="mord")return!1}for(var n in t.style)if(t.style.hasOwnProperty(n)&&t.style[n]!==e.style[n])return!1;for(var i in e.style)if(e.style.hasOwnProperty(i)&&t.style[i]!==e.style[i])return!1;return!0},"canCombine"),vxe=o(t=>{for(var e=0;er&&(r=s.height),s.depth>n&&(n=s.depth),s.maxFontSize>i&&(i=s.maxFontSize)}e.height=r,e.depth=n,e.maxFontSize=i},"sizeElementFromChildren"),ds=o(function(e,r,n,i){var a=new jf(e,r,n,i);return I7(a),a},"makeSpan"),OG=o((t,e,r,n)=>new jf(t,e,r,n),"makeSvgSpan"),xxe=o(function(e,r,n){var i=ds([e],[],r);return i.height=Math.max(n||r.fontMetrics().defaultRuleThickness,r.minRuleThickness),i.style.borderBottomWidth=ct(i.height),i.maxFontSize=1,i},"makeLineSpan"),bxe=o(function(e,r,n,i){var a=new iy(e,r,n,i);return I7(a),a},"makeAnchor"),PG=o(function(e){var r=new Xf(e);return I7(r),r},"makeFragment"),wxe=o(function(e,r){return e instanceof Xf?ds([],[e],r):e},"wrapFragment"),Txe=o(function(e){if(e.positionType==="individualShift"){for(var r=e.children,n=[r[0]],i=-r[0].shift-r[0].elem.depth,a=i,s=1;s{var r=ds(["mspace"],[],e),n=Hn(t,e);return r.style.marginRight=ct(n),r},"makeGlue"),a4=o(function(e,r,n){var i="";switch(e){case"amsrm":i="AMS";break;case"textrm":i="Main";break;case"textsf":i="SansSerif";break;case"texttt":i="Typewriter";break;default:i=e}var a;return r==="textbf"&&n==="textit"?a="BoldItalic":r==="textbf"?a="Bold":r==="textit"?a="Italic":a="Regular",i+"-"+a},"retrieveTextFontName"),BG={mathbf:{variant:"bold",fontName:"Main-Bold"},mathrm:{variant:"normal",fontName:"Main-Regular"},textit:{variant:"italic",fontName:"Main-Italic"},mathit:{variant:"italic",fontName:"Main-Italic"},mathnormal:{variant:"italic",fontName:"Math-Italic"},mathbb:{variant:"double-struck",fontName:"AMS-Regular"},mathcal:{variant:"script",fontName:"Caligraphic-Regular"},mathfrak:{variant:"fraktur",fontName:"Fraktur-Regular"},mathscr:{variant:"script",fontName:"Script-Regular"},mathsf:{variant:"sans-serif",fontName:"SansSerif-Regular"},mathtt:{variant:"monospace",fontName:"Typewriter-Regular"}},FG={vec:["vec",.471,.714],oiintSize1:["oiintSize1",.957,.499],oiintSize2:["oiintSize2",1.472,.659],oiiintSize1:["oiiintSize1",1.304,.499],oiiintSize2:["oiiintSize2",1.98,.659]},Cxe=o(function(e,r){var[n,i,a]=FG[e],s=new Jl(n),l=new ll([s],{width:ct(i),height:ct(a),style:"width:"+ct(i),viewBox:"0 0 "+1e3*i+" "+1e3*a,preserveAspectRatio:"xMinYMin"}),u=OG(["overlay"],[l],r);return u.height=a,u.style.height=ct(a),u.style.width=ct(i),u},"staticSvg"),Be={fontMap:BG,makeSymbol:ol,mathsym:pxe,makeSpan:ds,makeSvgSpan:OG,makeLineSpan:xxe,makeAnchor:bxe,makeFragment:PG,wrapFragment:wxe,makeVList:kxe,makeOrd:gxe,makeGlue:Exe,staticSvg:Cxe,svgData:FG,tryCombineChars:vxe},Un={number:3,unit:"mu"},Wf={number:4,unit:"mu"},nu={number:5,unit:"mu"},Sxe={mord:{mop:Un,mbin:Wf,mrel:nu,minner:Un},mop:{mord:Un,mop:Un,mrel:nu,minner:Un},mbin:{mord:Wf,mop:Wf,mopen:Wf,minner:Wf},mrel:{mord:nu,mop:nu,mopen:nu,minner:nu},mopen:{},mclose:{mop:Un,mbin:Wf,mrel:nu,minner:Un},mpunct:{mord:Un,mop:Un,mrel:nu,mopen:Un,mclose:Un,mpunct:Un,minner:Un},minner:{mord:Un,mop:Un,mbin:Wf,mrel:nu,mopen:Un,mpunct:Un,minner:Un}},Axe={mord:{mop:Un},mop:{mord:Un,mop:Un},mbin:{},mrel:{},mopen:{},mclose:{mop:Un},mpunct:{},minner:{mop:Un}},zG={},m4={},g4={};o(vt,"defineFunction");o(Kf,"defineFunctionBuilders");y4=o(function(e){return e.type==="ordgroup"&&e.body.length===1?e.body[0]:e},"normalizeArgument"),ui=o(function(e){return e.type==="ordgroup"?e.body:[e]},"ordargument"),su=Be.makeSpan,_xe=["leftmost","mbin","mopen","mrel","mop","mpunct"],Lxe=["rightmost","mrel","mclose","mpunct"],Dxe={display:Ht.DISPLAY,text:Ht.TEXT,script:Ht.SCRIPT,scriptscript:Ht.SCRIPTSCRIPT},Rxe={mord:"mord",mop:"mop",mbin:"mbin",mrel:"mrel",mopen:"mopen",mclose:"mclose",mpunct:"mpunct",minner:"minner"},Ri=o(function(e,r,n,i){i===void 0&&(i=[null,null]);for(var a=[],s=0;s{var v=y.classes[0],x=g.classes[0];v==="mbin"&&Vt.contains(Lxe,x)?y.classes[0]="mord":x==="mbin"&&Vt.contains(_xe,v)&&(g.classes[0]="mord")},{node:d},p,m),aG(a,(g,y)=>{var v=E7(y),x=E7(g),b=v&&x?g.hasClass("mtight")?Axe[v][x]:Sxe[v][x]:null;if(b)return Be.makeGlue(b,h)},{node:d},p,m),a},"buildExpression"),aG=o(function t(e,r,n,i,a){i&&e.push(i);for(var s=0;sp=>{e.splice(d+1,0,p),s++})(s)}i&&e.pop()},"traverseNonSpaceNodes"),GG=o(function(e){return e instanceof Xf||e instanceof iy||e instanceof jf&&e.hasClass("enclosing")?e:null},"checkPartialGroup"),Nxe=o(function t(e,r){var n=GG(e);if(n){var i=n.children;if(i.length){if(r==="right")return t(i[i.length-1],"right");if(r==="left")return t(i[0],"left")}}return e},"getOutermostNode"),E7=o(function(e,r){return e?(r&&(e=Nxe(e,r)),Rxe[e.classes[0]]||null):null},"getTypeOfDomTree"),sy=o(function(e,r){var n=["nulldelimiter"].concat(e.baseSizingClasses());return su(r.concat(n))},"makeNullDelimiter"),Cr=o(function(e,r,n){if(!e)return su();if(m4[e.type]){var i=m4[e.type](e,r);if(n&&r.size!==n.size){i=su(r.sizingClasses(n),[i],r);var a=r.sizeMultiplier/n.sizeMultiplier;i.height*=a,i.depth*=a}return i}else throw new nt("Got group of unknown type: '"+e.type+"'")},"buildGroup");o(s4,"buildHTMLUnbreakable");o(C7,"buildHTML");o($G,"newDocumentFragment");ps=class{static{o(this,"MathNode")}constructor(e,r,n){this.type=void 0,this.attributes=void 0,this.children=void 0,this.classes=void 0,this.type=e,this.attributes={},this.children=r||[],this.classes=n||[]}setAttribute(e,r){this.attributes[e]=r}getAttribute(e){return this.attributes[e]}toNode(){var e=document.createElementNS("http://www.w3.org/1998/Math/MathML",this.type);for(var r in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,r)&&e.setAttribute(r,this.attributes[r]);this.classes.length>0&&(e.className=dh(this.classes));for(var n=0;n0&&(e+=' class ="'+Vt.escape(dh(this.classes))+'"'),e+=">";for(var n=0;n",e}toText(){return this.children.map(e=>e.toText()).join("")}},qf=class{static{o(this,"TextNode")}constructor(e){this.text=void 0,this.text=e}toNode(){return document.createTextNode(this.text)}toMarkup(){return Vt.escape(this.toText())}toText(){return this.text}},S7=class{static{o(this,"SpaceNode")}constructor(e){this.width=void 0,this.character=void 0,this.width=e,e>=.05555&&e<=.05556?this.character="\u200A":e>=.1666&&e<=.1667?this.character="\u2009":e>=.2222&&e<=.2223?this.character="\u2005":e>=.2777&&e<=.2778?this.character="\u2005\u200A":e>=-.05556&&e<=-.05555?this.character="\u200A\u2063":e>=-.1667&&e<=-.1666?this.character="\u2009\u2063":e>=-.2223&&e<=-.2222?this.character="\u205F\u2063":e>=-.2778&&e<=-.2777?this.character="\u2005\u2063":this.character=null}toNode(){if(this.character)return document.createTextNode(this.character);var e=document.createElementNS("http://www.w3.org/1998/Math/MathML","mspace");return e.setAttribute("width",ct(this.width)),e}toMarkup(){return this.character?""+this.character+"":''}toText(){return this.character?this.character:" "}},et={MathNode:ps,TextNode:qf,SpaceNode:S7,newDocumentFragment:$G},_o=o(function(e,r,n){return wn[r][e]&&wn[r][e].replace&&e.charCodeAt(0)!==55349&&!(IG.hasOwnProperty(e)&&n&&(n.fontFamily&&n.fontFamily.slice(4,6)==="tt"||n.font&&n.font.slice(4,6)==="tt"))&&(e=wn[r][e].replace),new et.TextNode(e)},"makeText"),O7=o(function(e){return e.length===1?e[0]:new et.MathNode("mrow",e)},"makeRow"),P7=o(function(e,r){if(r.fontFamily==="texttt")return"monospace";if(r.fontFamily==="textsf")return r.fontShape==="textit"&&r.fontWeight==="textbf"?"sans-serif-bold-italic":r.fontShape==="textit"?"sans-serif-italic":r.fontWeight==="textbf"?"bold-sans-serif":"sans-serif";if(r.fontShape==="textit"&&r.fontWeight==="textbf")return"bold-italic";if(r.fontShape==="textit")return"italic";if(r.fontWeight==="textbf")return"bold";var n=r.font;if(!n||n==="mathnormal")return null;var i=e.mode;if(n==="mathit")return"italic";if(n==="boldsymbol")return e.type==="textord"?"bold":"bold-italic";if(n==="mathbf")return"bold";if(n==="mathbb")return"double-struck";if(n==="mathfrak")return"fraktur";if(n==="mathscr"||n==="mathcal")return"script";if(n==="mathsf")return"sans-serif";if(n==="mathtt")return"monospace";var a=e.text;if(Vt.contains(["\\imath","\\jmath"],a))return null;wn[i][a]&&wn[i][a].replace&&(a=wn[i][a].replace);var s=Be.fontMap[n].fontName;return M7(a,s,i)?Be.fontMap[n].variant:null},"getVariant"),gs=o(function(e,r,n){if(e.length===1){var i=fn(e[0],r);return n&&i instanceof ps&&i.type==="mo"&&(i.setAttribute("lspace","0em"),i.setAttribute("rspace","0em")),[i]}for(var a=[],s,l=0;l0&&(d.text=d.text.slice(0,1)+"\u0338"+d.text.slice(1),a.pop())}}}a.push(u),s=u}return a},"buildExpression"),ph=o(function(e,r,n){return O7(gs(e,r,n))},"buildExpressionRow"),fn=o(function(e,r){if(!e)return new et.MathNode("mrow");if(g4[e.type]){var n=g4[e.type](e,r);return n}else throw new nt("Got group of unknown type: '"+e.type+"'")},"buildGroup");o(sG,"buildMathML");VG=o(function(e){return new d4({style:e.displayMode?Ht.DISPLAY:Ht.TEXT,maxSize:e.maxSize,minRuleThickness:e.minRuleThickness})},"optionsFromSettings"),UG=o(function(e,r){if(r.displayMode){var n=["katex-display"];r.leqno&&n.push("leqno"),r.fleqn&&n.push("fleqn"),e=Be.makeSpan(n,[e])}return e},"displayWrap"),Mxe=o(function(e,r,n){var i=VG(n),a;if(n.output==="mathml")return sG(e,r,i,n.displayMode,!0);if(n.output==="html"){var s=C7(e,i);a=Be.makeSpan(["katex"],[s])}else{var l=sG(e,r,i,n.displayMode,!1),u=C7(e,i);a=Be.makeSpan(["katex"],[l,u])}return UG(a,n)},"buildTree"),Ixe=o(function(e,r,n){var i=VG(n),a=C7(e,i),s=Be.makeSpan(["katex"],[a]);return UG(s,n)},"buildHTMLTree"),Oxe={widehat:"^",widecheck:"\u02C7",widetilde:"~",utilde:"~",overleftarrow:"\u2190",underleftarrow:"\u2190",xleftarrow:"\u2190",overrightarrow:"\u2192",underrightarrow:"\u2192",xrightarrow:"\u2192",underbrace:"\u23DF",overbrace:"\u23DE",overgroup:"\u23E0",undergroup:"\u23E1",overleftrightarrow:"\u2194",underleftrightarrow:"\u2194",xleftrightarrow:"\u2194",Overrightarrow:"\u21D2",xRightarrow:"\u21D2",overleftharpoon:"\u21BC",xleftharpoonup:"\u21BC",overrightharpoon:"\u21C0",xrightharpoonup:"\u21C0",xLeftarrow:"\u21D0",xLeftrightarrow:"\u21D4",xhookleftarrow:"\u21A9",xhookrightarrow:"\u21AA",xmapsto:"\u21A6",xrightharpoondown:"\u21C1",xleftharpoondown:"\u21BD",xrightleftharpoons:"\u21CC",xleftrightharpoons:"\u21CB",xtwoheadleftarrow:"\u219E",xtwoheadrightarrow:"\u21A0",xlongequal:"=",xtofrom:"\u21C4",xrightleftarrows:"\u21C4",xrightequilibrium:"\u21CC",xleftequilibrium:"\u21CB","\\cdrightarrow":"\u2192","\\cdleftarrow":"\u2190","\\cdlongequal":"="},Pxe=o(function(e){var r=new et.MathNode("mo",[new et.TextNode(Oxe[e.replace(/^\\/,"")])]);return r.setAttribute("stretchy","true"),r},"mathMLnode"),Bxe={overrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],overleftarrow:[["leftarrow"],.888,522,"xMinYMin"],underrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],underleftarrow:[["leftarrow"],.888,522,"xMinYMin"],xrightarrow:[["rightarrow"],1.469,522,"xMaxYMin"],"\\cdrightarrow":[["rightarrow"],3,522,"xMaxYMin"],xleftarrow:[["leftarrow"],1.469,522,"xMinYMin"],"\\cdleftarrow":[["leftarrow"],3,522,"xMinYMin"],Overrightarrow:[["doublerightarrow"],.888,560,"xMaxYMin"],xRightarrow:[["doublerightarrow"],1.526,560,"xMaxYMin"],xLeftarrow:[["doubleleftarrow"],1.526,560,"xMinYMin"],overleftharpoon:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoonup:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoondown:[["leftharpoondown"],.888,522,"xMinYMin"],overrightharpoon:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoonup:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoondown:[["rightharpoondown"],.888,522,"xMaxYMin"],xlongequal:[["longequal"],.888,334,"xMinYMin"],"\\cdlongequal":[["longequal"],3,334,"xMinYMin"],xtwoheadleftarrow:[["twoheadleftarrow"],.888,334,"xMinYMin"],xtwoheadrightarrow:[["twoheadrightarrow"],.888,334,"xMaxYMin"],overleftrightarrow:[["leftarrow","rightarrow"],.888,522],overbrace:[["leftbrace","midbrace","rightbrace"],1.6,548],underbrace:[["leftbraceunder","midbraceunder","rightbraceunder"],1.6,548],underleftrightarrow:[["leftarrow","rightarrow"],.888,522],xleftrightarrow:[["leftarrow","rightarrow"],1.75,522],xLeftrightarrow:[["doubleleftarrow","doublerightarrow"],1.75,560],xrightleftharpoons:[["leftharpoondownplus","rightharpoonplus"],1.75,716],xleftrightharpoons:[["leftharpoonplus","rightharpoondownplus"],1.75,716],xhookleftarrow:[["leftarrow","righthook"],1.08,522],xhookrightarrow:[["lefthook","rightarrow"],1.08,522],overlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],underlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],overgroup:[["leftgroup","rightgroup"],.888,342],undergroup:[["leftgroupunder","rightgroupunder"],.888,342],xmapsto:[["leftmapsto","rightarrow"],1.5,522],xtofrom:[["leftToFrom","rightToFrom"],1.75,528],xrightleftarrows:[["baraboveleftarrow","rightarrowabovebar"],1.75,901],xrightequilibrium:[["baraboveshortleftharpoon","rightharpoonaboveshortbar"],1.75,716],xleftequilibrium:[["shortbaraboveleftharpoon","shortrightharpoonabovebar"],1.75,716]},Fxe=o(function(e){return e.type==="ordgroup"?e.body.length:1},"groupLength"),zxe=o(function(e,r){function n(){var l=4e5,u=e.label.slice(1);if(Vt.contains(["widehat","widecheck","widetilde","utilde"],u)){var h=e,f=Fxe(h.base),d,p,m;if(f>5)u==="widehat"||u==="widecheck"?(d=420,l=2364,m=.42,p=u+"4"):(d=312,l=2340,m=.34,p="tilde4");else{var g=[1,1,2,2,3,3][f];u==="widehat"||u==="widecheck"?(l=[0,1062,2364,2364,2364][g],d=[0,239,300,360,420][g],m=[0,.24,.3,.3,.36,.42][g],p=u+g):(l=[0,600,1033,2339,2340][g],d=[0,260,286,306,312][g],m=[0,.26,.286,.3,.306,.34][g],p="tilde"+g)}var y=new Jl(p),v=new ll([y],{width:"100%",height:ct(m),viewBox:"0 0 "+l+" "+d,preserveAspectRatio:"none"});return{span:Be.makeSvgSpan([],[v],r),minWidth:0,height:m}}else{var x=[],b=Bxe[u],[w,S,T]=b,E=T/1e3,_=w.length,A,L;if(_===1){var M=b[3];A=["hide-tail"],L=[M]}else if(_===2)A=["halfarrow-left","halfarrow-right"],L=["xMinYMin","xMaxYMin"];else if(_===3)A=["brace-left","brace-center","brace-right"],L=["xMinYMin","xMidYMin","xMaxYMin"];else throw new Error(`Correct katexImagesData or update code here to support + `+_+" children.");for(var N=0;N<_;N++){var k=new Jl(w[N]),I=new ll([k],{width:"400em",height:ct(E),viewBox:"0 0 "+l+" "+T,preserveAspectRatio:L[N]+" slice"}),C=Be.makeSvgSpan([A[N]],[I],r);if(_===1)return{span:C,minWidth:S,height:E};C.style.height=ct(E),x.push(C)}return{span:Be.makeSpan(["stretchy"],x,r),minWidth:S,height:E}}}o(n,"buildSvgSpan_");var{span:i,minWidth:a,height:s}=n();return i.height=s,i.style.height=ct(s),a>0&&(i.style.minWidth=ct(a)),i},"svgSpan"),Gxe=o(function(e,r,n,i,a){var s,l=e.height+e.depth+n+i;if(/fbox|color|angl/.test(r)){if(s=Be.makeSpan(["stretchy",r],[],a),r==="fbox"){var u=a.color&&a.getColor();u&&(s.style.borderColor=u)}}else{var h=[];/^[bx]cancel$/.test(r)&&h.push(new ay({x1:"0",y1:"0",x2:"100%",y2:"100%","stroke-width":"0.046em"})),/^x?cancel$/.test(r)&&h.push(new ay({x1:"0",y1:"100%",x2:"100%",y2:"0","stroke-width":"0.046em"}));var f=new ll(h,{width:"100%",height:ct(l)});s=Be.makeSvgSpan([],[f],a)}return s.height=l,s.style.height=ct(l),s},"encloseSpan"),ou={encloseSpan:Gxe,mathMLnode:Pxe,svgSpan:zxe};o(ir,"assertNodeType");o(B7,"assertSymbolNodeType");o(T4,"checkSymbolNodeType");F7=o((t,e)=>{var r,n,i;t&&t.type==="supsub"?(n=ir(t.base,"accent"),r=n.base,t.base=r,i=uxe(Cr(t,e)),t.base=n):(n=ir(t,"accent"),r=n.base);var a=Cr(r,e.havingCrampedStyle()),s=n.isShifty&&Vt.isCharacterBox(r),l=0;if(s){var u=Vt.getBaseElem(r),h=Cr(u,e.havingCrampedStyle());l=tG(h).skew}var f=n.label==="\\c",d=f?a.height+a.depth:Math.min(a.height,e.fontMetrics().xHeight),p;if(n.isStretchy)p=ou.svgSpan(n,e),p=Be.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:a},{type:"elem",elem:p,wrapperClasses:["svg-align"],wrapperStyle:l>0?{width:"calc(100% - "+ct(2*l)+")",marginLeft:ct(2*l)}:void 0}]},e);else{var m,g;n.label==="\\vec"?(m=Be.staticSvg("vec",e),g=Be.svgData.vec[1]):(m=Be.makeOrd({mode:n.mode,text:n.label},e,"textord"),m=tG(m),m.italic=0,g=m.width,f&&(d+=m.depth)),p=Be.makeSpan(["accent-body"],[m]);var y=n.label==="\\textcircled";y&&(p.classes.push("accent-full"),d=a.height);var v=l;y||(v-=g/2),p.style.left=ct(v),n.label==="\\textcircled"&&(p.style.top=".2em"),p=Be.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:a},{type:"kern",size:-d},{type:"elem",elem:p}]},e)}var x=Be.makeSpan(["mord","accent"],[p],e);return i?(i.children[0]=x,i.height=Math.max(x.height,i.height),i.classes[0]="mord",i):x},"htmlBuilder$a"),HG=o((t,e)=>{var r=t.isStretchy?ou.mathMLnode(t.label):new et.MathNode("mo",[_o(t.label,t.mode)]),n=new et.MathNode("mover",[fn(t.base,e),r]);return n.setAttribute("accent","true"),n},"mathmlBuilder$9"),$xe=new RegExp(["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring"].map(t=>"\\"+t).join("|"));vt({type:"accent",names:["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring","\\widecheck","\\widehat","\\widetilde","\\overrightarrow","\\overleftarrow","\\Overrightarrow","\\overleftrightarrow","\\overgroup","\\overlinesegment","\\overleftharpoon","\\overrightharpoon"],props:{numArgs:1},handler:o((t,e)=>{var r=y4(e[0]),n=!$xe.test(t.funcName),i=!n||t.funcName==="\\widehat"||t.funcName==="\\widetilde"||t.funcName==="\\widecheck";return{type:"accent",mode:t.parser.mode,label:t.funcName,isStretchy:n,isShifty:i,base:r}},"handler"),htmlBuilder:F7,mathmlBuilder:HG});vt({type:"accent",names:["\\'","\\`","\\^","\\~","\\=","\\u","\\.",'\\"',"\\c","\\r","\\H","\\v","\\textcircled"],props:{numArgs:1,allowedInText:!0,allowedInMath:!0,argTypes:["primitive"]},handler:o((t,e)=>{var r=e[0],n=t.parser.mode;return n==="math"&&(t.parser.settings.reportNonstrict("mathVsTextAccents","LaTeX's accent "+t.funcName+" works only in text mode"),n="text"),{type:"accent",mode:n,label:t.funcName,isStretchy:!1,isShifty:!0,base:r}},"handler"),htmlBuilder:F7,mathmlBuilder:HG});vt({type:"accentUnder",names:["\\underleftarrow","\\underrightarrow","\\underleftrightarrow","\\undergroup","\\underlinesegment","\\utilde"],props:{numArgs:1},handler:o((t,e)=>{var{parser:r,funcName:n}=t,i=e[0];return{type:"accentUnder",mode:r.mode,label:n,base:i}},"handler"),htmlBuilder:o((t,e)=>{var r=Cr(t.base,e),n=ou.svgSpan(t,e),i=t.label==="\\utilde"?.12:0,a=Be.makeVList({positionType:"top",positionData:r.height,children:[{type:"elem",elem:n,wrapperClasses:["svg-align"]},{type:"kern",size:i},{type:"elem",elem:r}]},e);return Be.makeSpan(["mord","accentunder"],[a],e)},"htmlBuilder"),mathmlBuilder:o((t,e)=>{var r=ou.mathMLnode(t.label),n=new et.MathNode("munder",[fn(t.base,e),r]);return n.setAttribute("accentunder","true"),n},"mathmlBuilder")});o4=o(t=>{var e=new et.MathNode("mpadded",t?[t]:[]);return e.setAttribute("width","+0.6em"),e.setAttribute("lspace","0.3em"),e},"paddedNode");vt({type:"xArrow",names:["\\xleftarrow","\\xrightarrow","\\xLeftarrow","\\xRightarrow","\\xleftrightarrow","\\xLeftrightarrow","\\xhookleftarrow","\\xhookrightarrow","\\xmapsto","\\xrightharpoondown","\\xrightharpoonup","\\xleftharpoondown","\\xleftharpoonup","\\xrightleftharpoons","\\xleftrightharpoons","\\xlongequal","\\xtwoheadrightarrow","\\xtwoheadleftarrow","\\xtofrom","\\xrightleftarrows","\\xrightequilibrium","\\xleftequilibrium","\\\\cdrightarrow","\\\\cdleftarrow","\\\\cdlongequal"],props:{numArgs:1,numOptionalArgs:1},handler(t,e,r){var{parser:n,funcName:i}=t;return{type:"xArrow",mode:n.mode,label:i,body:e[0],below:r[0]}},htmlBuilder(t,e){var r=e.style,n=e.havingStyle(r.sup()),i=Be.wrapFragment(Cr(t.body,n,e),e),a=t.label.slice(0,2)==="\\x"?"x":"cd";i.classes.push(a+"-arrow-pad");var s;t.below&&(n=e.havingStyle(r.sub()),s=Be.wrapFragment(Cr(t.below,n,e),e),s.classes.push(a+"-arrow-pad"));var l=ou.svgSpan(t,e),u=-e.fontMetrics().axisHeight+.5*l.height,h=-e.fontMetrics().axisHeight-.5*l.height-.111;(i.depth>.25||t.label==="\\xleftequilibrium")&&(h-=i.depth);var f;if(s){var d=-e.fontMetrics().axisHeight+s.height+.5*l.height+.111;f=Be.makeVList({positionType:"individualShift",children:[{type:"elem",elem:i,shift:h},{type:"elem",elem:l,shift:u},{type:"elem",elem:s,shift:d}]},e)}else f=Be.makeVList({positionType:"individualShift",children:[{type:"elem",elem:i,shift:h},{type:"elem",elem:l,shift:u}]},e);return f.children[0].children[0].children[1].classes.push("svg-align"),Be.makeSpan(["mrel","x-arrow"],[f],e)},mathmlBuilder(t,e){var r=ou.mathMLnode(t.label);r.setAttribute("minsize",t.label.charAt(0)==="x"?"1.75em":"3.0em");var n;if(t.body){var i=o4(fn(t.body,e));if(t.below){var a=o4(fn(t.below,e));n=new et.MathNode("munderover",[r,a,i])}else n=new et.MathNode("mover",[r,i])}else if(t.below){var s=o4(fn(t.below,e));n=new et.MathNode("munder",[r,s])}else n=o4(),n=new et.MathNode("mover",[r,n]);return n}});Vxe=Be.makeSpan;o(YG,"htmlBuilder$9");o(WG,"mathmlBuilder$8");vt({type:"mclass",names:["\\mathord","\\mathbin","\\mathrel","\\mathopen","\\mathclose","\\mathpunct","\\mathinner"],props:{numArgs:1,primitive:!0},handler(t,e){var{parser:r,funcName:n}=t,i=e[0];return{type:"mclass",mode:r.mode,mclass:"m"+n.slice(5),body:ui(i),isCharacterBox:Vt.isCharacterBox(i)}},htmlBuilder:YG,mathmlBuilder:WG});k4=o(t=>{var e=t.type==="ordgroup"&&t.body.length?t.body[0]:t;return e.type==="atom"&&(e.family==="bin"||e.family==="rel")?"m"+e.family:"mord"},"binrelClass");vt({type:"mclass",names:["\\@binrel"],props:{numArgs:2},handler(t,e){var{parser:r}=t;return{type:"mclass",mode:r.mode,mclass:k4(e[0]),body:ui(e[1]),isCharacterBox:Vt.isCharacterBox(e[1])}}});vt({type:"mclass",names:["\\stackrel","\\overset","\\underset"],props:{numArgs:2},handler(t,e){var{parser:r,funcName:n}=t,i=e[1],a=e[0],s;n!=="\\stackrel"?s=k4(i):s="mrel";var l={type:"op",mode:i.mode,limits:!0,alwaysHandleSupSub:!0,parentIsSupSub:!1,symbol:!1,suppressBaseShift:n!=="\\stackrel",body:ui(i)},u={type:"supsub",mode:a.mode,base:l,sup:n==="\\underset"?null:a,sub:n==="\\underset"?a:null};return{type:"mclass",mode:r.mode,mclass:s,body:[u],isCharacterBox:Vt.isCharacterBox(u)}},htmlBuilder:YG,mathmlBuilder:WG});vt({type:"pmb",names:["\\pmb"],props:{numArgs:1,allowedInText:!0},handler(t,e){var{parser:r}=t;return{type:"pmb",mode:r.mode,mclass:k4(e[0]),body:ui(e[0])}},htmlBuilder(t,e){var r=Ri(t.body,e,!0),n=Be.makeSpan([t.mclass],r,e);return n.style.textShadow="0.02em 0.01em 0.04px",n},mathmlBuilder(t,e){var r=gs(t.body,e),n=new et.MathNode("mstyle",r);return n.setAttribute("style","text-shadow: 0.02em 0.01em 0.04px"),n}});Uxe={">":"\\\\cdrightarrow","<":"\\\\cdleftarrow","=":"\\\\cdlongequal",A:"\\uparrow",V:"\\downarrow","|":"\\Vert",".":"no arrow"},oG=o(()=>({type:"styling",body:[],mode:"math",style:"display"}),"newCell"),lG=o(t=>t.type==="textord"&&t.text==="@","isStartOfArrow"),Hxe=o((t,e)=>(t.type==="mathord"||t.type==="atom")&&t.text===e,"isLabelEnd");o(Yxe,"cdArrow");o(Wxe,"parseCD");vt({type:"cdlabel",names:["\\\\cdleft","\\\\cdright"],props:{numArgs:1},handler(t,e){var{parser:r,funcName:n}=t;return{type:"cdlabel",mode:r.mode,side:n.slice(4),label:e[0]}},htmlBuilder(t,e){var r=e.havingStyle(e.style.sup()),n=Be.wrapFragment(Cr(t.label,r,e),e);return n.classes.push("cd-label-"+t.side),n.style.bottom=ct(.8-n.depth),n.height=0,n.depth=0,n},mathmlBuilder(t,e){var r=new et.MathNode("mrow",[fn(t.label,e)]);return r=new et.MathNode("mpadded",[r]),r.setAttribute("width","0"),t.side==="left"&&r.setAttribute("lspace","-1width"),r.setAttribute("voffset","0.7em"),r=new et.MathNode("mstyle",[r]),r.setAttribute("displaystyle","false"),r.setAttribute("scriptlevel","1"),r}});vt({type:"cdlabelparent",names:["\\\\cdparent"],props:{numArgs:1},handler(t,e){var{parser:r}=t;return{type:"cdlabelparent",mode:r.mode,fragment:e[0]}},htmlBuilder(t,e){var r=Be.wrapFragment(Cr(t.fragment,e),e);return r.classes.push("cd-vert-arrow"),r},mathmlBuilder(t,e){return new et.MathNode("mrow",[fn(t.fragment,e)])}});vt({type:"textord",names:["\\@char"],props:{numArgs:1,allowedInText:!0},handler(t,e){for(var{parser:r}=t,n=ir(e[0],"ordgroup"),i=n.body,a="",s=0;s=1114111)throw new nt("\\@char with invalid code point "+a);return u<=65535?h=String.fromCharCode(u):(u-=65536,h=String.fromCharCode((u>>10)+55296,(u&1023)+56320)),{type:"textord",mode:r.mode,text:h}}});qG=o((t,e)=>{var r=Ri(t.body,e.withColor(t.color),!1);return Be.makeFragment(r)},"htmlBuilder$8"),XG=o((t,e)=>{var r=gs(t.body,e.withColor(t.color)),n=new et.MathNode("mstyle",r);return n.setAttribute("mathcolor",t.color),n},"mathmlBuilder$7");vt({type:"color",names:["\\textcolor"],props:{numArgs:2,allowedInText:!0,argTypes:["color","original"]},handler(t,e){var{parser:r}=t,n=ir(e[0],"color-token").color,i=e[1];return{type:"color",mode:r.mode,color:n,body:ui(i)}},htmlBuilder:qG,mathmlBuilder:XG});vt({type:"color",names:["\\color"],props:{numArgs:1,allowedInText:!0,argTypes:["color"]},handler(t,e){var{parser:r,breakOnTokenText:n}=t,i=ir(e[0],"color-token").color;r.gullet.macros.set("\\current@color",i);var a=r.parseExpression(!0,n);return{type:"color",mode:r.mode,color:i,body:a}},htmlBuilder:qG,mathmlBuilder:XG});vt({type:"cr",names:["\\\\"],props:{numArgs:0,numOptionalArgs:0,allowedInText:!0},handler(t,e,r){var{parser:n}=t,i=n.gullet.future().text==="["?n.parseSizeGroup(!0):null,a=!n.settings.displayMode||!n.settings.useStrictBehavior("newLineInDisplayMode","In LaTeX, \\\\ or \\newline does nothing in display mode");return{type:"cr",mode:n.mode,newLine:a,size:i&&ir(i,"size").value}},htmlBuilder(t,e){var r=Be.makeSpan(["mspace"],[],e);return t.newLine&&(r.classes.push("newline"),t.size&&(r.style.marginTop=ct(Hn(t.size,e)))),r},mathmlBuilder(t,e){var r=new et.MathNode("mspace");return t.newLine&&(r.setAttribute("linebreak","newline"),t.size&&r.setAttribute("height",ct(Hn(t.size,e)))),r}});A7={"\\global":"\\global","\\long":"\\\\globallong","\\\\globallong":"\\\\globallong","\\def":"\\gdef","\\gdef":"\\gdef","\\edef":"\\xdef","\\xdef":"\\xdef","\\let":"\\\\globallet","\\futurelet":"\\\\globalfuture"},jG=o(t=>{var e=t.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(e))throw new nt("Expected a control sequence",t);return e},"checkControlSequence"),qxe=o(t=>{var e=t.gullet.popToken();return e.text==="="&&(e=t.gullet.popToken(),e.text===" "&&(e=t.gullet.popToken())),e},"getRHS"),KG=o((t,e,r,n)=>{var i=t.gullet.macros.get(r.text);i==null&&(r.noexpand=!0,i={tokens:[r],numArgs:0,unexpandable:!t.gullet.isExpandable(r.text)}),t.gullet.macros.set(e,i,n)},"letCommand");vt({type:"internal",names:["\\global","\\long","\\\\globallong"],props:{numArgs:0,allowedInText:!0},handler(t){var{parser:e,funcName:r}=t;e.consumeSpaces();var n=e.fetch();if(A7[n.text])return(r==="\\global"||r==="\\\\globallong")&&(n.text=A7[n.text]),ir(e.parseFunction(),"internal");throw new nt("Invalid token after macro prefix",n)}});vt({type:"internal",names:["\\def","\\gdef","\\edef","\\xdef"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(t){var{parser:e,funcName:r}=t,n=e.gullet.popToken(),i=n.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(i))throw new nt("Expected a control sequence",n);for(var a=0,s,l=[[]];e.gullet.future().text!=="{";)if(n=e.gullet.popToken(),n.text==="#"){if(e.gullet.future().text==="{"){s=e.gullet.future(),l[a].push("{");break}if(n=e.gullet.popToken(),!/^[1-9]$/.test(n.text))throw new nt('Invalid argument number "'+n.text+'"');if(parseInt(n.text)!==a+1)throw new nt('Argument number "'+n.text+'" out of order');a++,l.push([])}else{if(n.text==="EOF")throw new nt("Expected a macro definition");l[a].push(n.text)}var{tokens:u}=e.gullet.consumeArg();return s&&u.unshift(s),(r==="\\edef"||r==="\\xdef")&&(u=e.gullet.expandTokens(u),u.reverse()),e.gullet.macros.set(i,{tokens:u,numArgs:a,delimiters:l},r===A7[r]),{type:"internal",mode:e.mode}}});vt({type:"internal",names:["\\let","\\\\globallet"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(t){var{parser:e,funcName:r}=t,n=jG(e.gullet.popToken());e.gullet.consumeSpaces();var i=qxe(e);return KG(e,n,i,r==="\\\\globallet"),{type:"internal",mode:e.mode}}});vt({type:"internal",names:["\\futurelet","\\\\globalfuture"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(t){var{parser:e,funcName:r}=t,n=jG(e.gullet.popToken()),i=e.gullet.popToken(),a=e.gullet.popToken();return KG(e,n,a,r==="\\\\globalfuture"),e.gullet.pushToken(a),e.gullet.pushToken(i),{type:"internal",mode:e.mode}}});ey=o(function(e,r,n){var i=wn.math[e]&&wn.math[e].replace,a=M7(i||e,r,n);if(!a)throw new Error("Unsupported symbol "+e+" and font size "+r+".");return a},"getMetrics"),z7=o(function(e,r,n,i){var a=n.havingBaseStyle(r),s=Be.makeSpan(i.concat(a.sizingClasses(n)),[e],n),l=a.sizeMultiplier/n.sizeMultiplier;return s.height*=l,s.depth*=l,s.maxFontSize=a.sizeMultiplier,s},"styleWrap"),QG=o(function(e,r,n){var i=r.havingBaseStyle(n),a=(1-r.sizeMultiplier/i.sizeMultiplier)*r.fontMetrics().axisHeight;e.classes.push("delimcenter"),e.style.top=ct(a),e.height-=a,e.depth+=a},"centerSpan"),Xxe=o(function(e,r,n,i,a,s){var l=Be.makeSymbol(e,"Main-Regular",a,i),u=z7(l,r,i,s);return n&&QG(u,i,r),u},"makeSmallDelim"),jxe=o(function(e,r,n,i){return Be.makeSymbol(e,"Size"+r+"-Regular",n,i)},"mathrmSize"),ZG=o(function(e,r,n,i,a,s){var l=jxe(e,r,a,i),u=z7(Be.makeSpan(["delimsizing","size"+r],[l],i),Ht.TEXT,i,s);return n&&QG(u,i,Ht.TEXT),u},"makeLargeDelim"),h7=o(function(e,r,n){var i;r==="Size1-Regular"?i="delim-size1":i="delim-size4";var a=Be.makeSpan(["delimsizinginner",i],[Be.makeSpan([],[Be.makeSymbol(e,r,n)])]);return{type:"elem",elem:a}},"makeGlyphSpan"),f7=o(function(e,r,n){var i=Zl["Size4-Regular"][e.charCodeAt(0)]?Zl["Size4-Regular"][e.charCodeAt(0)][4]:Zl["Size1-Regular"][e.charCodeAt(0)][4],a=new Jl("inner",nxe(e,Math.round(1e3*r))),s=new ll([a],{width:ct(i),height:ct(r),style:"width:"+ct(i),viewBox:"0 0 "+1e3*i+" "+Math.round(1e3*r),preserveAspectRatio:"xMinYMin"}),l=Be.makeSvgSpan([],[s],n);return l.height=r,l.style.height=ct(r),l.style.width=ct(i),{type:"elem",elem:l}},"makeInner"),_7=.008,l4={type:"kern",size:-1*_7},Kxe=["|","\\lvert","\\rvert","\\vert"],Qxe=["\\|","\\lVert","\\rVert","\\Vert"],JG=o(function(e,r,n,i,a,s){var l,u,h,f,d="",p=0;l=h=f=e,u=null;var m="Size1-Regular";e==="\\uparrow"?h=f="\u23D0":e==="\\Uparrow"?h=f="\u2016":e==="\\downarrow"?l=h="\u23D0":e==="\\Downarrow"?l=h="\u2016":e==="\\updownarrow"?(l="\\uparrow",h="\u23D0",f="\\downarrow"):e==="\\Updownarrow"?(l="\\Uparrow",h="\u2016",f="\\Downarrow"):Vt.contains(Kxe,e)?(h="\u2223",d="vert",p=333):Vt.contains(Qxe,e)?(h="\u2225",d="doublevert",p=556):e==="["||e==="\\lbrack"?(l="\u23A1",h="\u23A2",f="\u23A3",m="Size4-Regular",d="lbrack",p=667):e==="]"||e==="\\rbrack"?(l="\u23A4",h="\u23A5",f="\u23A6",m="Size4-Regular",d="rbrack",p=667):e==="\\lfloor"||e==="\u230A"?(h=l="\u23A2",f="\u23A3",m="Size4-Regular",d="lfloor",p=667):e==="\\lceil"||e==="\u2308"?(l="\u23A1",h=f="\u23A2",m="Size4-Regular",d="lceil",p=667):e==="\\rfloor"||e==="\u230B"?(h=l="\u23A5",f="\u23A6",m="Size4-Regular",d="rfloor",p=667):e==="\\rceil"||e==="\u2309"?(l="\u23A4",h=f="\u23A5",m="Size4-Regular",d="rceil",p=667):e==="("||e==="\\lparen"?(l="\u239B",h="\u239C",f="\u239D",m="Size4-Regular",d="lparen",p=875):e===")"||e==="\\rparen"?(l="\u239E",h="\u239F",f="\u23A0",m="Size4-Regular",d="rparen",p=875):e==="\\{"||e==="\\lbrace"?(l="\u23A7",u="\u23A8",f="\u23A9",h="\u23AA",m="Size4-Regular"):e==="\\}"||e==="\\rbrace"?(l="\u23AB",u="\u23AC",f="\u23AD",h="\u23AA",m="Size4-Regular"):e==="\\lgroup"||e==="\u27EE"?(l="\u23A7",f="\u23A9",h="\u23AA",m="Size4-Regular"):e==="\\rgroup"||e==="\u27EF"?(l="\u23AB",f="\u23AD",h="\u23AA",m="Size4-Regular"):e==="\\lmoustache"||e==="\u23B0"?(l="\u23A7",f="\u23AD",h="\u23AA",m="Size4-Regular"):(e==="\\rmoustache"||e==="\u23B1")&&(l="\u23AB",f="\u23A9",h="\u23AA",m="Size4-Regular");var g=ey(l,m,a),y=g.height+g.depth,v=ey(h,m,a),x=v.height+v.depth,b=ey(f,m,a),w=b.height+b.depth,S=0,T=1;if(u!==null){var E=ey(u,m,a);S=E.height+E.depth,T=2}var _=y+w+S,A=Math.max(0,Math.ceil((r-_)/(T*x))),L=_+A*T*x,M=i.fontMetrics().axisHeight;n&&(M*=i.sizeMultiplier);var N=L/2-M,k=[];if(d.length>0){var I=L-y-w,C=Math.round(L*1e3),O=ixe(d,Math.round(I*1e3)),D=new Jl(d,O),P=(p/1e3).toFixed(3)+"em",F=(C/1e3).toFixed(3)+"em",B=new ll([D],{width:P,height:F,viewBox:"0 0 "+p+" "+C}),$=Be.makeSvgSpan([],[B],i);$.height=C/1e3,$.style.width=P,$.style.height=F,k.push({type:"elem",elem:$})}else{if(k.push(h7(f,m,a)),k.push(l4),u===null){var z=L-y-w+2*_7;k.push(f7(h,z,i))}else{var Y=(L-y-w-S)/2+2*_7;k.push(f7(h,Y,i)),k.push(l4),k.push(h7(u,m,a)),k.push(l4),k.push(f7(h,Y,i))}k.push(l4),k.push(h7(l,m,a))}var Q=i.havingBaseStyle(Ht.TEXT),X=Be.makeVList({positionType:"bottom",positionData:N,children:k},Q);return z7(Be.makeSpan(["delimsizing","mult"],[X],Q),Ht.TEXT,i,s)},"makeStackedDelim"),d7=80,p7=.08,m7=o(function(e,r,n,i,a){var s=rxe(e,i,n),l=new Jl(e,s),u=new ll([l],{width:"400em",height:ct(r),viewBox:"0 0 400000 "+n,preserveAspectRatio:"xMinYMin slice"});return Be.makeSvgSpan(["hide-tail"],[u],a)},"sqrtSvg"),Zxe=o(function(e,r){var n=r.havingBaseSizing(),i=n$("\\surd",e*n.sizeMultiplier,r$,n),a=n.sizeMultiplier,s=Math.max(0,r.minRuleThickness-r.fontMetrics().sqrtRuleThickness),l,u=0,h=0,f=0,d;return i.type==="small"?(f=1e3+1e3*s+d7,e<1?a=1:e<1.4&&(a=.7),u=(1+s+p7)/a,h=(1+s)/a,l=m7("sqrtMain",u,f,s,r),l.style.minWidth="0.853em",d=.833/a):i.type==="large"?(f=(1e3+d7)*ty[i.size],h=(ty[i.size]+s)/a,u=(ty[i.size]+s+p7)/a,l=m7("sqrtSize"+i.size,u,f,s,r),l.style.minWidth="1.02em",d=1/a):(u=e+s+p7,h=e+s,f=Math.floor(1e3*e+s)+d7,l=m7("sqrtTall",u,f,s,r),l.style.minWidth="0.742em",d=1.056),l.height=h,l.style.height=ct(u),{span:l,advanceWidth:d,ruleWidth:(r.fontMetrics().sqrtRuleThickness+s)*a}},"makeSqrtImage"),e$=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230A","\u230B","\\lceil","\\rceil","\u2308","\u2309","\\surd"],Jxe=["\\uparrow","\\downarrow","\\updownarrow","\\Uparrow","\\Downarrow","\\Updownarrow","|","\\|","\\vert","\\Vert","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27EE","\u27EF","\\lmoustache","\\rmoustache","\u23B0","\u23B1"],t$=["<",">","\\langle","\\rangle","/","\\backslash","\\lt","\\gt"],ty=[0,1.2,1.8,2.4,3],ebe=o(function(e,r,n,i,a){if(e==="<"||e==="\\lt"||e==="\u27E8"?e="\\langle":(e===">"||e==="\\gt"||e==="\u27E9")&&(e="\\rangle"),Vt.contains(e$,e)||Vt.contains(t$,e))return ZG(e,r,!1,n,i,a);if(Vt.contains(Jxe,e))return JG(e,ty[r],!1,n,i,a);throw new nt("Illegal delimiter: '"+e+"'")},"makeSizedDelim"),tbe=[{type:"small",style:Ht.SCRIPTSCRIPT},{type:"small",style:Ht.SCRIPT},{type:"small",style:Ht.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4}],rbe=[{type:"small",style:Ht.SCRIPTSCRIPT},{type:"small",style:Ht.SCRIPT},{type:"small",style:Ht.TEXT},{type:"stack"}],r$=[{type:"small",style:Ht.SCRIPTSCRIPT},{type:"small",style:Ht.SCRIPT},{type:"small",style:Ht.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4},{type:"stack"}],nbe=o(function(e){if(e.type==="small")return"Main-Regular";if(e.type==="large")return"Size"+e.size+"-Regular";if(e.type==="stack")return"Size4-Regular";throw new Error("Add support for delim type '"+e.type+"' here.")},"delimTypeToFont"),n$=o(function(e,r,n,i){for(var a=Math.min(2,3-i.style.size),s=a;sr)return n[s]}return n[n.length-1]},"traverseSequence"),i$=o(function(e,r,n,i,a,s){e==="<"||e==="\\lt"||e==="\u27E8"?e="\\langle":(e===">"||e==="\\gt"||e==="\u27E9")&&(e="\\rangle");var l;Vt.contains(t$,e)?l=tbe:Vt.contains(e$,e)?l=r$:l=rbe;var u=n$(e,r,l,i);return u.type==="small"?Xxe(e,u.style,n,i,a,s):u.type==="large"?ZG(e,u.size,n,i,a,s):JG(e,r,n,i,a,s)},"makeCustomSizedDelim"),ibe=o(function(e,r,n,i,a,s){var l=i.fontMetrics().axisHeight*i.sizeMultiplier,u=901,h=5/i.fontMetrics().ptPerEm,f=Math.max(r-l,n+l),d=Math.max(f/500*u,2*f-h);return i$(e,d,!0,i,a,s)},"makeLeftRightDelim"),au={sqrtImage:Zxe,sizedDelim:ebe,sizeToMaxHeight:ty,customSizedDelim:i$,leftRightDelim:ibe},cG={"\\bigl":{mclass:"mopen",size:1},"\\Bigl":{mclass:"mopen",size:2},"\\biggl":{mclass:"mopen",size:3},"\\Biggl":{mclass:"mopen",size:4},"\\bigr":{mclass:"mclose",size:1},"\\Bigr":{mclass:"mclose",size:2},"\\biggr":{mclass:"mclose",size:3},"\\Biggr":{mclass:"mclose",size:4},"\\bigm":{mclass:"mrel",size:1},"\\Bigm":{mclass:"mrel",size:2},"\\biggm":{mclass:"mrel",size:3},"\\Biggm":{mclass:"mrel",size:4},"\\big":{mclass:"mord",size:1},"\\Big":{mclass:"mord",size:2},"\\bigg":{mclass:"mord",size:3},"\\Bigg":{mclass:"mord",size:4}},abe=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230A","\u230B","\\lceil","\\rceil","\u2308","\u2309","<",">","\\langle","\u27E8","\\rangle","\u27E9","\\lt","\\gt","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27EE","\u27EF","\\lmoustache","\\rmoustache","\u23B0","\u23B1","/","\\backslash","|","\\vert","\\|","\\Vert","\\uparrow","\\Uparrow","\\downarrow","\\Downarrow","\\updownarrow","\\Updownarrow","."];o(E4,"checkDelimiter");vt({type:"delimsizing",names:["\\bigl","\\Bigl","\\biggl","\\Biggl","\\bigr","\\Bigr","\\biggr","\\Biggr","\\bigm","\\Bigm","\\biggm","\\Biggm","\\big","\\Big","\\bigg","\\Bigg"],props:{numArgs:1,argTypes:["primitive"]},handler:o((t,e)=>{var r=E4(e[0],t);return{type:"delimsizing",mode:t.parser.mode,size:cG[t.funcName].size,mclass:cG[t.funcName].mclass,delim:r.text}},"handler"),htmlBuilder:o((t,e)=>t.delim==="."?Be.makeSpan([t.mclass]):au.sizedDelim(t.delim,t.size,e,t.mode,[t.mclass]),"htmlBuilder"),mathmlBuilder:o(t=>{var e=[];t.delim!=="."&&e.push(_o(t.delim,t.mode));var r=new et.MathNode("mo",e);t.mclass==="mopen"||t.mclass==="mclose"?r.setAttribute("fence","true"):r.setAttribute("fence","false"),r.setAttribute("stretchy","true");var n=ct(au.sizeToMaxHeight[t.size]);return r.setAttribute("minsize",n),r.setAttribute("maxsize",n),r},"mathmlBuilder")});o(uG,"assertParsed");vt({type:"leftright-right",names:["\\right"],props:{numArgs:1,primitive:!0},handler:o((t,e)=>{var r=t.parser.gullet.macros.get("\\current@color");if(r&&typeof r!="string")throw new nt("\\current@color set to non-string in \\right");return{type:"leftright-right",mode:t.parser.mode,delim:E4(e[0],t).text,color:r}},"handler")});vt({type:"leftright",names:["\\left"],props:{numArgs:1,primitive:!0},handler:o((t,e)=>{var r=E4(e[0],t),n=t.parser;++n.leftrightDepth;var i=n.parseExpression(!1);--n.leftrightDepth,n.expect("\\right",!1);var a=ir(n.parseFunction(),"leftright-right");return{type:"leftright",mode:n.mode,body:i,left:r.text,right:a.delim,rightColor:a.color}},"handler"),htmlBuilder:o((t,e)=>{uG(t);for(var r=Ri(t.body,e,!0,["mopen","mclose"]),n=0,i=0,a=!1,s=0;s{uG(t);var r=gs(t.body,e);if(t.left!=="."){var n=new et.MathNode("mo",[_o(t.left,t.mode)]);n.setAttribute("fence","true"),r.unshift(n)}if(t.right!=="."){var i=new et.MathNode("mo",[_o(t.right,t.mode)]);i.setAttribute("fence","true"),t.rightColor&&i.setAttribute("mathcolor",t.rightColor),r.push(i)}return O7(r)},"mathmlBuilder")});vt({type:"middle",names:["\\middle"],props:{numArgs:1,primitive:!0},handler:o((t,e)=>{var r=E4(e[0],t);if(!t.parser.leftrightDepth)throw new nt("\\middle without preceding \\left",r);return{type:"middle",mode:t.parser.mode,delim:r.text}},"handler"),htmlBuilder:o((t,e)=>{var r;if(t.delim===".")r=sy(e,[]);else{r=au.sizedDelim(t.delim,1,e,t.mode,[]);var n={delim:t.delim,options:e};r.isMiddle=n}return r},"htmlBuilder"),mathmlBuilder:o((t,e)=>{var r=t.delim==="\\vert"||t.delim==="|"?_o("|","text"):_o(t.delim,t.mode),n=new et.MathNode("mo",[r]);return n.setAttribute("fence","true"),n.setAttribute("lspace","0.05em"),n.setAttribute("rspace","0.05em"),n},"mathmlBuilder")});G7=o((t,e)=>{var r=Be.wrapFragment(Cr(t.body,e),e),n=t.label.slice(1),i=e.sizeMultiplier,a,s=0,l=Vt.isCharacterBox(t.body);if(n==="sout")a=Be.makeSpan(["stretchy","sout"]),a.height=e.fontMetrics().defaultRuleThickness/i,s=-.5*e.fontMetrics().xHeight;else if(n==="phase"){var u=Hn({number:.6,unit:"pt"},e),h=Hn({number:.35,unit:"ex"},e),f=e.havingBaseSizing();i=i/f.sizeMultiplier;var d=r.height+r.depth+u+h;r.style.paddingLeft=ct(d/2+u);var p=Math.floor(1e3*d*i),m=exe(p),g=new ll([new Jl("phase",m)],{width:"400em",height:ct(p/1e3),viewBox:"0 0 400000 "+p,preserveAspectRatio:"xMinYMin slice"});a=Be.makeSvgSpan(["hide-tail"],[g],e),a.style.height=ct(d),s=r.depth+u+h}else{/cancel/.test(n)?l||r.classes.push("cancel-pad"):n==="angl"?r.classes.push("anglpad"):r.classes.push("boxpad");var y=0,v=0,x=0;/box/.test(n)?(x=Math.max(e.fontMetrics().fboxrule,e.minRuleThickness),y=e.fontMetrics().fboxsep+(n==="colorbox"?0:x),v=y):n==="angl"?(x=Math.max(e.fontMetrics().defaultRuleThickness,e.minRuleThickness),y=4*x,v=Math.max(0,.25-r.depth)):(y=l?.2:0,v=y),a=ou.encloseSpan(r,n,y,v,e),/fbox|boxed|fcolorbox/.test(n)?(a.style.borderStyle="solid",a.style.borderWidth=ct(x)):n==="angl"&&x!==.049&&(a.style.borderTopWidth=ct(x),a.style.borderRightWidth=ct(x)),s=r.depth+v,t.backgroundColor&&(a.style.backgroundColor=t.backgroundColor,t.borderColor&&(a.style.borderColor=t.borderColor))}var b;if(t.backgroundColor)b=Be.makeVList({positionType:"individualShift",children:[{type:"elem",elem:a,shift:s},{type:"elem",elem:r,shift:0}]},e);else{var w=/cancel|phase/.test(n)?["svg-align"]:[];b=Be.makeVList({positionType:"individualShift",children:[{type:"elem",elem:r,shift:0},{type:"elem",elem:a,shift:s,wrapperClasses:w}]},e)}return/cancel/.test(n)&&(b.height=r.height,b.depth=r.depth),/cancel/.test(n)&&!l?Be.makeSpan(["mord","cancel-lap"],[b],e):Be.makeSpan(["mord"],[b],e)},"htmlBuilder$7"),$7=o((t,e)=>{var r=0,n=new et.MathNode(t.label.indexOf("colorbox")>-1?"mpadded":"menclose",[fn(t.body,e)]);switch(t.label){case"\\cancel":n.setAttribute("notation","updiagonalstrike");break;case"\\bcancel":n.setAttribute("notation","downdiagonalstrike");break;case"\\phase":n.setAttribute("notation","phasorangle");break;case"\\sout":n.setAttribute("notation","horizontalstrike");break;case"\\fbox":n.setAttribute("notation","box");break;case"\\angl":n.setAttribute("notation","actuarial");break;case"\\fcolorbox":case"\\colorbox":if(r=e.fontMetrics().fboxsep*e.fontMetrics().ptPerEm,n.setAttribute("width","+"+2*r+"pt"),n.setAttribute("height","+"+2*r+"pt"),n.setAttribute("lspace",r+"pt"),n.setAttribute("voffset",r+"pt"),t.label==="\\fcolorbox"){var i=Math.max(e.fontMetrics().fboxrule,e.minRuleThickness);n.setAttribute("style","border: "+i+"em solid "+String(t.borderColor))}break;case"\\xcancel":n.setAttribute("notation","updiagonalstrike downdiagonalstrike");break}return t.backgroundColor&&n.setAttribute("mathbackground",t.backgroundColor),n},"mathmlBuilder$6");vt({type:"enclose",names:["\\colorbox"],props:{numArgs:2,allowedInText:!0,argTypes:["color","text"]},handler(t,e,r){var{parser:n,funcName:i}=t,a=ir(e[0],"color-token").color,s=e[1];return{type:"enclose",mode:n.mode,label:i,backgroundColor:a,body:s}},htmlBuilder:G7,mathmlBuilder:$7});vt({type:"enclose",names:["\\fcolorbox"],props:{numArgs:3,allowedInText:!0,argTypes:["color","color","text"]},handler(t,e,r){var{parser:n,funcName:i}=t,a=ir(e[0],"color-token").color,s=ir(e[1],"color-token").color,l=e[2];return{type:"enclose",mode:n.mode,label:i,backgroundColor:s,borderColor:a,body:l}},htmlBuilder:G7,mathmlBuilder:$7});vt({type:"enclose",names:["\\fbox"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!0},handler(t,e){var{parser:r}=t;return{type:"enclose",mode:r.mode,label:"\\fbox",body:e[0]}}});vt({type:"enclose",names:["\\cancel","\\bcancel","\\xcancel","\\sout","\\phase"],props:{numArgs:1},handler(t,e){var{parser:r,funcName:n}=t,i=e[0];return{type:"enclose",mode:r.mode,label:n,body:i}},htmlBuilder:G7,mathmlBuilder:$7});vt({type:"enclose",names:["\\angl"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!1},handler(t,e){var{parser:r}=t;return{type:"enclose",mode:r.mode,label:"\\angl",body:e[0]}}});a$={};o(ec,"defineEnvironment");s$={};o(le,"defineMacro");o(hG,"getHLines");C4=o(t=>{var e=t.parser.settings;if(!e.displayMode)throw new nt("{"+t.envName+"} can be used only in display mode.")},"validateAmsEnvironmentContext");o(V7,"getAutoTag");o(mh,"parseArray");o(U7,"dCellStyle");tc=o(function(e,r){var n,i,a=e.body.length,s=e.hLinesBeforeRow,l=0,u=new Array(a),h=[],f=Math.max(r.fontMetrics().arrayRuleWidth,r.minRuleThickness),d=1/r.fontMetrics().ptPerEm,p=5*d;if(e.colSeparationType&&e.colSeparationType==="small"){var m=r.havingStyle(Ht.SCRIPT).sizeMultiplier;p=.2778*(m/r.sizeMultiplier)}var g=e.colSeparationType==="CD"?Hn({number:3,unit:"ex"},r):12*d,y=3*d,v=e.arraystretch*g,x=.7*v,b=.3*v,w=0;function S(ke){for(var Ie=0;Ie0&&(w+=.25),h.push({pos:w,isDashed:ke[Ie]})}for(o(S,"setHLinePos"),S(s[0]),n=0;n0&&(N+=b,_ke))for(n=0;n=l)){var J=void 0;(i>0||e.hskipBeforeAndAfter)&&(J=Vt.deflt(Y.pregap,p),J!==0&&(O=Be.makeSpan(["arraycolsep"],[]),O.style.width=ct(J),C.push(O)));var Z=[];for(n=0;n0){for(var se=Be.makeLineSpan("hline",r,f),ce=Be.makeLineSpan("hdashline",r,f),ue=[{type:"elem",elem:u,shift:0}];h.length>0;){var te=h.pop(),De=te.pos-k;te.isDashed?ue.push({type:"elem",elem:ce,shift:De}):ue.push({type:"elem",elem:se,shift:De})}u=Be.makeVList({positionType:"individualShift",children:ue},r)}if(P.length===0)return Be.makeSpan(["mord"],[u],r);var oe=Be.makeVList({positionType:"individualShift",children:P},r);return oe=Be.makeSpan(["tag"],[oe],r),Be.makeFragment([u,oe])},"htmlBuilder"),sbe={c:"center ",l:"left ",r:"right "},rc=o(function(e,r){for(var n=[],i=new et.MathNode("mtd",[],["mtr-glue"]),a=new et.MathNode("mtd",[],["mml-eqn-num"]),s=0;s0){var g=e.cols,y="",v=!1,x=0,b=g.length;g[0].type==="separator"&&(p+="top ",x=1),g[g.length-1].type==="separator"&&(p+="bottom ",b-=1);for(var w=x;w0?"left ":"",p+=A[A.length-1].length>0?"right ":"";for(var L=1;L-1?"alignat":"align",a=e.envName==="split",s=mh(e.parser,{cols:n,addJot:!0,autoTag:a?void 0:V7(e.envName),emptySingleRow:!0,colSeparationType:i,maxNumCols:a?2:void 0,leqno:e.parser.settings.leqno},"display"),l,u=0,h={type:"ordgroup",mode:e.mode,body:[]};if(r[0]&&r[0].type==="ordgroup"){for(var f="",d=0;d0&&m&&(v=1),n[g]={type:"align",align:y,pregap:v,postgap:0}}return s.colSeparationType=m?"align":"alignat",s},"alignedHandler");ec({type:"array",names:["array","darray"],props:{numArgs:1},handler(t,e){var r=T4(e[0]),n=r?[e[0]]:ir(e[0],"ordgroup").body,i=n.map(function(s){var l=B7(s),u=l.text;if("lcr".indexOf(u)!==-1)return{type:"align",align:u};if(u==="|")return{type:"separator",separator:"|"};if(u===":")return{type:"separator",separator:":"};throw new nt("Unknown column alignment: "+u,s)}),a={cols:i,hskipBeforeAndAfter:!0,maxNumCols:i.length};return mh(t.parser,a,U7(t.envName))},htmlBuilder:tc,mathmlBuilder:rc});ec({type:"array",names:["matrix","pmatrix","bmatrix","Bmatrix","vmatrix","Vmatrix","matrix*","pmatrix*","bmatrix*","Bmatrix*","vmatrix*","Vmatrix*"],props:{numArgs:0},handler(t){var e={matrix:null,pmatrix:["(",")"],bmatrix:["[","]"],Bmatrix:["\\{","\\}"],vmatrix:["|","|"],Vmatrix:["\\Vert","\\Vert"]}[t.envName.replace("*","")],r="c",n={hskipBeforeAndAfter:!1,cols:[{type:"align",align:r}]};if(t.envName.charAt(t.envName.length-1)==="*"){var i=t.parser;if(i.consumeSpaces(),i.fetch().text==="["){if(i.consume(),i.consumeSpaces(),r=i.fetch().text,"lcr".indexOf(r)===-1)throw new nt("Expected l or c or r",i.nextToken);i.consume(),i.consumeSpaces(),i.expect("]"),i.consume(),n.cols=[{type:"align",align:r}]}}var a=mh(t.parser,n,U7(t.envName)),s=Math.max(0,...a.body.map(l=>l.length));return a.cols=new Array(s).fill({type:"align",align:r}),e?{type:"leftright",mode:t.mode,body:[a],left:e[0],right:e[1],rightColor:void 0}:a},htmlBuilder:tc,mathmlBuilder:rc});ec({type:"array",names:["smallmatrix"],props:{numArgs:0},handler(t){var e={arraystretch:.5},r=mh(t.parser,e,"script");return r.colSeparationType="small",r},htmlBuilder:tc,mathmlBuilder:rc});ec({type:"array",names:["subarray"],props:{numArgs:1},handler(t,e){var r=T4(e[0]),n=r?[e[0]]:ir(e[0],"ordgroup").body,i=n.map(function(s){var l=B7(s),u=l.text;if("lc".indexOf(u)!==-1)return{type:"align",align:u};throw new nt("Unknown column alignment: "+u,s)});if(i.length>1)throw new nt("{subarray} can contain only one column");var a={cols:i,hskipBeforeAndAfter:!1,arraystretch:.5};if(a=mh(t.parser,a,"script"),a.body.length>0&&a.body[0].length>1)throw new nt("{subarray} can contain only one column");return a},htmlBuilder:tc,mathmlBuilder:rc});ec({type:"array",names:["cases","dcases","rcases","drcases"],props:{numArgs:0},handler(t){var e={arraystretch:1.2,cols:[{type:"align",align:"l",pregap:0,postgap:1},{type:"align",align:"l",pregap:0,postgap:0}]},r=mh(t.parser,e,U7(t.envName));return{type:"leftright",mode:t.mode,body:[r],left:t.envName.indexOf("r")>-1?".":"\\{",right:t.envName.indexOf("r")>-1?"\\}":".",rightColor:void 0}},htmlBuilder:tc,mathmlBuilder:rc});ec({type:"array",names:["align","align*","aligned","split"],props:{numArgs:0},handler:o$,htmlBuilder:tc,mathmlBuilder:rc});ec({type:"array",names:["gathered","gather","gather*"],props:{numArgs:0},handler(t){Vt.contains(["gather","gather*"],t.envName)&&C4(t);var e={cols:[{type:"align",align:"c"}],addJot:!0,colSeparationType:"gather",autoTag:V7(t.envName),emptySingleRow:!0,leqno:t.parser.settings.leqno};return mh(t.parser,e,"display")},htmlBuilder:tc,mathmlBuilder:rc});ec({type:"array",names:["alignat","alignat*","alignedat"],props:{numArgs:1},handler:o$,htmlBuilder:tc,mathmlBuilder:rc});ec({type:"array",names:["equation","equation*"],props:{numArgs:0},handler(t){C4(t);var e={autoTag:V7(t.envName),emptySingleRow:!0,singleRow:!0,maxNumCols:1,leqno:t.parser.settings.leqno};return mh(t.parser,e,"display")},htmlBuilder:tc,mathmlBuilder:rc});ec({type:"array",names:["CD"],props:{numArgs:0},handler(t){return C4(t),Wxe(t.parser)},htmlBuilder:tc,mathmlBuilder:rc});le("\\nonumber","\\gdef\\@eqnsw{0}");le("\\notag","\\nonumber");vt({type:"text",names:["\\hline","\\hdashline"],props:{numArgs:0,allowedInText:!0,allowedInMath:!0},handler(t,e){throw new nt(t.funcName+" valid only within array environment")}});fG=a$;vt({type:"environment",names:["\\begin","\\end"],props:{numArgs:1,argTypes:["text"]},handler(t,e){var{parser:r,funcName:n}=t,i=e[0];if(i.type!=="ordgroup")throw new nt("Invalid environment name",i);for(var a="",s=0;s{var r=t.font,n=e.withFont(r);return Cr(t.body,n)},"htmlBuilder$5"),c$=o((t,e)=>{var r=t.font,n=e.withFont(r);return fn(t.body,n)},"mathmlBuilder$4"),dG={"\\Bbb":"\\mathbb","\\bold":"\\mathbf","\\frak":"\\mathfrak","\\bm":"\\boldsymbol"};vt({type:"font",names:["\\mathrm","\\mathit","\\mathbf","\\mathnormal","\\mathbb","\\mathcal","\\mathfrak","\\mathscr","\\mathsf","\\mathtt","\\Bbb","\\bold","\\frak"],props:{numArgs:1,allowedInArgument:!0},handler:o((t,e)=>{var{parser:r,funcName:n}=t,i=y4(e[0]),a=n;return a in dG&&(a=dG[a]),{type:"font",mode:r.mode,font:a.slice(1),body:i}},"handler"),htmlBuilder:l$,mathmlBuilder:c$});vt({type:"mclass",names:["\\boldsymbol","\\bm"],props:{numArgs:1},handler:o((t,e)=>{var{parser:r}=t,n=e[0],i=Vt.isCharacterBox(n);return{type:"mclass",mode:r.mode,mclass:k4(n),body:[{type:"font",mode:r.mode,font:"boldsymbol",body:n}],isCharacterBox:i}},"handler")});vt({type:"font",names:["\\rm","\\sf","\\tt","\\bf","\\it","\\cal"],props:{numArgs:0,allowedInText:!0},handler:o((t,e)=>{var{parser:r,funcName:n,breakOnTokenText:i}=t,{mode:a}=r,s=r.parseExpression(!0,i),l="math"+n.slice(1);return{type:"font",mode:a,font:l,body:{type:"ordgroup",mode:r.mode,body:s}}},"handler"),htmlBuilder:l$,mathmlBuilder:c$});u$=o((t,e)=>{var r=e;return t==="display"?r=r.id>=Ht.SCRIPT.id?r.text():Ht.DISPLAY:t==="text"&&r.size===Ht.DISPLAY.size?r=Ht.TEXT:t==="script"?r=Ht.SCRIPT:t==="scriptscript"&&(r=Ht.SCRIPTSCRIPT),r},"adjustStyle"),H7=o((t,e)=>{var r=u$(t.size,e.style),n=r.fracNum(),i=r.fracDen(),a;a=e.havingStyle(n);var s=Cr(t.numer,a,e);if(t.continued){var l=8.5/e.fontMetrics().ptPerEm,u=3.5/e.fontMetrics().ptPerEm;s.height=s.height0?g=3*p:g=7*p,y=e.fontMetrics().denom1):(d>0?(m=e.fontMetrics().num2,g=p):(m=e.fontMetrics().num3,g=3*p),y=e.fontMetrics().denom2);var v;if(f){var b=e.fontMetrics().axisHeight;m-s.depth-(b+.5*d){var r=new et.MathNode("mfrac",[fn(t.numer,e),fn(t.denom,e)]);if(!t.hasBarLine)r.setAttribute("linethickness","0px");else if(t.barSize){var n=Hn(t.barSize,e);r.setAttribute("linethickness",ct(n))}var i=u$(t.size,e.style);if(i.size!==e.style.size){r=new et.MathNode("mstyle",[r]);var a=i.size===Ht.DISPLAY.size?"true":"false";r.setAttribute("displaystyle",a),r.setAttribute("scriptlevel","0")}if(t.leftDelim!=null||t.rightDelim!=null){var s=[];if(t.leftDelim!=null){var l=new et.MathNode("mo",[new et.TextNode(t.leftDelim.replace("\\",""))]);l.setAttribute("fence","true"),s.push(l)}if(s.push(r),t.rightDelim!=null){var u=new et.MathNode("mo",[new et.TextNode(t.rightDelim.replace("\\",""))]);u.setAttribute("fence","true"),s.push(u)}return O7(s)}return r},"mathmlBuilder$3");vt({type:"genfrac",names:["\\dfrac","\\frac","\\tfrac","\\dbinom","\\binom","\\tbinom","\\\\atopfrac","\\\\bracefrac","\\\\brackfrac"],props:{numArgs:2,allowedInArgument:!0},handler:o((t,e)=>{var{parser:r,funcName:n}=t,i=e[0],a=e[1],s,l=null,u=null,h="auto";switch(n){case"\\dfrac":case"\\frac":case"\\tfrac":s=!0;break;case"\\\\atopfrac":s=!1;break;case"\\dbinom":case"\\binom":case"\\tbinom":s=!1,l="(",u=")";break;case"\\\\bracefrac":s=!1,l="\\{",u="\\}";break;case"\\\\brackfrac":s=!1,l="[",u="]";break;default:throw new Error("Unrecognized genfrac command")}switch(n){case"\\dfrac":case"\\dbinom":h="display";break;case"\\tfrac":case"\\tbinom":h="text";break}return{type:"genfrac",mode:r.mode,continued:!1,numer:i,denom:a,hasBarLine:s,leftDelim:l,rightDelim:u,size:h,barSize:null}},"handler"),htmlBuilder:H7,mathmlBuilder:Y7});vt({type:"genfrac",names:["\\cfrac"],props:{numArgs:2},handler:o((t,e)=>{var{parser:r,funcName:n}=t,i=e[0],a=e[1];return{type:"genfrac",mode:r.mode,continued:!0,numer:i,denom:a,hasBarLine:!0,leftDelim:null,rightDelim:null,size:"display",barSize:null}},"handler")});vt({type:"infix",names:["\\over","\\choose","\\atop","\\brace","\\brack"],props:{numArgs:0,infix:!0},handler(t){var{parser:e,funcName:r,token:n}=t,i;switch(r){case"\\over":i="\\frac";break;case"\\choose":i="\\binom";break;case"\\atop":i="\\\\atopfrac";break;case"\\brace":i="\\\\bracefrac";break;case"\\brack":i="\\\\brackfrac";break;default:throw new Error("Unrecognized infix genfrac command")}return{type:"infix",mode:e.mode,replaceWith:i,token:n}}});pG=["display","text","script","scriptscript"],mG=o(function(e){var r=null;return e.length>0&&(r=e,r=r==="."?null:r),r},"delimFromValue");vt({type:"genfrac",names:["\\genfrac"],props:{numArgs:6,allowedInArgument:!0,argTypes:["math","math","size","text","math","math"]},handler(t,e){var{parser:r}=t,n=e[4],i=e[5],a=y4(e[0]),s=a.type==="atom"&&a.family==="open"?mG(a.text):null,l=y4(e[1]),u=l.type==="atom"&&l.family==="close"?mG(l.text):null,h=ir(e[2],"size"),f,d=null;h.isBlank?f=!0:(d=h.value,f=d.number>0);var p="auto",m=e[3];if(m.type==="ordgroup"){if(m.body.length>0){var g=ir(m.body[0],"textord");p=pG[Number(g.text)]}}else m=ir(m,"textord"),p=pG[Number(m.text)];return{type:"genfrac",mode:r.mode,numer:n,denom:i,continued:!1,hasBarLine:f,barSize:d,leftDelim:s,rightDelim:u,size:p}},htmlBuilder:H7,mathmlBuilder:Y7});vt({type:"infix",names:["\\above"],props:{numArgs:1,argTypes:["size"],infix:!0},handler(t,e){var{parser:r,funcName:n,token:i}=t;return{type:"infix",mode:r.mode,replaceWith:"\\\\abovefrac",size:ir(e[0],"size").value,token:i}}});vt({type:"genfrac",names:["\\\\abovefrac"],props:{numArgs:3,argTypes:["math","size","math"]},handler:o((t,e)=>{var{parser:r,funcName:n}=t,i=e[0],a=z2e(ir(e[1],"infix").size),s=e[2],l=a.number>0;return{type:"genfrac",mode:r.mode,numer:i,denom:s,continued:!1,hasBarLine:l,barSize:a,leftDelim:null,rightDelim:null,size:"auto"}},"handler"),htmlBuilder:H7,mathmlBuilder:Y7});h$=o((t,e)=>{var r=e.style,n,i;t.type==="supsub"?(n=t.sup?Cr(t.sup,e.havingStyle(r.sup()),e):Cr(t.sub,e.havingStyle(r.sub()),e),i=ir(t.base,"horizBrace")):i=ir(t,"horizBrace");var a=Cr(i.base,e.havingBaseStyle(Ht.DISPLAY)),s=ou.svgSpan(i,e),l;if(i.isOver?(l=Be.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:a},{type:"kern",size:.1},{type:"elem",elem:s}]},e),l.children[0].children[0].children[1].classes.push("svg-align")):(l=Be.makeVList({positionType:"bottom",positionData:a.depth+.1+s.height,children:[{type:"elem",elem:s},{type:"kern",size:.1},{type:"elem",elem:a}]},e),l.children[0].children[0].children[0].classes.push("svg-align")),n){var u=Be.makeSpan(["mord",i.isOver?"mover":"munder"],[l],e);i.isOver?l=Be.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:u},{type:"kern",size:.2},{type:"elem",elem:n}]},e):l=Be.makeVList({positionType:"bottom",positionData:u.depth+.2+n.height+n.depth,children:[{type:"elem",elem:n},{type:"kern",size:.2},{type:"elem",elem:u}]},e)}return Be.makeSpan(["mord",i.isOver?"mover":"munder"],[l],e)},"htmlBuilder$3"),obe=o((t,e)=>{var r=ou.mathMLnode(t.label);return new et.MathNode(t.isOver?"mover":"munder",[fn(t.base,e),r])},"mathmlBuilder$2");vt({type:"horizBrace",names:["\\overbrace","\\underbrace"],props:{numArgs:1},handler(t,e){var{parser:r,funcName:n}=t;return{type:"horizBrace",mode:r.mode,label:n,isOver:/^\\over/.test(n),base:e[0]}},htmlBuilder:h$,mathmlBuilder:obe});vt({type:"href",names:["\\href"],props:{numArgs:2,argTypes:["url","original"],allowedInText:!0},handler:o((t,e)=>{var{parser:r}=t,n=e[1],i=ir(e[0],"url").url;return r.settings.isTrusted({command:"\\href",url:i})?{type:"href",mode:r.mode,href:i,body:ui(n)}:r.formatUnsupportedCmd("\\href")},"handler"),htmlBuilder:o((t,e)=>{var r=Ri(t.body,e,!1);return Be.makeAnchor(t.href,[],r,e)},"htmlBuilder"),mathmlBuilder:o((t,e)=>{var r=ph(t.body,e);return r instanceof ps||(r=new ps("mrow",[r])),r.setAttribute("href",t.href),r},"mathmlBuilder")});vt({type:"href",names:["\\url"],props:{numArgs:1,argTypes:["url"],allowedInText:!0},handler:o((t,e)=>{var{parser:r}=t,n=ir(e[0],"url").url;if(!r.settings.isTrusted({command:"\\url",url:n}))return r.formatUnsupportedCmd("\\url");for(var i=[],a=0;a{var{parser:r,funcName:n,token:i}=t,a=ir(e[0],"raw").string,s=e[1];r.settings.strict&&r.settings.reportNonstrict("htmlExtension","HTML extension is disabled on strict mode");var l,u={};switch(n){case"\\htmlClass":u.class=a,l={command:"\\htmlClass",class:a};break;case"\\htmlId":u.id=a,l={command:"\\htmlId",id:a};break;case"\\htmlStyle":u.style=a,l={command:"\\htmlStyle",style:a};break;case"\\htmlData":{for(var h=a.split(","),f=0;f{var r=Ri(t.body,e,!1),n=["enclosing"];t.attributes.class&&n.push(...t.attributes.class.trim().split(/\s+/));var i=Be.makeSpan(n,r,e);for(var a in t.attributes)a!=="class"&&t.attributes.hasOwnProperty(a)&&i.setAttribute(a,t.attributes[a]);return i},"htmlBuilder"),mathmlBuilder:o((t,e)=>ph(t.body,e),"mathmlBuilder")});vt({type:"htmlmathml",names:["\\html@mathml"],props:{numArgs:2,allowedInText:!0},handler:o((t,e)=>{var{parser:r}=t;return{type:"htmlmathml",mode:r.mode,html:ui(e[0]),mathml:ui(e[1])}},"handler"),htmlBuilder:o((t,e)=>{var r=Ri(t.html,e,!1);return Be.makeFragment(r)},"htmlBuilder"),mathmlBuilder:o((t,e)=>ph(t.mathml,e),"mathmlBuilder")});g7=o(function(e){if(/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(e))return{number:+e,unit:"bp"};var r=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(e);if(!r)throw new nt("Invalid size: '"+e+"' in \\includegraphics");var n={number:+(r[1]+r[2]),unit:r[3]};if(!DG(n))throw new nt("Invalid unit: '"+n.unit+"' in \\includegraphics.");return n},"sizeData");vt({type:"includegraphics",names:["\\includegraphics"],props:{numArgs:1,numOptionalArgs:1,argTypes:["raw","url"],allowedInText:!1},handler:o((t,e,r)=>{var{parser:n}=t,i={number:0,unit:"em"},a={number:.9,unit:"em"},s={number:0,unit:"em"},l="";if(r[0])for(var u=ir(r[0],"raw").string,h=u.split(","),f=0;f{var r=Hn(t.height,e),n=0;t.totalheight.number>0&&(n=Hn(t.totalheight,e)-r);var i=0;t.width.number>0&&(i=Hn(t.width,e));var a={height:ct(r+n)};i>0&&(a.width=ct(i)),n>0&&(a.verticalAlign=ct(-n));var s=new T7(t.src,t.alt,a);return s.height=r,s.depth=n,s},"htmlBuilder"),mathmlBuilder:o((t,e)=>{var r=new et.MathNode("mglyph",[]);r.setAttribute("alt",t.alt);var n=Hn(t.height,e),i=0;if(t.totalheight.number>0&&(i=Hn(t.totalheight,e)-n,r.setAttribute("valign",ct(-i))),r.setAttribute("height",ct(n+i)),t.width.number>0){var a=Hn(t.width,e);r.setAttribute("width",ct(a))}return r.setAttribute("src",t.src),r},"mathmlBuilder")});vt({type:"kern",names:["\\kern","\\mkern","\\hskip","\\mskip"],props:{numArgs:1,argTypes:["size"],primitive:!0,allowedInText:!0},handler(t,e){var{parser:r,funcName:n}=t,i=ir(e[0],"size");if(r.settings.strict){var a=n[1]==="m",s=i.value.unit==="mu";a?(s||r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" supports only mu units, "+("not "+i.value.unit+" units")),r.mode!=="math"&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" works only in math mode")):s&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" doesn't support mu units")}return{type:"kern",mode:r.mode,dimension:i.value}},htmlBuilder(t,e){return Be.makeGlue(t.dimension,e)},mathmlBuilder(t,e){var r=Hn(t.dimension,e);return new et.SpaceNode(r)}});vt({type:"lap",names:["\\mathllap","\\mathrlap","\\mathclap"],props:{numArgs:1,allowedInText:!0},handler:o((t,e)=>{var{parser:r,funcName:n}=t,i=e[0];return{type:"lap",mode:r.mode,alignment:n.slice(5),body:i}},"handler"),htmlBuilder:o((t,e)=>{var r;t.alignment==="clap"?(r=Be.makeSpan([],[Cr(t.body,e)]),r=Be.makeSpan(["inner"],[r],e)):r=Be.makeSpan(["inner"],[Cr(t.body,e)]);var n=Be.makeSpan(["fix"],[]),i=Be.makeSpan([t.alignment],[r,n],e),a=Be.makeSpan(["strut"]);return a.style.height=ct(i.height+i.depth),i.depth&&(a.style.verticalAlign=ct(-i.depth)),i.children.unshift(a),i=Be.makeSpan(["thinbox"],[i],e),Be.makeSpan(["mord","vbox"],[i],e)},"htmlBuilder"),mathmlBuilder:o((t,e)=>{var r=new et.MathNode("mpadded",[fn(t.body,e)]);if(t.alignment!=="rlap"){var n=t.alignment==="llap"?"-1":"-0.5";r.setAttribute("lspace",n+"width")}return r.setAttribute("width","0px"),r},"mathmlBuilder")});vt({type:"styling",names:["\\(","$"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(t,e){var{funcName:r,parser:n}=t,i=n.mode;n.switchMode("math");var a=r==="\\("?"\\)":"$",s=n.parseExpression(!1,a);return n.expect(a),n.switchMode(i),{type:"styling",mode:n.mode,style:"text",body:s}}});vt({type:"text",names:["\\)","\\]"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(t,e){throw new nt("Mismatched "+t.funcName)}});gG=o((t,e)=>{switch(e.style.size){case Ht.DISPLAY.size:return t.display;case Ht.TEXT.size:return t.text;case Ht.SCRIPT.size:return t.script;case Ht.SCRIPTSCRIPT.size:return t.scriptscript;default:return t.text}},"chooseMathStyle");vt({type:"mathchoice",names:["\\mathchoice"],props:{numArgs:4,primitive:!0},handler:o((t,e)=>{var{parser:r}=t;return{type:"mathchoice",mode:r.mode,display:ui(e[0]),text:ui(e[1]),script:ui(e[2]),scriptscript:ui(e[3])}},"handler"),htmlBuilder:o((t,e)=>{var r=gG(t,e),n=Ri(r,e,!1);return Be.makeFragment(n)},"htmlBuilder"),mathmlBuilder:o((t,e)=>{var r=gG(t,e);return ph(r,e)},"mathmlBuilder")});f$=o((t,e,r,n,i,a,s)=>{t=Be.makeSpan([],[t]);var l=r&&Vt.isCharacterBox(r),u,h;if(e){var f=Cr(e,n.havingStyle(i.sup()),n);h={elem:f,kern:Math.max(n.fontMetrics().bigOpSpacing1,n.fontMetrics().bigOpSpacing3-f.depth)}}if(r){var d=Cr(r,n.havingStyle(i.sub()),n);u={elem:d,kern:Math.max(n.fontMetrics().bigOpSpacing2,n.fontMetrics().bigOpSpacing4-d.height)}}var p;if(h&&u){var m=n.fontMetrics().bigOpSpacing5+u.elem.height+u.elem.depth+u.kern+t.depth+s;p=Be.makeVList({positionType:"bottom",positionData:m,children:[{type:"kern",size:n.fontMetrics().bigOpSpacing5},{type:"elem",elem:u.elem,marginLeft:ct(-a)},{type:"kern",size:u.kern},{type:"elem",elem:t},{type:"kern",size:h.kern},{type:"elem",elem:h.elem,marginLeft:ct(a)},{type:"kern",size:n.fontMetrics().bigOpSpacing5}]},n)}else if(u){var g=t.height-s;p=Be.makeVList({positionType:"top",positionData:g,children:[{type:"kern",size:n.fontMetrics().bigOpSpacing5},{type:"elem",elem:u.elem,marginLeft:ct(-a)},{type:"kern",size:u.kern},{type:"elem",elem:t}]},n)}else if(h){var y=t.depth+s;p=Be.makeVList({positionType:"bottom",positionData:y,children:[{type:"elem",elem:t},{type:"kern",size:h.kern},{type:"elem",elem:h.elem,marginLeft:ct(a)},{type:"kern",size:n.fontMetrics().bigOpSpacing5}]},n)}else return t;var v=[p];if(u&&a!==0&&!l){var x=Be.makeSpan(["mspace"],[],n);x.style.marginRight=ct(a),v.unshift(x)}return Be.makeSpan(["mop","op-limits"],v,n)},"assembleSupSub"),d$=["\\smallint"],xp=o((t,e)=>{var r,n,i=!1,a;t.type==="supsub"?(r=t.sup,n=t.sub,a=ir(t.base,"op"),i=!0):a=ir(t,"op");var s=e.style,l=!1;s.size===Ht.DISPLAY.size&&a.symbol&&!Vt.contains(d$,a.name)&&(l=!0);var u;if(a.symbol){var h=l?"Size2-Regular":"Size1-Regular",f="";if((a.name==="\\oiint"||a.name==="\\oiiint")&&(f=a.name.slice(1),a.name=f==="oiint"?"\\iint":"\\iiint"),u=Be.makeSymbol(a.name,h,"math",e,["mop","op-symbol",l?"large-op":"small-op"]),f.length>0){var d=u.italic,p=Be.staticSvg(f+"Size"+(l?"2":"1"),e);u=Be.makeVList({positionType:"individualShift",children:[{type:"elem",elem:u,shift:0},{type:"elem",elem:p,shift:l?.08:0}]},e),a.name="\\"+f,u.classes.unshift("mop"),u.italic=d}}else if(a.body){var m=Ri(a.body,e,!0);m.length===1&&m[0]instanceof ms?(u=m[0],u.classes[0]="mop"):u=Be.makeSpan(["mop"],m,e)}else{for(var g=[],y=1;y{var r;if(t.symbol)r=new ps("mo",[_o(t.name,t.mode)]),Vt.contains(d$,t.name)&&r.setAttribute("largeop","false");else if(t.body)r=new ps("mo",gs(t.body,e));else{r=new ps("mi",[new qf(t.name.slice(1))]);var n=new ps("mo",[_o("\u2061","text")]);t.parentIsSupSub?r=new ps("mrow",[r,n]):r=$G([r,n])}return r},"mathmlBuilder$1"),lbe={"\u220F":"\\prod","\u2210":"\\coprod","\u2211":"\\sum","\u22C0":"\\bigwedge","\u22C1":"\\bigvee","\u22C2":"\\bigcap","\u22C3":"\\bigcup","\u2A00":"\\bigodot","\u2A01":"\\bigoplus","\u2A02":"\\bigotimes","\u2A04":"\\biguplus","\u2A06":"\\bigsqcup"};vt({type:"op",names:["\\coprod","\\bigvee","\\bigwedge","\\biguplus","\\bigcap","\\bigcup","\\intop","\\prod","\\sum","\\bigotimes","\\bigoplus","\\bigodot","\\bigsqcup","\\smallint","\u220F","\u2210","\u2211","\u22C0","\u22C1","\u22C2","\u22C3","\u2A00","\u2A01","\u2A02","\u2A04","\u2A06"],props:{numArgs:0},handler:o((t,e)=>{var{parser:r,funcName:n}=t,i=n;return i.length===1&&(i=lbe[i]),{type:"op",mode:r.mode,limits:!0,parentIsSupSub:!1,symbol:!0,name:i}},"handler"),htmlBuilder:xp,mathmlBuilder:oy});vt({type:"op",names:["\\mathop"],props:{numArgs:1,primitive:!0},handler:o((t,e)=>{var{parser:r}=t,n=e[0];return{type:"op",mode:r.mode,limits:!1,parentIsSupSub:!1,symbol:!1,body:ui(n)}},"handler"),htmlBuilder:xp,mathmlBuilder:oy});cbe={"\u222B":"\\int","\u222C":"\\iint","\u222D":"\\iiint","\u222E":"\\oint","\u222F":"\\oiint","\u2230":"\\oiiint"};vt({type:"op",names:["\\arcsin","\\arccos","\\arctan","\\arctg","\\arcctg","\\arg","\\ch","\\cos","\\cosec","\\cosh","\\cot","\\cotg","\\coth","\\csc","\\ctg","\\cth","\\deg","\\dim","\\exp","\\hom","\\ker","\\lg","\\ln","\\log","\\sec","\\sin","\\sinh","\\sh","\\tan","\\tanh","\\tg","\\th"],props:{numArgs:0},handler(t){var{parser:e,funcName:r}=t;return{type:"op",mode:e.mode,limits:!1,parentIsSupSub:!1,symbol:!1,name:r}},htmlBuilder:xp,mathmlBuilder:oy});vt({type:"op",names:["\\det","\\gcd","\\inf","\\lim","\\max","\\min","\\Pr","\\sup"],props:{numArgs:0},handler(t){var{parser:e,funcName:r}=t;return{type:"op",mode:e.mode,limits:!0,parentIsSupSub:!1,symbol:!1,name:r}},htmlBuilder:xp,mathmlBuilder:oy});vt({type:"op",names:["\\int","\\iint","\\iiint","\\oint","\\oiint","\\oiiint","\u222B","\u222C","\u222D","\u222E","\u222F","\u2230"],props:{numArgs:0},handler(t){var{parser:e,funcName:r}=t,n=r;return n.length===1&&(n=cbe[n]),{type:"op",mode:e.mode,limits:!1,parentIsSupSub:!1,symbol:!0,name:n}},htmlBuilder:xp,mathmlBuilder:oy});p$=o((t,e)=>{var r,n,i=!1,a;t.type==="supsub"?(r=t.sup,n=t.sub,a=ir(t.base,"operatorname"),i=!0):a=ir(t,"operatorname");var s;if(a.body.length>0){for(var l=a.body.map(d=>{var p=d.text;return typeof p=="string"?{type:"textord",mode:d.mode,text:p}:d}),u=Ri(l,e.withFont("mathrm"),!0),h=0;h{for(var r=gs(t.body,e.withFont("mathrm")),n=!0,i=0;if.toText()).join("");r=[new et.TextNode(l)]}var u=new et.MathNode("mi",r);u.setAttribute("mathvariant","normal");var h=new et.MathNode("mo",[_o("\u2061","text")]);return t.parentIsSupSub?new et.MathNode("mrow",[u,h]):et.newDocumentFragment([u,h])},"mathmlBuilder");vt({type:"operatorname",names:["\\operatorname@","\\operatornamewithlimits"],props:{numArgs:1},handler:o((t,e)=>{var{parser:r,funcName:n}=t,i=e[0];return{type:"operatorname",mode:r.mode,body:ui(i),alwaysHandleSupSub:n==="\\operatornamewithlimits",limits:!1,parentIsSupSub:!1}},"handler"),htmlBuilder:p$,mathmlBuilder:ube});le("\\operatorname","\\@ifstar\\operatornamewithlimits\\operatorname@");Kf({type:"ordgroup",htmlBuilder(t,e){return t.semisimple?Be.makeFragment(Ri(t.body,e,!1)):Be.makeSpan(["mord"],Ri(t.body,e,!0),e)},mathmlBuilder(t,e){return ph(t.body,e,!0)}});vt({type:"overline",names:["\\overline"],props:{numArgs:1},handler(t,e){var{parser:r}=t,n=e[0];return{type:"overline",mode:r.mode,body:n}},htmlBuilder(t,e){var r=Cr(t.body,e.havingCrampedStyle()),n=Be.makeLineSpan("overline-line",e),i=e.fontMetrics().defaultRuleThickness,a=Be.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r},{type:"kern",size:3*i},{type:"elem",elem:n},{type:"kern",size:i}]},e);return Be.makeSpan(["mord","overline"],[a],e)},mathmlBuilder(t,e){var r=new et.MathNode("mo",[new et.TextNode("\u203E")]);r.setAttribute("stretchy","true");var n=new et.MathNode("mover",[fn(t.body,e),r]);return n.setAttribute("accent","true"),n}});vt({type:"phantom",names:["\\phantom"],props:{numArgs:1,allowedInText:!0},handler:o((t,e)=>{var{parser:r}=t,n=e[0];return{type:"phantom",mode:r.mode,body:ui(n)}},"handler"),htmlBuilder:o((t,e)=>{var r=Ri(t.body,e.withPhantom(),!1);return Be.makeFragment(r)},"htmlBuilder"),mathmlBuilder:o((t,e)=>{var r=gs(t.body,e);return new et.MathNode("mphantom",r)},"mathmlBuilder")});vt({type:"hphantom",names:["\\hphantom"],props:{numArgs:1,allowedInText:!0},handler:o((t,e)=>{var{parser:r}=t,n=e[0];return{type:"hphantom",mode:r.mode,body:n}},"handler"),htmlBuilder:o((t,e)=>{var r=Be.makeSpan([],[Cr(t.body,e.withPhantom())]);if(r.height=0,r.depth=0,r.children)for(var n=0;n{var r=gs(ui(t.body),e),n=new et.MathNode("mphantom",r),i=new et.MathNode("mpadded",[n]);return i.setAttribute("height","0px"),i.setAttribute("depth","0px"),i},"mathmlBuilder")});vt({type:"vphantom",names:["\\vphantom"],props:{numArgs:1,allowedInText:!0},handler:o((t,e)=>{var{parser:r}=t,n=e[0];return{type:"vphantom",mode:r.mode,body:n}},"handler"),htmlBuilder:o((t,e)=>{var r=Be.makeSpan(["inner"],[Cr(t.body,e.withPhantom())]),n=Be.makeSpan(["fix"],[]);return Be.makeSpan(["mord","rlap"],[r,n],e)},"htmlBuilder"),mathmlBuilder:o((t,e)=>{var r=gs(ui(t.body),e),n=new et.MathNode("mphantom",r),i=new et.MathNode("mpadded",[n]);return i.setAttribute("width","0px"),i},"mathmlBuilder")});vt({type:"raisebox",names:["\\raisebox"],props:{numArgs:2,argTypes:["size","hbox"],allowedInText:!0},handler(t,e){var{parser:r}=t,n=ir(e[0],"size").value,i=e[1];return{type:"raisebox",mode:r.mode,dy:n,body:i}},htmlBuilder(t,e){var r=Cr(t.body,e),n=Hn(t.dy,e);return Be.makeVList({positionType:"shift",positionData:-n,children:[{type:"elem",elem:r}]},e)},mathmlBuilder(t,e){var r=new et.MathNode("mpadded",[fn(t.body,e)]),n=t.dy.number+t.dy.unit;return r.setAttribute("voffset",n),r}});vt({type:"internal",names:["\\relax"],props:{numArgs:0,allowedInText:!0},handler(t){var{parser:e}=t;return{type:"internal",mode:e.mode}}});vt({type:"rule",names:["\\rule"],props:{numArgs:2,numOptionalArgs:1,argTypes:["size","size","size"]},handler(t,e,r){var{parser:n}=t,i=r[0],a=ir(e[0],"size"),s=ir(e[1],"size");return{type:"rule",mode:n.mode,shift:i&&ir(i,"size").value,width:a.value,height:s.value}},htmlBuilder(t,e){var r=Be.makeSpan(["mord","rule"],[],e),n=Hn(t.width,e),i=Hn(t.height,e),a=t.shift?Hn(t.shift,e):0;return r.style.borderRightWidth=ct(n),r.style.borderTopWidth=ct(i),r.style.bottom=ct(a),r.width=n,r.height=i+a,r.depth=-a,r.maxFontSize=i*1.125*e.sizeMultiplier,r},mathmlBuilder(t,e){var r=Hn(t.width,e),n=Hn(t.height,e),i=t.shift?Hn(t.shift,e):0,a=e.color&&e.getColor()||"black",s=new et.MathNode("mspace");s.setAttribute("mathbackground",a),s.setAttribute("width",ct(r)),s.setAttribute("height",ct(n));var l=new et.MathNode("mpadded",[s]);return i>=0?l.setAttribute("height",ct(i)):(l.setAttribute("height",ct(i)),l.setAttribute("depth",ct(-i))),l.setAttribute("voffset",ct(i)),l}});o(m$,"sizingGroup");yG=["\\tiny","\\sixptsize","\\scriptsize","\\footnotesize","\\small","\\normalsize","\\large","\\Large","\\LARGE","\\huge","\\Huge"],hbe=o((t,e)=>{var r=e.havingSize(t.size);return m$(t.body,r,e)},"htmlBuilder");vt({type:"sizing",names:yG,props:{numArgs:0,allowedInText:!0},handler:o((t,e)=>{var{breakOnTokenText:r,funcName:n,parser:i}=t,a=i.parseExpression(!1,r);return{type:"sizing",mode:i.mode,size:yG.indexOf(n)+1,body:a}},"handler"),htmlBuilder:hbe,mathmlBuilder:o((t,e)=>{var r=e.havingSize(t.size),n=gs(t.body,r),i=new et.MathNode("mstyle",n);return i.setAttribute("mathsize",ct(r.sizeMultiplier)),i},"mathmlBuilder")});vt({type:"smash",names:["\\smash"],props:{numArgs:1,numOptionalArgs:1,allowedInText:!0},handler:o((t,e,r)=>{var{parser:n}=t,i=!1,a=!1,s=r[0]&&ir(r[0],"ordgroup");if(s)for(var l="",u=0;u{var r=Be.makeSpan([],[Cr(t.body,e)]);if(!t.smashHeight&&!t.smashDepth)return r;if(t.smashHeight&&(r.height=0,r.children))for(var n=0;n{var r=new et.MathNode("mpadded",[fn(t.body,e)]);return t.smashHeight&&r.setAttribute("height","0px"),t.smashDepth&&r.setAttribute("depth","0px"),r},"mathmlBuilder")});vt({type:"sqrt",names:["\\sqrt"],props:{numArgs:1,numOptionalArgs:1},handler(t,e,r){var{parser:n}=t,i=r[0],a=e[0];return{type:"sqrt",mode:n.mode,body:a,index:i}},htmlBuilder(t,e){var r=Cr(t.body,e.havingCrampedStyle());r.height===0&&(r.height=e.fontMetrics().xHeight),r=Be.wrapFragment(r,e);var n=e.fontMetrics(),i=n.defaultRuleThickness,a=i;e.style.idr.height+r.depth+s&&(s=(s+d-r.height-r.depth)/2);var p=u.height-r.height-s-h;r.style.paddingLeft=ct(f);var m=Be.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r,wrapperClasses:["svg-align"]},{type:"kern",size:-(r.height+p)},{type:"elem",elem:u},{type:"kern",size:h}]},e);if(t.index){var g=e.havingStyle(Ht.SCRIPTSCRIPT),y=Cr(t.index,g,e),v=.6*(m.height-m.depth),x=Be.makeVList({positionType:"shift",positionData:-v,children:[{type:"elem",elem:y}]},e),b=Be.makeSpan(["root"],[x]);return Be.makeSpan(["mord","sqrt"],[b,m],e)}else return Be.makeSpan(["mord","sqrt"],[m],e)},mathmlBuilder(t,e){var{body:r,index:n}=t;return n?new et.MathNode("mroot",[fn(r,e),fn(n,e)]):new et.MathNode("msqrt",[fn(r,e)])}});vG={display:Ht.DISPLAY,text:Ht.TEXT,script:Ht.SCRIPT,scriptscript:Ht.SCRIPTSCRIPT};vt({type:"styling",names:["\\displaystyle","\\textstyle","\\scriptstyle","\\scriptscriptstyle"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(t,e){var{breakOnTokenText:r,funcName:n,parser:i}=t,a=i.parseExpression(!0,r),s=n.slice(1,n.length-5);return{type:"styling",mode:i.mode,style:s,body:a}},htmlBuilder(t,e){var r=vG[t.style],n=e.havingStyle(r).withFont("");return m$(t.body,n,e)},mathmlBuilder(t,e){var r=vG[t.style],n=e.havingStyle(r),i=gs(t.body,n),a=new et.MathNode("mstyle",i),s={display:["0","true"],text:["0","false"],script:["1","false"],scriptscript:["2","false"]},l=s[t.style];return a.setAttribute("scriptlevel",l[0]),a.setAttribute("displaystyle",l[1]),a}});fbe=o(function(e,r){var n=e.base;if(n)if(n.type==="op"){var i=n.limits&&(r.style.size===Ht.DISPLAY.size||n.alwaysHandleSupSub);return i?xp:null}else if(n.type==="operatorname"){var a=n.alwaysHandleSupSub&&(r.style.size===Ht.DISPLAY.size||n.limits);return a?p$:null}else{if(n.type==="accent")return Vt.isCharacterBox(n.base)?F7:null;if(n.type==="horizBrace"){var s=!e.sub;return s===n.isOver?h$:null}else return null}else return null},"htmlBuilderDelegate");Kf({type:"supsub",htmlBuilder(t,e){var r=fbe(t,e);if(r)return r(t,e);var{base:n,sup:i,sub:a}=t,s=Cr(n,e),l,u,h=e.fontMetrics(),f=0,d=0,p=n&&Vt.isCharacterBox(n);if(i){var m=e.havingStyle(e.style.sup());l=Cr(i,m,e),p||(f=s.height-m.fontMetrics().supDrop*m.sizeMultiplier/e.sizeMultiplier)}if(a){var g=e.havingStyle(e.style.sub());u=Cr(a,g,e),p||(d=s.depth+g.fontMetrics().subDrop*g.sizeMultiplier/e.sizeMultiplier)}var y;e.style===Ht.DISPLAY?y=h.sup1:e.style.cramped?y=h.sup3:y=h.sup2;var v=e.sizeMultiplier,x=ct(.5/h.ptPerEm/v),b=null;if(u){var w=t.base&&t.base.type==="op"&&t.base.name&&(t.base.name==="\\oiint"||t.base.name==="\\oiiint");(s instanceof ms||w)&&(b=ct(-s.italic))}var S;if(l&&u){f=Math.max(f,y,l.depth+.25*h.xHeight),d=Math.max(d,h.sub2);var T=h.defaultRuleThickness,E=4*T;if(f-l.depth-(u.height-d)0&&(f+=_,d-=_)}var A=[{type:"elem",elem:u,shift:d,marginRight:x,marginLeft:b},{type:"elem",elem:l,shift:-f,marginRight:x}];S=Be.makeVList({positionType:"individualShift",children:A},e)}else if(u){d=Math.max(d,h.sub1,u.height-.8*h.xHeight);var L=[{type:"elem",elem:u,marginLeft:b,marginRight:x}];S=Be.makeVList({positionType:"shift",positionData:d,children:L},e)}else if(l)f=Math.max(f,y,l.depth+.25*h.xHeight),S=Be.makeVList({positionType:"shift",positionData:-f,children:[{type:"elem",elem:l,marginRight:x}]},e);else throw new Error("supsub must have either sup or sub.");var M=E7(s,"right")||"mord";return Be.makeSpan([M],[s,Be.makeSpan(["msupsub"],[S])],e)},mathmlBuilder(t,e){var r=!1,n,i;t.base&&t.base.type==="horizBrace"&&(i=!!t.sup,i===t.base.isOver&&(r=!0,n=t.base.isOver)),t.base&&(t.base.type==="op"||t.base.type==="operatorname")&&(t.base.parentIsSupSub=!0);var a=[fn(t.base,e)];t.sub&&a.push(fn(t.sub,e)),t.sup&&a.push(fn(t.sup,e));var s;if(r)s=n?"mover":"munder";else if(t.sub)if(t.sup){var h=t.base;h&&h.type==="op"&&h.limits&&e.style===Ht.DISPLAY||h&&h.type==="operatorname"&&h.alwaysHandleSupSub&&(e.style===Ht.DISPLAY||h.limits)?s="munderover":s="msubsup"}else{var u=t.base;u&&u.type==="op"&&u.limits&&(e.style===Ht.DISPLAY||u.alwaysHandleSupSub)||u&&u.type==="operatorname"&&u.alwaysHandleSupSub&&(u.limits||e.style===Ht.DISPLAY)?s="munder":s="msub"}else{var l=t.base;l&&l.type==="op"&&l.limits&&(e.style===Ht.DISPLAY||l.alwaysHandleSupSub)||l&&l.type==="operatorname"&&l.alwaysHandleSupSub&&(l.limits||e.style===Ht.DISPLAY)?s="mover":s="msup"}return new et.MathNode(s,a)}});Kf({type:"atom",htmlBuilder(t,e){return Be.mathsym(t.text,t.mode,e,["m"+t.family])},mathmlBuilder(t,e){var r=new et.MathNode("mo",[_o(t.text,t.mode)]);if(t.family==="bin"){var n=P7(t,e);n==="bold-italic"&&r.setAttribute("mathvariant",n)}else t.family==="punct"?r.setAttribute("separator","true"):(t.family==="open"||t.family==="close")&&r.setAttribute("stretchy","false");return r}});g$={mi:"italic",mn:"normal",mtext:"normal"};Kf({type:"mathord",htmlBuilder(t,e){return Be.makeOrd(t,e,"mathord")},mathmlBuilder(t,e){var r=new et.MathNode("mi",[_o(t.text,t.mode,e)]),n=P7(t,e)||"italic";return n!==g$[r.type]&&r.setAttribute("mathvariant",n),r}});Kf({type:"textord",htmlBuilder(t,e){return Be.makeOrd(t,e,"textord")},mathmlBuilder(t,e){var r=_o(t.text,t.mode,e),n=P7(t,e)||"normal",i;return t.mode==="text"?i=new et.MathNode("mtext",[r]):/[0-9]/.test(t.text)?i=new et.MathNode("mn",[r]):t.text==="\\prime"?i=new et.MathNode("mo",[r]):i=new et.MathNode("mi",[r]),n!==g$[i.type]&&i.setAttribute("mathvariant",n),i}});y7={"\\nobreak":"nobreak","\\allowbreak":"allowbreak"},v7={" ":{},"\\ ":{},"~":{className:"nobreak"},"\\space":{},"\\nobreakspace":{className:"nobreak"}};Kf({type:"spacing",htmlBuilder(t,e){if(v7.hasOwnProperty(t.text)){var r=v7[t.text].className||"";if(t.mode==="text"){var n=Be.makeOrd(t,e,"textord");return n.classes.push(r),n}else return Be.makeSpan(["mspace",r],[Be.mathsym(t.text,t.mode,e)],e)}else{if(y7.hasOwnProperty(t.text))return Be.makeSpan(["mspace",y7[t.text]],[],e);throw new nt('Unknown type of space "'+t.text+'"')}},mathmlBuilder(t,e){var r;if(v7.hasOwnProperty(t.text))r=new et.MathNode("mtext",[new et.TextNode("\xA0")]);else{if(y7.hasOwnProperty(t.text))return new et.MathNode("mspace");throw new nt('Unknown type of space "'+t.text+'"')}return r}});xG=o(()=>{var t=new et.MathNode("mtd",[]);return t.setAttribute("width","50%"),t},"pad");Kf({type:"tag",mathmlBuilder(t,e){var r=new et.MathNode("mtable",[new et.MathNode("mtr",[xG(),new et.MathNode("mtd",[ph(t.body,e)]),xG(),new et.MathNode("mtd",[ph(t.tag,e)])])]);return r.setAttribute("width","100%"),r}});bG={"\\text":void 0,"\\textrm":"textrm","\\textsf":"textsf","\\texttt":"texttt","\\textnormal":"textrm"},wG={"\\textbf":"textbf","\\textmd":"textmd"},dbe={"\\textit":"textit","\\textup":"textup"},TG=o((t,e)=>{var r=t.font;if(r){if(bG[r])return e.withTextFontFamily(bG[r]);if(wG[r])return e.withTextFontWeight(wG[r]);if(r==="\\emph")return e.fontShape==="textit"?e.withTextFontShape("textup"):e.withTextFontShape("textit")}else return e;return e.withTextFontShape(dbe[r])},"optionsWithFont");vt({type:"text",names:["\\text","\\textrm","\\textsf","\\texttt","\\textnormal","\\textbf","\\textmd","\\textit","\\textup","\\emph"],props:{numArgs:1,argTypes:["text"],allowedInArgument:!0,allowedInText:!0},handler(t,e){var{parser:r,funcName:n}=t,i=e[0];return{type:"text",mode:r.mode,body:ui(i),font:n}},htmlBuilder(t,e){var r=TG(t,e),n=Ri(t.body,r,!0);return Be.makeSpan(["mord","text"],n,r)},mathmlBuilder(t,e){var r=TG(t,e);return ph(t.body,r)}});vt({type:"underline",names:["\\underline"],props:{numArgs:1,allowedInText:!0},handler(t,e){var{parser:r}=t;return{type:"underline",mode:r.mode,body:e[0]}},htmlBuilder(t,e){var r=Cr(t.body,e),n=Be.makeLineSpan("underline-line",e),i=e.fontMetrics().defaultRuleThickness,a=Be.makeVList({positionType:"top",positionData:r.height,children:[{type:"kern",size:i},{type:"elem",elem:n},{type:"kern",size:3*i},{type:"elem",elem:r}]},e);return Be.makeSpan(["mord","underline"],[a],e)},mathmlBuilder(t,e){var r=new et.MathNode("mo",[new et.TextNode("\u203E")]);r.setAttribute("stretchy","true");var n=new et.MathNode("munder",[fn(t.body,e),r]);return n.setAttribute("accentunder","true"),n}});vt({type:"vcenter",names:["\\vcenter"],props:{numArgs:1,argTypes:["original"],allowedInText:!1},handler(t,e){var{parser:r}=t;return{type:"vcenter",mode:r.mode,body:e[0]}},htmlBuilder(t,e){var r=Cr(t.body,e),n=e.fontMetrics().axisHeight,i=.5*(r.height-n-(r.depth+n));return Be.makeVList({positionType:"shift",positionData:i,children:[{type:"elem",elem:r}]},e)},mathmlBuilder(t,e){return new et.MathNode("mpadded",[fn(t.body,e)],["vcenter"])}});vt({type:"verb",names:["\\verb"],props:{numArgs:0,allowedInText:!0},handler(t,e,r){throw new nt("\\verb ended by end of line instead of matching delimiter")},htmlBuilder(t,e){for(var r=kG(t),n=[],i=e.havingStyle(e.style.text()),a=0;at.body.replace(/ /g,t.star?"\u2423":"\xA0"),"makeVerb"),fh=zG,y$=`[ \r + ]`,pbe="\\\\[a-zA-Z@]+",mbe="\\\\[^\uD800-\uDFFF]",gbe="("+pbe+")"+y$+"*",ybe=`\\\\( +|[ \r ]+ +?)[ \r ]*`,L7="[\u0300-\u036F]",vbe=new RegExp(L7+"+$"),xbe="("+y$+"+)|"+(ybe+"|")+"([!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]"+(L7+"*")+"|[\uD800-\uDBFF][\uDC00-\uDFFF]"+(L7+"*")+"|\\\\verb\\*([^]).*?\\4|\\\\verb([^*a-zA-Z]).*?\\5"+("|"+gbe)+("|"+mbe+")"),v4=class{static{o(this,"Lexer")}constructor(e,r){this.input=void 0,this.settings=void 0,this.tokenRegex=void 0,this.catcodes=void 0,this.input=e,this.settings=r,this.tokenRegex=new RegExp(xbe,"g"),this.catcodes={"%":14,"~":13}}setCatcode(e,r){this.catcodes[e]=r}lex(){var e=this.input,r=this.tokenRegex.lastIndex;if(r===e.length)return new Ao("EOF",new Xs(this,r,r));var n=this.tokenRegex.exec(e);if(n===null||n.index!==r)throw new nt("Unexpected character: '"+e[r]+"'",new Ao(e[r],new Xs(this,r,r+1)));var i=n[6]||n[3]||(n[2]?"\\ ":" ");if(this.catcodes[i]===14){var a=e.indexOf(` +`,this.tokenRegex.lastIndex);return a===-1?(this.tokenRegex.lastIndex=e.length,this.settings.reportNonstrict("commentAtEnd","% comment has no terminating newline; LaTeX would fail because of commenting the end of math mode (e.g. $)")):this.tokenRegex.lastIndex=a+1,this.lex()}return new Ao(i,new Xs(this,r,this.tokenRegex.lastIndex))}},D7=class{static{o(this,"Namespace")}constructor(e,r){e===void 0&&(e={}),r===void 0&&(r={}),this.current=void 0,this.builtins=void 0,this.undefStack=void 0,this.current=r,this.builtins=e,this.undefStack=[]}beginGroup(){this.undefStack.push({})}endGroup(){if(this.undefStack.length===0)throw new nt("Unbalanced namespace destruction: attempt to pop global namespace; please report this as a bug");var e=this.undefStack.pop();for(var r in e)e.hasOwnProperty(r)&&(e[r]==null?delete this.current[r]:this.current[r]=e[r])}endGroups(){for(;this.undefStack.length>0;)this.endGroup()}has(e){return this.current.hasOwnProperty(e)||this.builtins.hasOwnProperty(e)}get(e){return this.current.hasOwnProperty(e)?this.current[e]:this.builtins[e]}set(e,r,n){if(n===void 0&&(n=!1),n){for(var i=0;i0&&(this.undefStack[this.undefStack.length-1][e]=r)}else{var a=this.undefStack[this.undefStack.length-1];a&&!a.hasOwnProperty(e)&&(a[e]=this.current[e])}r==null?delete this.current[e]:this.current[e]=r}},bbe=s$;le("\\noexpand",function(t){var e=t.popToken();return t.isExpandable(e.text)&&(e.noexpand=!0,e.treatAsRelax=!0),{tokens:[e],numArgs:0}});le("\\expandafter",function(t){var e=t.popToken();return t.expandOnce(!0),{tokens:[e],numArgs:0}});le("\\@firstoftwo",function(t){var e=t.consumeArgs(2);return{tokens:e[0],numArgs:0}});le("\\@secondoftwo",function(t){var e=t.consumeArgs(2);return{tokens:e[1],numArgs:0}});le("\\@ifnextchar",function(t){var e=t.consumeArgs(3);t.consumeSpaces();var r=t.future();return e[0].length===1&&e[0][0].text===r.text?{tokens:e[1],numArgs:0}:{tokens:e[2],numArgs:0}});le("\\@ifstar","\\@ifnextchar *{\\@firstoftwo{#1}}");le("\\TextOrMath",function(t){var e=t.consumeArgs(2);return t.mode==="text"?{tokens:e[0],numArgs:0}:{tokens:e[1],numArgs:0}});EG={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,a:10,A:10,b:11,B:11,c:12,C:12,d:13,D:13,e:14,E:14,f:15,F:15};le("\\char",function(t){var e=t.popToken(),r,n="";if(e.text==="'")r=8,e=t.popToken();else if(e.text==='"')r=16,e=t.popToken();else if(e.text==="`")if(e=t.popToken(),e.text[0]==="\\")n=e.text.charCodeAt(1);else{if(e.text==="EOF")throw new nt("\\char` missing argument");n=e.text.charCodeAt(0)}else r=10;if(r){if(n=EG[e.text],n==null||n>=r)throw new nt("Invalid base-"+r+" digit "+e.text);for(var i;(i=EG[t.future().text])!=null&&i{var n=t.consumeArg().tokens;if(n.length!==1)throw new nt("\\newcommand's first argument must be a macro name");var i=n[0].text,a=t.isDefined(i);if(a&&!e)throw new nt("\\newcommand{"+i+"} attempting to redefine "+(i+"; use \\renewcommand"));if(!a&&!r)throw new nt("\\renewcommand{"+i+"} when command "+i+" does not yet exist; use \\newcommand");var s=0;if(n=t.consumeArg().tokens,n.length===1&&n[0].text==="["){for(var l="",u=t.expandNextToken();u.text!=="]"&&u.text!=="EOF";)l+=u.text,u=t.expandNextToken();if(!l.match(/^\s*[0-9]+\s*$/))throw new nt("Invalid number of arguments: "+l);s=parseInt(l),n=t.consumeArg().tokens}return t.macros.set(i,{tokens:n,numArgs:s}),""},"newcommand");le("\\newcommand",t=>W7(t,!1,!0));le("\\renewcommand",t=>W7(t,!0,!1));le("\\providecommand",t=>W7(t,!0,!0));le("\\message",t=>{var e=t.consumeArgs(1)[0];return console.log(e.reverse().map(r=>r.text).join("")),""});le("\\errmessage",t=>{var e=t.consumeArgs(1)[0];return console.error(e.reverse().map(r=>r.text).join("")),""});le("\\show",t=>{var e=t.popToken(),r=e.text;return console.log(e,t.macros.get(r),fh[r],wn.math[r],wn.text[r]),""});le("\\bgroup","{");le("\\egroup","}");le("~","\\nobreakspace");le("\\lq","`");le("\\rq","'");le("\\aa","\\r a");le("\\AA","\\r A");le("\\textcopyright","\\html@mathml{\\textcircled{c}}{\\char`\xA9}");le("\\copyright","\\TextOrMath{\\textcopyright}{\\text{\\textcopyright}}");le("\\textregistered","\\html@mathml{\\textcircled{\\scriptsize R}}{\\char`\xAE}");le("\u212C","\\mathscr{B}");le("\u2130","\\mathscr{E}");le("\u2131","\\mathscr{F}");le("\u210B","\\mathscr{H}");le("\u2110","\\mathscr{I}");le("\u2112","\\mathscr{L}");le("\u2133","\\mathscr{M}");le("\u211B","\\mathscr{R}");le("\u212D","\\mathfrak{C}");le("\u210C","\\mathfrak{H}");le("\u2128","\\mathfrak{Z}");le("\\Bbbk","\\Bbb{k}");le("\xB7","\\cdotp");le("\\llap","\\mathllap{\\textrm{#1}}");le("\\rlap","\\mathrlap{\\textrm{#1}}");le("\\clap","\\mathclap{\\textrm{#1}}");le("\\mathstrut","\\vphantom{(}");le("\\underbar","\\underline{\\text{#1}}");le("\\not",'\\html@mathml{\\mathrel{\\mathrlap\\@not}}{\\char"338}');le("\\neq","\\html@mathml{\\mathrel{\\not=}}{\\mathrel{\\char`\u2260}}");le("\\ne","\\neq");le("\u2260","\\neq");le("\\notin","\\html@mathml{\\mathrel{{\\in}\\mathllap{/\\mskip1mu}}}{\\mathrel{\\char`\u2209}}");le("\u2209","\\notin");le("\u2258","\\html@mathml{\\mathrel{=\\kern{-1em}\\raisebox{0.4em}{$\\scriptsize\\frown$}}}{\\mathrel{\\char`\u2258}}");le("\u2259","\\html@mathml{\\stackrel{\\tiny\\wedge}{=}}{\\mathrel{\\char`\u2258}}");le("\u225A","\\html@mathml{\\stackrel{\\tiny\\vee}{=}}{\\mathrel{\\char`\u225A}}");le("\u225B","\\html@mathml{\\stackrel{\\scriptsize\\star}{=}}{\\mathrel{\\char`\u225B}}");le("\u225D","\\html@mathml{\\stackrel{\\tiny\\mathrm{def}}{=}}{\\mathrel{\\char`\u225D}}");le("\u225E","\\html@mathml{\\stackrel{\\tiny\\mathrm{m}}{=}}{\\mathrel{\\char`\u225E}}");le("\u225F","\\html@mathml{\\stackrel{\\tiny?}{=}}{\\mathrel{\\char`\u225F}}");le("\u27C2","\\perp");le("\u203C","\\mathclose{!\\mkern-0.8mu!}");le("\u220C","\\notni");le("\u231C","\\ulcorner");le("\u231D","\\urcorner");le("\u231E","\\llcorner");le("\u231F","\\lrcorner");le("\xA9","\\copyright");le("\xAE","\\textregistered");le("\uFE0F","\\textregistered");le("\\ulcorner",'\\html@mathml{\\@ulcorner}{\\mathop{\\char"231c}}');le("\\urcorner",'\\html@mathml{\\@urcorner}{\\mathop{\\char"231d}}');le("\\llcorner",'\\html@mathml{\\@llcorner}{\\mathop{\\char"231e}}');le("\\lrcorner",'\\html@mathml{\\@lrcorner}{\\mathop{\\char"231f}}');le("\\vdots","\\mathord{\\varvdots\\rule{0pt}{15pt}}");le("\u22EE","\\vdots");le("\\varGamma","\\mathit{\\Gamma}");le("\\varDelta","\\mathit{\\Delta}");le("\\varTheta","\\mathit{\\Theta}");le("\\varLambda","\\mathit{\\Lambda}");le("\\varXi","\\mathit{\\Xi}");le("\\varPi","\\mathit{\\Pi}");le("\\varSigma","\\mathit{\\Sigma}");le("\\varUpsilon","\\mathit{\\Upsilon}");le("\\varPhi","\\mathit{\\Phi}");le("\\varPsi","\\mathit{\\Psi}");le("\\varOmega","\\mathit{\\Omega}");le("\\substack","\\begin{subarray}{c}#1\\end{subarray}");le("\\colon","\\nobreak\\mskip2mu\\mathpunct{}\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu\\relax");le("\\boxed","\\fbox{$\\displaystyle{#1}$}");le("\\iff","\\DOTSB\\;\\Longleftrightarrow\\;");le("\\implies","\\DOTSB\\;\\Longrightarrow\\;");le("\\impliedby","\\DOTSB\\;\\Longleftarrow\\;");CG={",":"\\dotsc","\\not":"\\dotsb","+":"\\dotsb","=":"\\dotsb","<":"\\dotsb",">":"\\dotsb","-":"\\dotsb","*":"\\dotsb",":":"\\dotsb","\\DOTSB":"\\dotsb","\\coprod":"\\dotsb","\\bigvee":"\\dotsb","\\bigwedge":"\\dotsb","\\biguplus":"\\dotsb","\\bigcap":"\\dotsb","\\bigcup":"\\dotsb","\\prod":"\\dotsb","\\sum":"\\dotsb","\\bigotimes":"\\dotsb","\\bigoplus":"\\dotsb","\\bigodot":"\\dotsb","\\bigsqcup":"\\dotsb","\\And":"\\dotsb","\\longrightarrow":"\\dotsb","\\Longrightarrow":"\\dotsb","\\longleftarrow":"\\dotsb","\\Longleftarrow":"\\dotsb","\\longleftrightarrow":"\\dotsb","\\Longleftrightarrow":"\\dotsb","\\mapsto":"\\dotsb","\\longmapsto":"\\dotsb","\\hookrightarrow":"\\dotsb","\\doteq":"\\dotsb","\\mathbin":"\\dotsb","\\mathrel":"\\dotsb","\\relbar":"\\dotsb","\\Relbar":"\\dotsb","\\xrightarrow":"\\dotsb","\\xleftarrow":"\\dotsb","\\DOTSI":"\\dotsi","\\int":"\\dotsi","\\oint":"\\dotsi","\\iint":"\\dotsi","\\iiint":"\\dotsi","\\iiiint":"\\dotsi","\\idotsint":"\\dotsi","\\DOTSX":"\\dotsx"};le("\\dots",function(t){var e="\\dotso",r=t.expandAfterFuture().text;return r in CG?e=CG[r]:(r.slice(0,4)==="\\not"||r in wn.math&&Vt.contains(["bin","rel"],wn.math[r].group))&&(e="\\dotsb"),e});q7={")":!0,"]":!0,"\\rbrack":!0,"\\}":!0,"\\rbrace":!0,"\\rangle":!0,"\\rceil":!0,"\\rfloor":!0,"\\rgroup":!0,"\\rmoustache":!0,"\\right":!0,"\\bigr":!0,"\\biggr":!0,"\\Bigr":!0,"\\Biggr":!0,$:!0,";":!0,".":!0,",":!0};le("\\dotso",function(t){var e=t.future().text;return e in q7?"\\ldots\\,":"\\ldots"});le("\\dotsc",function(t){var e=t.future().text;return e in q7&&e!==","?"\\ldots\\,":"\\ldots"});le("\\cdots",function(t){var e=t.future().text;return e in q7?"\\@cdots\\,":"\\@cdots"});le("\\dotsb","\\cdots");le("\\dotsm","\\cdots");le("\\dotsi","\\!\\cdots");le("\\dotsx","\\ldots\\,");le("\\DOTSI","\\relax");le("\\DOTSB","\\relax");le("\\DOTSX","\\relax");le("\\tmspace","\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax");le("\\,","\\tmspace+{3mu}{.1667em}");le("\\thinspace","\\,");le("\\>","\\mskip{4mu}");le("\\:","\\tmspace+{4mu}{.2222em}");le("\\medspace","\\:");le("\\;","\\tmspace+{5mu}{.2777em}");le("\\thickspace","\\;");le("\\!","\\tmspace-{3mu}{.1667em}");le("\\negthinspace","\\!");le("\\negmedspace","\\tmspace-{4mu}{.2222em}");le("\\negthickspace","\\tmspace-{5mu}{.277em}");le("\\enspace","\\kern.5em ");le("\\enskip","\\hskip.5em\\relax");le("\\quad","\\hskip1em\\relax");le("\\qquad","\\hskip2em\\relax");le("\\tag","\\@ifstar\\tag@literal\\tag@paren");le("\\tag@paren","\\tag@literal{({#1})}");le("\\tag@literal",t=>{if(t.macros.get("\\df@tag"))throw new nt("Multiple \\tag");return"\\gdef\\df@tag{\\text{#1}}"});le("\\bmod","\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}\\mathbin{\\rm mod}\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}");le("\\pod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)");le("\\pmod","\\pod{{\\rm mod}\\mkern6mu#1}");le("\\mod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}{\\rm mod}\\,\\,#1");le("\\newline","\\\\\\relax");le("\\TeX","\\textrm{\\html@mathml{T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX}{TeX}}");v$=ct(Zl["Main-Regular"][84][1]-.7*Zl["Main-Regular"][65][1]);le("\\LaTeX","\\textrm{\\html@mathml{"+("L\\kern-.36em\\raisebox{"+v$+"}{\\scriptstyle A}")+"\\kern-.15em\\TeX}{LaTeX}}");le("\\KaTeX","\\textrm{\\html@mathml{"+("K\\kern-.17em\\raisebox{"+v$+"}{\\scriptstyle A}")+"\\kern-.15em\\TeX}{KaTeX}}");le("\\hspace","\\@ifstar\\@hspacer\\@hspace");le("\\@hspace","\\hskip #1\\relax");le("\\@hspacer","\\rule{0pt}{0pt}\\hskip #1\\relax");le("\\ordinarycolon",":");le("\\vcentcolon","\\mathrel{\\mathop\\ordinarycolon}");le("\\dblcolon",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}}{\\mathop{\\char"2237}}');le("\\coloneqq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2254}}');le("\\Coloneqq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2237\\char"3d}}');le("\\coloneq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"3a\\char"2212}}');le("\\Coloneq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"2237\\char"2212}}');le("\\eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2255}}');le("\\Eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"3d\\char"2237}}');le("\\eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2239}}');le("\\Eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"2212\\char"2237}}');le("\\colonapprox",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"3a\\char"2248}}');le("\\Colonapprox",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"2237\\char"2248}}');le("\\colonsim",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"3a\\char"223c}}');le("\\Colonsim",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"2237\\char"223c}}');le("\u2237","\\dblcolon");le("\u2239","\\eqcolon");le("\u2254","\\coloneqq");le("\u2255","\\eqqcolon");le("\u2A74","\\Coloneqq");le("\\ratio","\\vcentcolon");le("\\coloncolon","\\dblcolon");le("\\colonequals","\\coloneqq");le("\\coloncolonequals","\\Coloneqq");le("\\equalscolon","\\eqqcolon");le("\\equalscoloncolon","\\Eqqcolon");le("\\colonminus","\\coloneq");le("\\coloncolonminus","\\Coloneq");le("\\minuscolon","\\eqcolon");le("\\minuscoloncolon","\\Eqcolon");le("\\coloncolonapprox","\\Colonapprox");le("\\coloncolonsim","\\Colonsim");le("\\simcolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}");le("\\simcoloncolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}");le("\\approxcolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}");le("\\approxcoloncolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}");le("\\notni","\\html@mathml{\\not\\ni}{\\mathrel{\\char`\u220C}}");le("\\limsup","\\DOTSB\\operatorname*{lim\\,sup}");le("\\liminf","\\DOTSB\\operatorname*{lim\\,inf}");le("\\injlim","\\DOTSB\\operatorname*{inj\\,lim}");le("\\projlim","\\DOTSB\\operatorname*{proj\\,lim}");le("\\varlimsup","\\DOTSB\\operatorname*{\\overline{lim}}");le("\\varliminf","\\DOTSB\\operatorname*{\\underline{lim}}");le("\\varinjlim","\\DOTSB\\operatorname*{\\underrightarrow{lim}}");le("\\varprojlim","\\DOTSB\\operatorname*{\\underleftarrow{lim}}");le("\\gvertneqq","\\html@mathml{\\@gvertneqq}{\u2269}");le("\\lvertneqq","\\html@mathml{\\@lvertneqq}{\u2268}");le("\\ngeqq","\\html@mathml{\\@ngeqq}{\u2271}");le("\\ngeqslant","\\html@mathml{\\@ngeqslant}{\u2271}");le("\\nleqq","\\html@mathml{\\@nleqq}{\u2270}");le("\\nleqslant","\\html@mathml{\\@nleqslant}{\u2270}");le("\\nshortmid","\\html@mathml{\\@nshortmid}{\u2224}");le("\\nshortparallel","\\html@mathml{\\@nshortparallel}{\u2226}");le("\\nsubseteqq","\\html@mathml{\\@nsubseteqq}{\u2288}");le("\\nsupseteqq","\\html@mathml{\\@nsupseteqq}{\u2289}");le("\\varsubsetneq","\\html@mathml{\\@varsubsetneq}{\u228A}");le("\\varsubsetneqq","\\html@mathml{\\@varsubsetneqq}{\u2ACB}");le("\\varsupsetneq","\\html@mathml{\\@varsupsetneq}{\u228B}");le("\\varsupsetneqq","\\html@mathml{\\@varsupsetneqq}{\u2ACC}");le("\\imath","\\html@mathml{\\@imath}{\u0131}");le("\\jmath","\\html@mathml{\\@jmath}{\u0237}");le("\\llbracket","\\html@mathml{\\mathopen{[\\mkern-3.2mu[}}{\\mathopen{\\char`\u27E6}}");le("\\rrbracket","\\html@mathml{\\mathclose{]\\mkern-3.2mu]}}{\\mathclose{\\char`\u27E7}}");le("\u27E6","\\llbracket");le("\u27E7","\\rrbracket");le("\\lBrace","\\html@mathml{\\mathopen{\\{\\mkern-3.2mu[}}{\\mathopen{\\char`\u2983}}");le("\\rBrace","\\html@mathml{\\mathclose{]\\mkern-3.2mu\\}}}{\\mathclose{\\char`\u2984}}");le("\u2983","\\lBrace");le("\u2984","\\rBrace");le("\\minuso","\\mathbin{\\html@mathml{{\\mathrlap{\\mathchoice{\\kern{0.145em}}{\\kern{0.145em}}{\\kern{0.1015em}}{\\kern{0.0725em}}\\circ}{-}}}{\\char`\u29B5}}");le("\u29B5","\\minuso");le("\\darr","\\downarrow");le("\\dArr","\\Downarrow");le("\\Darr","\\Downarrow");le("\\lang","\\langle");le("\\rang","\\rangle");le("\\uarr","\\uparrow");le("\\uArr","\\Uparrow");le("\\Uarr","\\Uparrow");le("\\N","\\mathbb{N}");le("\\R","\\mathbb{R}");le("\\Z","\\mathbb{Z}");le("\\alef","\\aleph");le("\\alefsym","\\aleph");le("\\Alpha","\\mathrm{A}");le("\\Beta","\\mathrm{B}");le("\\bull","\\bullet");le("\\Chi","\\mathrm{X}");le("\\clubs","\\clubsuit");le("\\cnums","\\mathbb{C}");le("\\Complex","\\mathbb{C}");le("\\Dagger","\\ddagger");le("\\diamonds","\\diamondsuit");le("\\empty","\\emptyset");le("\\Epsilon","\\mathrm{E}");le("\\Eta","\\mathrm{H}");le("\\exist","\\exists");le("\\harr","\\leftrightarrow");le("\\hArr","\\Leftrightarrow");le("\\Harr","\\Leftrightarrow");le("\\hearts","\\heartsuit");le("\\image","\\Im");le("\\infin","\\infty");le("\\Iota","\\mathrm{I}");le("\\isin","\\in");le("\\Kappa","\\mathrm{K}");le("\\larr","\\leftarrow");le("\\lArr","\\Leftarrow");le("\\Larr","\\Leftarrow");le("\\lrarr","\\leftrightarrow");le("\\lrArr","\\Leftrightarrow");le("\\Lrarr","\\Leftrightarrow");le("\\Mu","\\mathrm{M}");le("\\natnums","\\mathbb{N}");le("\\Nu","\\mathrm{N}");le("\\Omicron","\\mathrm{O}");le("\\plusmn","\\pm");le("\\rarr","\\rightarrow");le("\\rArr","\\Rightarrow");le("\\Rarr","\\Rightarrow");le("\\real","\\Re");le("\\reals","\\mathbb{R}");le("\\Reals","\\mathbb{R}");le("\\Rho","\\mathrm{P}");le("\\sdot","\\cdot");le("\\sect","\\S");le("\\spades","\\spadesuit");le("\\sub","\\subset");le("\\sube","\\subseteq");le("\\supe","\\supseteq");le("\\Tau","\\mathrm{T}");le("\\thetasym","\\vartheta");le("\\weierp","\\wp");le("\\Zeta","\\mathrm{Z}");le("\\argmin","\\DOTSB\\operatorname*{arg\\,min}");le("\\argmax","\\DOTSB\\operatorname*{arg\\,max}");le("\\plim","\\DOTSB\\mathop{\\operatorname{plim}}\\limits");le("\\bra","\\mathinner{\\langle{#1}|}");le("\\ket","\\mathinner{|{#1}\\rangle}");le("\\braket","\\mathinner{\\langle{#1}\\rangle}");le("\\Bra","\\left\\langle#1\\right|");le("\\Ket","\\left|#1\\right\\rangle");x$=o(t=>e=>{var r=e.consumeArg().tokens,n=e.consumeArg().tokens,i=e.consumeArg().tokens,a=e.consumeArg().tokens,s=e.macros.get("|"),l=e.macros.get("\\|");e.macros.beginGroup();var u=o(d=>p=>{t&&(p.macros.set("|",s),i.length&&p.macros.set("\\|",l));var m=d;if(!d&&i.length){var g=p.future();g.text==="|"&&(p.popToken(),m=!0)}return{tokens:m?i:n,numArgs:0}},"midMacro");e.macros.set("|",u(!1)),i.length&&e.macros.set("\\|",u(!0));var h=e.consumeArg().tokens,f=e.expandTokens([...a,...h,...r]);return e.macros.endGroup(),{tokens:f.reverse(),numArgs:0}},"braketHelper");le("\\bra@ket",x$(!1));le("\\bra@set",x$(!0));le("\\Braket","\\bra@ket{\\left\\langle}{\\,\\middle\\vert\\,}{\\,\\middle\\vert\\,}{\\right\\rangle}");le("\\Set","\\bra@set{\\left\\{\\:}{\\;\\middle\\vert\\;}{\\;\\middle\\Vert\\;}{\\:\\right\\}}");le("\\set","\\bra@set{\\{\\,}{\\mid}{}{\\,\\}}");le("\\angln","{\\angl n}");le("\\blue","\\textcolor{##6495ed}{#1}");le("\\orange","\\textcolor{##ffa500}{#1}");le("\\pink","\\textcolor{##ff00af}{#1}");le("\\red","\\textcolor{##df0030}{#1}");le("\\green","\\textcolor{##28ae7b}{#1}");le("\\gray","\\textcolor{gray}{#1}");le("\\purple","\\textcolor{##9d38bd}{#1}");le("\\blueA","\\textcolor{##ccfaff}{#1}");le("\\blueB","\\textcolor{##80f6ff}{#1}");le("\\blueC","\\textcolor{##63d9ea}{#1}");le("\\blueD","\\textcolor{##11accd}{#1}");le("\\blueE","\\textcolor{##0c7f99}{#1}");le("\\tealA","\\textcolor{##94fff5}{#1}");le("\\tealB","\\textcolor{##26edd5}{#1}");le("\\tealC","\\textcolor{##01d1c1}{#1}");le("\\tealD","\\textcolor{##01a995}{#1}");le("\\tealE","\\textcolor{##208170}{#1}");le("\\greenA","\\textcolor{##b6ffb0}{#1}");le("\\greenB","\\textcolor{##8af281}{#1}");le("\\greenC","\\textcolor{##74cf70}{#1}");le("\\greenD","\\textcolor{##1fab54}{#1}");le("\\greenE","\\textcolor{##0d923f}{#1}");le("\\goldA","\\textcolor{##ffd0a9}{#1}");le("\\goldB","\\textcolor{##ffbb71}{#1}");le("\\goldC","\\textcolor{##ff9c39}{#1}");le("\\goldD","\\textcolor{##e07d10}{#1}");le("\\goldE","\\textcolor{##a75a05}{#1}");le("\\redA","\\textcolor{##fca9a9}{#1}");le("\\redB","\\textcolor{##ff8482}{#1}");le("\\redC","\\textcolor{##f9685d}{#1}");le("\\redD","\\textcolor{##e84d39}{#1}");le("\\redE","\\textcolor{##bc2612}{#1}");le("\\maroonA","\\textcolor{##ffbde0}{#1}");le("\\maroonB","\\textcolor{##ff92c6}{#1}");le("\\maroonC","\\textcolor{##ed5fa6}{#1}");le("\\maroonD","\\textcolor{##ca337c}{#1}");le("\\maroonE","\\textcolor{##9e034e}{#1}");le("\\purpleA","\\textcolor{##ddd7ff}{#1}");le("\\purpleB","\\textcolor{##c6b9fc}{#1}");le("\\purpleC","\\textcolor{##aa87ff}{#1}");le("\\purpleD","\\textcolor{##7854ab}{#1}");le("\\purpleE","\\textcolor{##543b78}{#1}");le("\\mintA","\\textcolor{##f5f9e8}{#1}");le("\\mintB","\\textcolor{##edf2df}{#1}");le("\\mintC","\\textcolor{##e0e5cc}{#1}");le("\\grayA","\\textcolor{##f6f7f7}{#1}");le("\\grayB","\\textcolor{##f0f1f2}{#1}");le("\\grayC","\\textcolor{##e3e5e6}{#1}");le("\\grayD","\\textcolor{##d6d8da}{#1}");le("\\grayE","\\textcolor{##babec2}{#1}");le("\\grayF","\\textcolor{##888d93}{#1}");le("\\grayG","\\textcolor{##626569}{#1}");le("\\grayH","\\textcolor{##3b3e40}{#1}");le("\\grayI","\\textcolor{##21242c}{#1}");le("\\kaBlue","\\textcolor{##314453}{#1}");le("\\kaGreen","\\textcolor{##71B307}{#1}");b$={"^":!0,_:!0,"\\limits":!0,"\\nolimits":!0},R7=class{static{o(this,"MacroExpander")}constructor(e,r,n){this.settings=void 0,this.expansionCount=void 0,this.lexer=void 0,this.macros=void 0,this.stack=void 0,this.mode=void 0,this.settings=r,this.expansionCount=0,this.feed(e),this.macros=new D7(bbe,r.macros),this.mode=n,this.stack=[]}feed(e){this.lexer=new v4(e,this.settings)}switchMode(e){this.mode=e}beginGroup(){this.macros.beginGroup()}endGroup(){this.macros.endGroup()}endGroups(){this.macros.endGroups()}future(){return this.stack.length===0&&this.pushToken(this.lexer.lex()),this.stack[this.stack.length-1]}popToken(){return this.future(),this.stack.pop()}pushToken(e){this.stack.push(e)}pushTokens(e){this.stack.push(...e)}scanArgument(e){var r,n,i;if(e){if(this.consumeSpaces(),this.future().text!=="[")return null;r=this.popToken(),{tokens:i,end:n}=this.consumeArg(["]"])}else({tokens:i,start:r,end:n}=this.consumeArg());return this.pushToken(new Ao("EOF",n.loc)),this.pushTokens(i),r.range(n,"")}consumeSpaces(){for(;;){var e=this.future();if(e.text===" ")this.stack.pop();else break}}consumeArg(e){var r=[],n=e&&e.length>0;n||this.consumeSpaces();var i=this.future(),a,s=0,l=0;do{if(a=this.popToken(),r.push(a),a.text==="{")++s;else if(a.text==="}"){if(--s,s===-1)throw new nt("Extra }",a)}else if(a.text==="EOF")throw new nt("Unexpected end of input in a macro argument, expected '"+(e&&n?e[l]:"}")+"'",a);if(e&&n)if((s===0||s===1&&e[l]==="{")&&a.text===e[l]){if(++l,l===e.length){r.splice(-l,l);break}}else l=0}while(s!==0||n);return i.text==="{"&&r[r.length-1].text==="}"&&(r.pop(),r.shift()),r.reverse(),{tokens:r,start:i,end:a}}consumeArgs(e,r){if(r){if(r.length!==e+1)throw new nt("The length of delimiters doesn't match the number of args!");for(var n=r[0],i=0;ithis.settings.maxExpand)throw new nt("Too many expansions: infinite loop or need to increase maxExpand setting")}expandOnce(e){var r=this.popToken(),n=r.text,i=r.noexpand?null:this._getExpansion(n);if(i==null||e&&i.unexpandable){if(e&&i==null&&n[0]==="\\"&&!this.isDefined(n))throw new nt("Undefined control sequence: "+n);return this.pushToken(r),!1}this.countExpansion(1);var a=i.tokens,s=this.consumeArgs(i.numArgs,i.delimiters);if(i.numArgs){a=a.slice();for(var l=a.length-1;l>=0;--l){var u=a[l];if(u.text==="#"){if(l===0)throw new nt("Incomplete placeholder at end of macro body",u);if(u=a[--l],u.text==="#")a.splice(l+1,1);else if(/^[1-9]$/.test(u.text))a.splice(l,2,...s[+u.text-1]);else throw new nt("Not a valid argument number",u)}}}return this.pushTokens(a),a.length}expandAfterFuture(){return this.expandOnce(),this.future()}expandNextToken(){for(;;)if(this.expandOnce()===!1){var e=this.stack.pop();return e.treatAsRelax&&(e.text="\\relax"),e}throw new Error}expandMacro(e){return this.macros.has(e)?this.expandTokens([new Ao(e)]):void 0}expandTokens(e){var r=[],n=this.stack.length;for(this.pushTokens(e);this.stack.length>n;)if(this.expandOnce(!0)===!1){var i=this.stack.pop();i.treatAsRelax&&(i.noexpand=!1,i.treatAsRelax=!1),r.push(i)}return this.countExpansion(r.length),r}expandMacroAsText(e){var r=this.expandMacro(e);return r&&r.map(n=>n.text).join("")}_getExpansion(e){var r=this.macros.get(e);if(r==null)return r;if(e.length===1){var n=this.lexer.catcodes[e];if(n!=null&&n!==13)return}var i=typeof r=="function"?r(this):r;if(typeof i=="string"){var a=0;if(i.indexOf("#")!==-1)for(var s=i.replace(/##/g,"");s.indexOf("#"+(a+1))!==-1;)++a;for(var l=new v4(i,this.settings),u=[],h=l.lex();h.text!=="EOF";)u.push(h),h=l.lex();u.reverse();var f={tokens:u,numArgs:a};return f}return i}isDefined(e){return this.macros.has(e)||fh.hasOwnProperty(e)||wn.math.hasOwnProperty(e)||wn.text.hasOwnProperty(e)||b$.hasOwnProperty(e)}isExpandable(e){var r=this.macros.get(e);return r!=null?typeof r=="string"||typeof r=="function"||!r.unexpandable:fh.hasOwnProperty(e)&&!fh[e].primitive}},SG=/^[₊₋₌₍₎₀₁₂₃₄₅₆₇₈₉ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓᵦᵧᵨᵩᵪ]/,c4=Object.freeze({"\u208A":"+","\u208B":"-","\u208C":"=","\u208D":"(","\u208E":")","\u2080":"0","\u2081":"1","\u2082":"2","\u2083":"3","\u2084":"4","\u2085":"5","\u2086":"6","\u2087":"7","\u2088":"8","\u2089":"9","\u2090":"a","\u2091":"e","\u2095":"h","\u1D62":"i","\u2C7C":"j","\u2096":"k","\u2097":"l","\u2098":"m","\u2099":"n","\u2092":"o","\u209A":"p","\u1D63":"r","\u209B":"s","\u209C":"t","\u1D64":"u","\u1D65":"v","\u2093":"x","\u1D66":"\u03B2","\u1D67":"\u03B3","\u1D68":"\u03C1","\u1D69":"\u03D5","\u1D6A":"\u03C7","\u207A":"+","\u207B":"-","\u207C":"=","\u207D":"(","\u207E":")","\u2070":"0","\xB9":"1","\xB2":"2","\xB3":"3","\u2074":"4","\u2075":"5","\u2076":"6","\u2077":"7","\u2078":"8","\u2079":"9","\u1D2C":"A","\u1D2E":"B","\u1D30":"D","\u1D31":"E","\u1D33":"G","\u1D34":"H","\u1D35":"I","\u1D36":"J","\u1D37":"K","\u1D38":"L","\u1D39":"M","\u1D3A":"N","\u1D3C":"O","\u1D3E":"P","\u1D3F":"R","\u1D40":"T","\u1D41":"U","\u2C7D":"V","\u1D42":"W","\u1D43":"a","\u1D47":"b","\u1D9C":"c","\u1D48":"d","\u1D49":"e","\u1DA0":"f","\u1D4D":"g",\u02B0:"h","\u2071":"i",\u02B2:"j","\u1D4F":"k",\u02E1:"l","\u1D50":"m",\u207F:"n","\u1D52":"o","\u1D56":"p",\u02B3:"r",\u02E2:"s","\u1D57":"t","\u1D58":"u","\u1D5B":"v",\u02B7:"w",\u02E3:"x",\u02B8:"y","\u1DBB":"z","\u1D5D":"\u03B2","\u1D5E":"\u03B3","\u1D5F":"\u03B4","\u1D60":"\u03D5","\u1D61":"\u03C7","\u1DBF":"\u03B8"}),x7={"\u0301":{text:"\\'",math:"\\acute"},"\u0300":{text:"\\`",math:"\\grave"},"\u0308":{text:'\\"',math:"\\ddot"},"\u0303":{text:"\\~",math:"\\tilde"},"\u0304":{text:"\\=",math:"\\bar"},"\u0306":{text:"\\u",math:"\\breve"},"\u030C":{text:"\\v",math:"\\check"},"\u0302":{text:"\\^",math:"\\hat"},"\u0307":{text:"\\.",math:"\\dot"},"\u030A":{text:"\\r",math:"\\mathring"},"\u030B":{text:"\\H"},"\u0327":{text:"\\c"}},AG={\u00E1:"a\u0301",\u00E0:"a\u0300",\u00E4:"a\u0308",\u01DF:"a\u0308\u0304",\u00E3:"a\u0303",\u0101:"a\u0304",\u0103:"a\u0306",\u1EAF:"a\u0306\u0301",\u1EB1:"a\u0306\u0300",\u1EB5:"a\u0306\u0303",\u01CE:"a\u030C",\u00E2:"a\u0302",\u1EA5:"a\u0302\u0301",\u1EA7:"a\u0302\u0300",\u1EAB:"a\u0302\u0303",\u0227:"a\u0307",\u01E1:"a\u0307\u0304",\u00E5:"a\u030A",\u01FB:"a\u030A\u0301",\u1E03:"b\u0307",\u0107:"c\u0301",\u1E09:"c\u0327\u0301",\u010D:"c\u030C",\u0109:"c\u0302",\u010B:"c\u0307",\u00E7:"c\u0327",\u010F:"d\u030C",\u1E0B:"d\u0307",\u1E11:"d\u0327",\u00E9:"e\u0301",\u00E8:"e\u0300",\u00EB:"e\u0308",\u1EBD:"e\u0303",\u0113:"e\u0304",\u1E17:"e\u0304\u0301",\u1E15:"e\u0304\u0300",\u0115:"e\u0306",\u1E1D:"e\u0327\u0306",\u011B:"e\u030C",\u00EA:"e\u0302",\u1EBF:"e\u0302\u0301",\u1EC1:"e\u0302\u0300",\u1EC5:"e\u0302\u0303",\u0117:"e\u0307",\u0229:"e\u0327",\u1E1F:"f\u0307",\u01F5:"g\u0301",\u1E21:"g\u0304",\u011F:"g\u0306",\u01E7:"g\u030C",\u011D:"g\u0302",\u0121:"g\u0307",\u0123:"g\u0327",\u1E27:"h\u0308",\u021F:"h\u030C",\u0125:"h\u0302",\u1E23:"h\u0307",\u1E29:"h\u0327",\u00ED:"i\u0301",\u00EC:"i\u0300",\u00EF:"i\u0308",\u1E2F:"i\u0308\u0301",\u0129:"i\u0303",\u012B:"i\u0304",\u012D:"i\u0306",\u01D0:"i\u030C",\u00EE:"i\u0302",\u01F0:"j\u030C",\u0135:"j\u0302",\u1E31:"k\u0301",\u01E9:"k\u030C",\u0137:"k\u0327",\u013A:"l\u0301",\u013E:"l\u030C",\u013C:"l\u0327",\u1E3F:"m\u0301",\u1E41:"m\u0307",\u0144:"n\u0301",\u01F9:"n\u0300",\u00F1:"n\u0303",\u0148:"n\u030C",\u1E45:"n\u0307",\u0146:"n\u0327",\u00F3:"o\u0301",\u00F2:"o\u0300",\u00F6:"o\u0308",\u022B:"o\u0308\u0304",\u00F5:"o\u0303",\u1E4D:"o\u0303\u0301",\u1E4F:"o\u0303\u0308",\u022D:"o\u0303\u0304",\u014D:"o\u0304",\u1E53:"o\u0304\u0301",\u1E51:"o\u0304\u0300",\u014F:"o\u0306",\u01D2:"o\u030C",\u00F4:"o\u0302",\u1ED1:"o\u0302\u0301",\u1ED3:"o\u0302\u0300",\u1ED7:"o\u0302\u0303",\u022F:"o\u0307",\u0231:"o\u0307\u0304",\u0151:"o\u030B",\u1E55:"p\u0301",\u1E57:"p\u0307",\u0155:"r\u0301",\u0159:"r\u030C",\u1E59:"r\u0307",\u0157:"r\u0327",\u015B:"s\u0301",\u1E65:"s\u0301\u0307",\u0161:"s\u030C",\u1E67:"s\u030C\u0307",\u015D:"s\u0302",\u1E61:"s\u0307",\u015F:"s\u0327",\u1E97:"t\u0308",\u0165:"t\u030C",\u1E6B:"t\u0307",\u0163:"t\u0327",\u00FA:"u\u0301",\u00F9:"u\u0300",\u00FC:"u\u0308",\u01D8:"u\u0308\u0301",\u01DC:"u\u0308\u0300",\u01D6:"u\u0308\u0304",\u01DA:"u\u0308\u030C",\u0169:"u\u0303",\u1E79:"u\u0303\u0301",\u016B:"u\u0304",\u1E7B:"u\u0304\u0308",\u016D:"u\u0306",\u01D4:"u\u030C",\u00FB:"u\u0302",\u016F:"u\u030A",\u0171:"u\u030B",\u1E7D:"v\u0303",\u1E83:"w\u0301",\u1E81:"w\u0300",\u1E85:"w\u0308",\u0175:"w\u0302",\u1E87:"w\u0307",\u1E98:"w\u030A",\u1E8D:"x\u0308",\u1E8B:"x\u0307",\u00FD:"y\u0301",\u1EF3:"y\u0300",\u00FF:"y\u0308",\u1EF9:"y\u0303",\u0233:"y\u0304",\u0177:"y\u0302",\u1E8F:"y\u0307",\u1E99:"y\u030A",\u017A:"z\u0301",\u017E:"z\u030C",\u1E91:"z\u0302",\u017C:"z\u0307",\u00C1:"A\u0301",\u00C0:"A\u0300",\u00C4:"A\u0308",\u01DE:"A\u0308\u0304",\u00C3:"A\u0303",\u0100:"A\u0304",\u0102:"A\u0306",\u1EAE:"A\u0306\u0301",\u1EB0:"A\u0306\u0300",\u1EB4:"A\u0306\u0303",\u01CD:"A\u030C",\u00C2:"A\u0302",\u1EA4:"A\u0302\u0301",\u1EA6:"A\u0302\u0300",\u1EAA:"A\u0302\u0303",\u0226:"A\u0307",\u01E0:"A\u0307\u0304",\u00C5:"A\u030A",\u01FA:"A\u030A\u0301",\u1E02:"B\u0307",\u0106:"C\u0301",\u1E08:"C\u0327\u0301",\u010C:"C\u030C",\u0108:"C\u0302",\u010A:"C\u0307",\u00C7:"C\u0327",\u010E:"D\u030C",\u1E0A:"D\u0307",\u1E10:"D\u0327",\u00C9:"E\u0301",\u00C8:"E\u0300",\u00CB:"E\u0308",\u1EBC:"E\u0303",\u0112:"E\u0304",\u1E16:"E\u0304\u0301",\u1E14:"E\u0304\u0300",\u0114:"E\u0306",\u1E1C:"E\u0327\u0306",\u011A:"E\u030C",\u00CA:"E\u0302",\u1EBE:"E\u0302\u0301",\u1EC0:"E\u0302\u0300",\u1EC4:"E\u0302\u0303",\u0116:"E\u0307",\u0228:"E\u0327",\u1E1E:"F\u0307",\u01F4:"G\u0301",\u1E20:"G\u0304",\u011E:"G\u0306",\u01E6:"G\u030C",\u011C:"G\u0302",\u0120:"G\u0307",\u0122:"G\u0327",\u1E26:"H\u0308",\u021E:"H\u030C",\u0124:"H\u0302",\u1E22:"H\u0307",\u1E28:"H\u0327",\u00CD:"I\u0301",\u00CC:"I\u0300",\u00CF:"I\u0308",\u1E2E:"I\u0308\u0301",\u0128:"I\u0303",\u012A:"I\u0304",\u012C:"I\u0306",\u01CF:"I\u030C",\u00CE:"I\u0302",\u0130:"I\u0307",\u0134:"J\u0302",\u1E30:"K\u0301",\u01E8:"K\u030C",\u0136:"K\u0327",\u0139:"L\u0301",\u013D:"L\u030C",\u013B:"L\u0327",\u1E3E:"M\u0301",\u1E40:"M\u0307",\u0143:"N\u0301",\u01F8:"N\u0300",\u00D1:"N\u0303",\u0147:"N\u030C",\u1E44:"N\u0307",\u0145:"N\u0327",\u00D3:"O\u0301",\u00D2:"O\u0300",\u00D6:"O\u0308",\u022A:"O\u0308\u0304",\u00D5:"O\u0303",\u1E4C:"O\u0303\u0301",\u1E4E:"O\u0303\u0308",\u022C:"O\u0303\u0304",\u014C:"O\u0304",\u1E52:"O\u0304\u0301",\u1E50:"O\u0304\u0300",\u014E:"O\u0306",\u01D1:"O\u030C",\u00D4:"O\u0302",\u1ED0:"O\u0302\u0301",\u1ED2:"O\u0302\u0300",\u1ED6:"O\u0302\u0303",\u022E:"O\u0307",\u0230:"O\u0307\u0304",\u0150:"O\u030B",\u1E54:"P\u0301",\u1E56:"P\u0307",\u0154:"R\u0301",\u0158:"R\u030C",\u1E58:"R\u0307",\u0156:"R\u0327",\u015A:"S\u0301",\u1E64:"S\u0301\u0307",\u0160:"S\u030C",\u1E66:"S\u030C\u0307",\u015C:"S\u0302",\u1E60:"S\u0307",\u015E:"S\u0327",\u0164:"T\u030C",\u1E6A:"T\u0307",\u0162:"T\u0327",\u00DA:"U\u0301",\u00D9:"U\u0300",\u00DC:"U\u0308",\u01D7:"U\u0308\u0301",\u01DB:"U\u0308\u0300",\u01D5:"U\u0308\u0304",\u01D9:"U\u0308\u030C",\u0168:"U\u0303",\u1E78:"U\u0303\u0301",\u016A:"U\u0304",\u1E7A:"U\u0304\u0308",\u016C:"U\u0306",\u01D3:"U\u030C",\u00DB:"U\u0302",\u016E:"U\u030A",\u0170:"U\u030B",\u1E7C:"V\u0303",\u1E82:"W\u0301",\u1E80:"W\u0300",\u1E84:"W\u0308",\u0174:"W\u0302",\u1E86:"W\u0307",\u1E8C:"X\u0308",\u1E8A:"X\u0307",\u00DD:"Y\u0301",\u1EF2:"Y\u0300",\u0178:"Y\u0308",\u1EF8:"Y\u0303",\u0232:"Y\u0304",\u0176:"Y\u0302",\u1E8E:"Y\u0307",\u0179:"Z\u0301",\u017D:"Z\u030C",\u1E90:"Z\u0302",\u017B:"Z\u0307",\u03AC:"\u03B1\u0301",\u1F70:"\u03B1\u0300",\u1FB1:"\u03B1\u0304",\u1FB0:"\u03B1\u0306",\u03AD:"\u03B5\u0301",\u1F72:"\u03B5\u0300",\u03AE:"\u03B7\u0301",\u1F74:"\u03B7\u0300",\u03AF:"\u03B9\u0301",\u1F76:"\u03B9\u0300",\u03CA:"\u03B9\u0308",\u0390:"\u03B9\u0308\u0301",\u1FD2:"\u03B9\u0308\u0300",\u1FD1:"\u03B9\u0304",\u1FD0:"\u03B9\u0306",\u03CC:"\u03BF\u0301",\u1F78:"\u03BF\u0300",\u03CD:"\u03C5\u0301",\u1F7A:"\u03C5\u0300",\u03CB:"\u03C5\u0308",\u03B0:"\u03C5\u0308\u0301",\u1FE2:"\u03C5\u0308\u0300",\u1FE1:"\u03C5\u0304",\u1FE0:"\u03C5\u0306",\u03CE:"\u03C9\u0301",\u1F7C:"\u03C9\u0300",\u038E:"\u03A5\u0301",\u1FEA:"\u03A5\u0300",\u03AB:"\u03A5\u0308",\u1FE9:"\u03A5\u0304",\u1FE8:"\u03A5\u0306",\u038F:"\u03A9\u0301",\u1FFA:"\u03A9\u0300"},x4=class t{static{o(this,"Parser")}constructor(e,r){this.mode=void 0,this.gullet=void 0,this.settings=void 0,this.leftrightDepth=void 0,this.nextToken=void 0,this.mode="math",this.gullet=new R7(e,r,this.mode),this.settings=r,this.leftrightDepth=0}expect(e,r){if(r===void 0&&(r=!0),this.fetch().text!==e)throw new nt("Expected '"+e+"', got '"+this.fetch().text+"'",this.fetch());r&&this.consume()}consume(){this.nextToken=null}fetch(){return this.nextToken==null&&(this.nextToken=this.gullet.expandNextToken()),this.nextToken}switchMode(e){this.mode=e,this.gullet.switchMode(e)}parse(){this.settings.globalGroup||this.gullet.beginGroup(),this.settings.colorIsTextColor&&this.gullet.macros.set("\\color","\\textcolor");try{var e=this.parseExpression(!1);return this.expect("EOF"),this.settings.globalGroup||this.gullet.endGroup(),e}finally{this.gullet.endGroups()}}subparse(e){var r=this.nextToken;this.consume(),this.gullet.pushToken(new Ao("}")),this.gullet.pushTokens(e);var n=this.parseExpression(!1);return this.expect("}"),this.nextToken=r,n}parseExpression(e,r){for(var n=[];;){this.mode==="math"&&this.consumeSpaces();var i=this.fetch();if(t.endOfExpression.indexOf(i.text)!==-1||r&&i.text===r||e&&fh[i.text]&&fh[i.text].infix)break;var a=this.parseAtom(r);if(a){if(a.type==="internal")continue}else break;n.push(a)}return this.mode==="text"&&this.formLigatures(n),this.handleInfixNodes(n)}handleInfixNodes(e){for(var r=-1,n,i=0;i=0&&this.settings.reportNonstrict("unicodeTextInMathMode",'Latin-1/Unicode text character "'+r[0]+'" used in math mode',e);var l=wn[this.mode][r].group,u=Xs.range(e),h;if(hxe.hasOwnProperty(l)){var f=l;h={type:"atom",mode:this.mode,family:f,loc:u,text:r}}else h={type:l,mode:this.mode,loc:u,text:r};s=h}else if(r.charCodeAt(0)>=128)this.settings.strict&&(LG(r.charCodeAt(0))?this.mode==="math"&&this.settings.reportNonstrict("unicodeTextInMathMode",'Unicode text character "'+r[0]+'" used in math mode',e):this.settings.reportNonstrict("unknownSymbol",'Unrecognized Unicode character "'+r[0]+'"'+(" ("+r.charCodeAt(0)+")"),e)),s={type:"textord",mode:"text",loc:Xs.range(e),text:r};else return null;if(this.consume(),a)for(var d=0;d{e.tagName==="A"&&e.hasAttribute("target")&&e.setAttribute(t,e.getAttribute("target")??"")}),bp.default.addHook("afterSanitizeAttributes",e=>{e.tagName==="A"&&e.hasAttribute(t)&&(e.setAttribute("target",e.getAttribute(t)??""),e.removeAttribute(t),e.getAttribute("target")==="_blank"&&e.setAttribute("rel","noopener"))})}var bp,Qf,Cbe,Sbe,A$,C$,qr,_be,Lbe,Dbe,Rbe,_$,Nbe,yr,Mbe,Ibe,gh,K7,Obe,Pbe,S$,Q7,Ni,Zf,yh,We,rr=R(()=>{"use strict";bp=Xi(o7(),1),Qf=//gi,Cbe=o(t=>t?_$(t).replace(/\\n/g,"#br#").split("#br#"):[""],"getRows"),Sbe=(()=>{let t=!1;return()=>{t||(Abe(),t=!0)}})();o(Abe,"setupDompurifyHooks");A$=o(t=>(Sbe(),bp.default.sanitize(t)),"removeScript"),C$=o((t,e)=>{if(e.flowchart?.htmlLabels!==!1){let r=e.securityLevel;r==="antiscript"||r==="strict"?t=A$(t):r!=="loose"&&(t=_$(t),t=t.replace(//g,">"),t=t.replace(/=/g,"="),t=Rbe(t))}return t},"sanitizeMore"),qr=o((t,e)=>t&&(e.dompurifyConfig?t=bp.default.sanitize(C$(t,e),e.dompurifyConfig).toString():t=bp.default.sanitize(C$(t,e),{FORBID_TAGS:["style"]}).toString(),t),"sanitizeText"),_be=o((t,e)=>typeof t=="string"?qr(t,e):t.flat().map(r=>qr(r,e)),"sanitizeTextOrArray"),Lbe=o(t=>Qf.test(t),"hasBreaks"),Dbe=o(t=>t.split(Qf),"splitBreaks"),Rbe=o(t=>t.replace(/#br#/g,"
"),"placeholderToBreak"),_$=o(t=>t.replace(Qf,"#br#"),"breakToPlaceholder"),Nbe=o(t=>{let e="";return t&&(e=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search,e=e.replaceAll(/\(/g,"\\("),e=e.replaceAll(/\)/g,"\\)")),e},"getUrl"),yr=o(t=>!(t===!1||["false","null","0"].includes(String(t).trim().toLowerCase())),"evaluate"),Mbe=o(function(...t){let e=t.filter(r=>!isNaN(r));return Math.max(...e)},"getMax"),Ibe=o(function(...t){let e=t.filter(r=>!isNaN(r));return Math.min(...e)},"getMin"),gh=o(function(t){let e=t.split(/(,)/),r=[];for(let n=0;n0&&n+1Math.max(0,t.split(e).length-1),"countOccurrence"),Obe=o((t,e)=>{let r=K7(t,"~"),n=K7(e,"~");return r===1&&n===1},"shouldCombineSets"),Pbe=o(t=>{let e=K7(t,"~"),r=!1;if(e<=1)return t;e%2!==0&&t.startsWith("~")&&(t=t.substring(1),r=!0);let n=[...t],i=n.indexOf("~"),a=n.lastIndexOf("~");for(;i!==-1&&a!==-1&&i!==a;)n[i]="<",n[a]=">",i=n.indexOf("~"),a=n.lastIndexOf("~");return r&&n.unshift("~"),n.join("")},"processSet"),S$=o(()=>window.MathMLElement!==void 0,"isMathMLSupported"),Q7=/\$\$(.*)\$\$/g,Ni=o(t=>(t.match(Q7)?.length??0)>0,"hasKatex"),Zf=o(async(t,e)=>{t=await yh(t,e);let r=document.createElement("div");r.innerHTML=t,r.id="katex-temp",r.style.visibility="hidden",r.style.position="absolute",r.style.top="0",document.querySelector("body")?.insertAdjacentElement("beforeend",r);let i={width:r.clientWidth,height:r.clientHeight};return r.remove(),i},"calculateMathMLDimensions"),yh=o(async(t,e)=>{if(!Ni(t))return t;if(!(S$()||e.legacyMathML||e.forceLegacyMathML))return t.replace(Q7,"MathML is unsupported in this environment.");let{default:r}=await Promise.resolve().then(()=>(E$(),k$)),n=e.forceLegacyMathML||!S$()&&e.legacyMathML?"htmlAndMathml":"mathml";return t.split(Qf).map(i=>Ni(i)?`
${i}
`:`
${i}
`).join("").replace(Q7,(i,a)=>r.renderToString(a,{throwOnError:!0,displayMode:!0,output:n}).replace(/\n/g," ").replace(//g,""))},"renderKatex"),We={getRows:Cbe,sanitizeText:qr,sanitizeTextOrArray:_be,hasBreaks:Lbe,splitBreaks:Dbe,lineBreakRegex:Qf,removeScript:A$,getUrl:Nbe,evaluate:yr,getMax:Mbe,getMin:Ibe}});var Bbe,Fbe,Sr,Lo,Yn=R(()=>{"use strict";ut();Bbe=o(function(t,e){for(let r of e)t.attr(r[0],r[1])},"d3Attrs"),Fbe=o(function(t,e,r){let n=new Map;return r?(n.set("width","100%"),n.set("style",`max-width: ${e}px;`)):(n.set("height",t),n.set("width",e)),n},"calculateSvgSizeAttrs"),Sr=o(function(t,e,r,n){let i=Fbe(e,r,n);Bbe(t,i)},"configureSvgSize"),Lo=o(function(t,e,r,n){let i=e.node().getBBox(),a=i.width,s=i.height;V.info(`SVG bounds: ${a}x${s}`,i);let l=0,u=0;V.info(`Graph bounds: ${l}x${u}`,t),l=a+r*2,u=s+r*2,V.info(`Calculated bounds: ${l}x${u}`),Sr(e,u,l,n);let h=`${i.x-r} ${i.y-r} ${i.width+2*r} ${i.height+2*r}`;e.attr("viewBox",h)},"setupGraphViewbox")});var S4,zbe,L$,D$,Z7=R(()=>{"use strict";ut();S4={},zbe=o((t,e,r)=>{let n="";return t in S4&&S4[t]?n=S4[t](r):V.warn(`No theme found for ${t}`),` & { + font-family: ${r.fontFamily}; + font-size: ${r.fontSize}; + fill: ${r.textColor} + } + + /* Classes common for multiple diagrams */ + + & .error-icon { + fill: ${r.errorBkgColor}; + } + & .error-text { + fill: ${r.errorTextColor}; + stroke: ${r.errorTextColor}; + } + + & .edge-thickness-normal { + stroke-width: 1px; + } + & .edge-thickness-thick { + stroke-width: 3.5px + } + & .edge-pattern-solid { + stroke-dasharray: 0; + } + & .edge-thickness-invisible { + stroke-width: 0; + fill: none; + } + & .edge-pattern-dashed{ + stroke-dasharray: 3; + } + .edge-pattern-dotted { + stroke-dasharray: 2; + } + + & .marker { + fill: ${r.lineColor}; + stroke: ${r.lineColor}; + } + & .marker.cross { + stroke: ${r.lineColor}; + } + + & svg { + font-family: ${r.fontFamily}; + font-size: ${r.fontSize}; + } + & p { + margin: 0 + } + + ${n} + + ${e} +`},"getStyles"),L$=o((t,e)=>{e!==void 0&&(S4[t]=e)},"addStylesForDiagram"),D$=zbe});var ly={};hr(ly,{clear:()=>vr,getAccDescription:()=>Lr,getAccTitle:()=>Ar,getDiagramTitle:()=>Xr,setAccDescription:()=>_r,setAccTitle:()=>kr,setDiagramTitle:()=>nn});var J7,eS,tS,rS,vr,kr,Ar,_r,Lr,nn,Xr,bi=R(()=>{"use strict";rr();qs();J7="",eS="",tS="",rS=o(t=>qr(t,Or()),"sanitizeText"),vr=o(()=>{J7="",tS="",eS=""},"clear"),kr=o(t=>{J7=rS(t).replace(/^\s+/g,"")},"setAccTitle"),Ar=o(()=>J7,"getAccTitle"),_r=o(t=>{tS=rS(t).replace(/\n\s+/g,` +`)},"setAccDescription"),Lr=o(()=>tS,"getAccDescription"),nn=o(t=>{eS=rS(t)},"setDiagramTitle"),Xr=o(()=>eS,"getDiagramTitle")});var R$,Gbe,de,iS,_4,$be,aS,Vbe,A4,Jf,cy,nS,_t=R(()=>{"use strict";Hf();ut();qs();rr();Yn();Z7();bi();R$=V,Gbe=$1,de=Or,iS=Zb,_4=uh,$be=o(t=>qr(t,de()),"sanitizeText"),aS=Lo,Vbe=o(()=>ly,"getCommonDb"),A4={},Jf=o((t,e,r)=>{A4[t]&&R$.warn(`Diagram with id ${t} already registered. Overwriting.`),A4[t]=e,r&&$C(t,r),L$(t,e.styles),e.injectUtils?.(R$,Gbe,de,$be,aS,Vbe(),()=>{})},"registerDiagram"),cy=o(t=>{if(t in A4)return A4[t];throw new nS(t)},"getDiagram"),nS=class extends Error{static{o(this,"DiagramNotFoundError")}constructor(e){super(`Diagram ${e} not found.`)}}});var ul,vh,ja,cl,nc,uy,sS,oS,L4,D4,N$,Ube,Hbe,Ybe,Wbe,qbe,Xbe,jbe,Kbe,Qbe,Zbe,Jbe,e4e,t4e,r4e,n4e,i4e,a4e,M$,s4e,o4e,I$,l4e,c4e,u4e,h4e,xh,f4e,d4e,p4e,m4e,g4e,hy,lS=R(()=>{"use strict";_t();rr();bi();ul=[],vh=[""],ja="global",cl="",nc=[{alias:"global",label:{text:"global"},type:{text:"global"},tags:null,link:null,parentBoundary:""}],uy=[],sS="",oS=!1,L4=4,D4=2,Ube=o(function(){return N$},"getC4Type"),Hbe=o(function(t){N$=qr(t,de())},"setC4Type"),Ybe=o(function(t,e,r,n,i,a,s,l,u){if(t==null||e===void 0||e===null||r===void 0||r===null||n===void 0||n===null)return;let h={},f=uy.find(d=>d.from===e&&d.to===r);if(f?h=f:uy.push(h),h.type=t,h.from=e,h.to=r,h.label={text:n},i==null)h.techn={text:""};else if(typeof i=="object"){let[d,p]=Object.entries(i)[0];h[d]={text:p}}else h.techn={text:i};if(a==null)h.descr={text:""};else if(typeof a=="object"){let[d,p]=Object.entries(a)[0];h[d]={text:p}}else h.descr={text:a};if(typeof s=="object"){let[d,p]=Object.entries(s)[0];h[d]=p}else h.sprite=s;if(typeof l=="object"){let[d,p]=Object.entries(l)[0];h[d]=p}else h.tags=l;if(typeof u=="object"){let[d,p]=Object.entries(u)[0];h[d]=p}else h.link=u;h.wrap=xh()},"addRel"),Wbe=o(function(t,e,r,n,i,a,s){if(e===null||r===null)return;let l={},u=ul.find(h=>h.alias===e);if(u&&e===u.alias?l=u:(l.alias=e,ul.push(l)),r==null?l.label={text:""}:l.label={text:r},n==null)l.descr={text:""};else if(typeof n=="object"){let[h,f]=Object.entries(n)[0];l[h]={text:f}}else l.descr={text:n};if(typeof i=="object"){let[h,f]=Object.entries(i)[0];l[h]=f}else l.sprite=i;if(typeof a=="object"){let[h,f]=Object.entries(a)[0];l[h]=f}else l.tags=a;if(typeof s=="object"){let[h,f]=Object.entries(s)[0];l[h]=f}else l.link=s;l.typeC4Shape={text:t},l.parentBoundary=ja,l.wrap=xh()},"addPersonOrSystem"),qbe=o(function(t,e,r,n,i,a,s,l){if(e===null||r===null)return;let u={},h=ul.find(f=>f.alias===e);if(h&&e===h.alias?u=h:(u.alias=e,ul.push(u)),r==null?u.label={text:""}:u.label={text:r},n==null)u.techn={text:""};else if(typeof n=="object"){let[f,d]=Object.entries(n)[0];u[f]={text:d}}else u.techn={text:n};if(i==null)u.descr={text:""};else if(typeof i=="object"){let[f,d]=Object.entries(i)[0];u[f]={text:d}}else u.descr={text:i};if(typeof a=="object"){let[f,d]=Object.entries(a)[0];u[f]=d}else u.sprite=a;if(typeof s=="object"){let[f,d]=Object.entries(s)[0];u[f]=d}else u.tags=s;if(typeof l=="object"){let[f,d]=Object.entries(l)[0];u[f]=d}else u.link=l;u.wrap=xh(),u.typeC4Shape={text:t},u.parentBoundary=ja},"addContainer"),Xbe=o(function(t,e,r,n,i,a,s,l){if(e===null||r===null)return;let u={},h=ul.find(f=>f.alias===e);if(h&&e===h.alias?u=h:(u.alias=e,ul.push(u)),r==null?u.label={text:""}:u.label={text:r},n==null)u.techn={text:""};else if(typeof n=="object"){let[f,d]=Object.entries(n)[0];u[f]={text:d}}else u.techn={text:n};if(i==null)u.descr={text:""};else if(typeof i=="object"){let[f,d]=Object.entries(i)[0];u[f]={text:d}}else u.descr={text:i};if(typeof a=="object"){let[f,d]=Object.entries(a)[0];u[f]=d}else u.sprite=a;if(typeof s=="object"){let[f,d]=Object.entries(s)[0];u[f]=d}else u.tags=s;if(typeof l=="object"){let[f,d]=Object.entries(l)[0];u[f]=d}else u.link=l;u.wrap=xh(),u.typeC4Shape={text:t},u.parentBoundary=ja},"addComponent"),jbe=o(function(t,e,r,n,i){if(t===null||e===null)return;let a={},s=nc.find(l=>l.alias===t);if(s&&t===s.alias?a=s:(a.alias=t,nc.push(a)),e==null?a.label={text:""}:a.label={text:e},r==null)a.type={text:"system"};else if(typeof r=="object"){let[l,u]=Object.entries(r)[0];a[l]={text:u}}else a.type={text:r};if(typeof n=="object"){let[l,u]=Object.entries(n)[0];a[l]=u}else a.tags=n;if(typeof i=="object"){let[l,u]=Object.entries(i)[0];a[l]=u}else a.link=i;a.parentBoundary=ja,a.wrap=xh(),cl=ja,ja=t,vh.push(cl)},"addPersonOrSystemBoundary"),Kbe=o(function(t,e,r,n,i){if(t===null||e===null)return;let a={},s=nc.find(l=>l.alias===t);if(s&&t===s.alias?a=s:(a.alias=t,nc.push(a)),e==null?a.label={text:""}:a.label={text:e},r==null)a.type={text:"container"};else if(typeof r=="object"){let[l,u]=Object.entries(r)[0];a[l]={text:u}}else a.type={text:r};if(typeof n=="object"){let[l,u]=Object.entries(n)[0];a[l]=u}else a.tags=n;if(typeof i=="object"){let[l,u]=Object.entries(i)[0];a[l]=u}else a.link=i;a.parentBoundary=ja,a.wrap=xh(),cl=ja,ja=t,vh.push(cl)},"addContainerBoundary"),Qbe=o(function(t,e,r,n,i,a,s,l){if(e===null||r===null)return;let u={},h=nc.find(f=>f.alias===e);if(h&&e===h.alias?u=h:(u.alias=e,nc.push(u)),r==null?u.label={text:""}:u.label={text:r},n==null)u.type={text:"node"};else if(typeof n=="object"){let[f,d]=Object.entries(n)[0];u[f]={text:d}}else u.type={text:n};if(i==null)u.descr={text:""};else if(typeof i=="object"){let[f,d]=Object.entries(i)[0];u[f]={text:d}}else u.descr={text:i};if(typeof s=="object"){let[f,d]=Object.entries(s)[0];u[f]=d}else u.tags=s;if(typeof l=="object"){let[f,d]=Object.entries(l)[0];u[f]=d}else u.link=l;u.nodeType=t,u.parentBoundary=ja,u.wrap=xh(),cl=ja,ja=e,vh.push(cl)},"addDeploymentNode"),Zbe=o(function(){ja=cl,vh.pop(),cl=vh.pop(),vh.push(cl)},"popBoundaryParseStack"),Jbe=o(function(t,e,r,n,i,a,s,l,u,h,f){let d=ul.find(p=>p.alias===e);if(!(d===void 0&&(d=nc.find(p=>p.alias===e),d===void 0))){if(r!=null)if(typeof r=="object"){let[p,m]=Object.entries(r)[0];d[p]=m}else d.bgColor=r;if(n!=null)if(typeof n=="object"){let[p,m]=Object.entries(n)[0];d[p]=m}else d.fontColor=n;if(i!=null)if(typeof i=="object"){let[p,m]=Object.entries(i)[0];d[p]=m}else d.borderColor=i;if(a!=null)if(typeof a=="object"){let[p,m]=Object.entries(a)[0];d[p]=m}else d.shadowing=a;if(s!=null)if(typeof s=="object"){let[p,m]=Object.entries(s)[0];d[p]=m}else d.shape=s;if(l!=null)if(typeof l=="object"){let[p,m]=Object.entries(l)[0];d[p]=m}else d.sprite=l;if(u!=null)if(typeof u=="object"){let[p,m]=Object.entries(u)[0];d[p]=m}else d.techn=u;if(h!=null)if(typeof h=="object"){let[p,m]=Object.entries(h)[0];d[p]=m}else d.legendText=h;if(f!=null)if(typeof f=="object"){let[p,m]=Object.entries(f)[0];d[p]=m}else d.legendSprite=f}},"updateElStyle"),e4e=o(function(t,e,r,n,i,a,s){let l=uy.find(u=>u.from===e&&u.to===r);if(l!==void 0){if(n!=null)if(typeof n=="object"){let[u,h]=Object.entries(n)[0];l[u]=h}else l.textColor=n;if(i!=null)if(typeof i=="object"){let[u,h]=Object.entries(i)[0];l[u]=h}else l.lineColor=i;if(a!=null)if(typeof a=="object"){let[u,h]=Object.entries(a)[0];l[u]=parseInt(h)}else l.offsetX=parseInt(a);if(s!=null)if(typeof s=="object"){let[u,h]=Object.entries(s)[0];l[u]=parseInt(h)}else l.offsetY=parseInt(s)}},"updateRelStyle"),t4e=o(function(t,e,r){let n=L4,i=D4;if(typeof e=="object"){let a=Object.values(e)[0];n=parseInt(a)}else n=parseInt(e);if(typeof r=="object"){let a=Object.values(r)[0];i=parseInt(a)}else i=parseInt(r);n>=1&&(L4=n),i>=1&&(D4=i)},"updateLayoutConfig"),r4e=o(function(){return L4},"getC4ShapeInRow"),n4e=o(function(){return D4},"getC4BoundaryInRow"),i4e=o(function(){return ja},"getCurrentBoundaryParse"),a4e=o(function(){return cl},"getParentBoundaryParse"),M$=o(function(t){return t==null?ul:ul.filter(e=>e.parentBoundary===t)},"getC4ShapeArray"),s4e=o(function(t){return ul.find(e=>e.alias===t)},"getC4Shape"),o4e=o(function(t){return Object.keys(M$(t))},"getC4ShapeKeys"),I$=o(function(t){return t==null?nc:nc.filter(e=>e.parentBoundary===t)},"getBoundaries"),l4e=I$,c4e=o(function(){return uy},"getRels"),u4e=o(function(){return sS},"getTitle"),h4e=o(function(t){oS=t},"setWrap"),xh=o(function(){return oS},"autoWrap"),f4e=o(function(){ul=[],nc=[{alias:"global",label:{text:"global"},type:{text:"global"},tags:null,link:null,parentBoundary:""}],cl="",ja="global",vh=[""],uy=[],vh=[""],sS="",oS=!1,L4=4,D4=2},"clear"),d4e={SOLID:0,DOTTED:1,NOTE:2,SOLID_CROSS:3,DOTTED_CROSS:4,SOLID_OPEN:5,DOTTED_OPEN:6,LOOP_START:10,LOOP_END:11,ALT_START:12,ALT_ELSE:13,ALT_END:14,OPT_START:15,OPT_END:16,ACTIVE_START:17,ACTIVE_END:18,PAR_START:19,PAR_AND:20,PAR_END:21,RECT_START:22,RECT_END:23,SOLID_POINT:24,DOTTED_POINT:25},p4e={FILLED:0,OPEN:1},m4e={LEFTOF:0,RIGHTOF:1,OVER:2},g4e=o(function(t){sS=qr(t,de())},"setTitle"),hy={addPersonOrSystem:Wbe,addPersonOrSystemBoundary:jbe,addContainer:qbe,addContainerBoundary:Kbe,addComponent:Xbe,addDeploymentNode:Qbe,popBoundaryParseStack:Zbe,addRel:Ybe,updateElStyle:Jbe,updateRelStyle:e4e,updateLayoutConfig:t4e,autoWrap:xh,setWrap:h4e,getC4ShapeArray:M$,getC4Shape:s4e,getC4ShapeKeys:o4e,getBoundaries:I$,getBoundarys:l4e,getCurrentBoundaryParse:i4e,getParentBoundaryParse:a4e,getRels:c4e,getTitle:u4e,getC4Type:Ube,getC4ShapeInRow:r4e,getC4BoundaryInRow:n4e,setAccTitle:kr,getAccTitle:Ar,getAccDescription:Lr,setAccDescription:_r,getConfig:o(()=>de().c4,"getConfig"),clear:f4e,LINETYPE:d4e,ARROWTYPE:p4e,PLACEMENT:m4e,setTitle:g4e,setC4Type:Hbe}});function ed(t,e){return t==null||e==null?NaN:te?1:t>=e?0:NaN}var cS=R(()=>{"use strict";o(ed,"ascending")});function uS(t,e){return t==null||e==null?NaN:et?1:e>=t?0:NaN}var O$=R(()=>{"use strict";o(uS,"descending")});function td(t){let e,r,n;t.length!==2?(e=ed,r=o((l,u)=>ed(t(l),u),"compare2"),n=o((l,u)=>t(l)-u,"delta")):(e=t===ed||t===uS?t:y4e,r=t,n=t);function i(l,u,h=0,f=l.length){if(h>>1;r(l[d],u)<0?h=d+1:f=d}while(h>>1;r(l[d],u)<=0?h=d+1:f=d}while(hh&&n(l[d-1],u)>-n(l[d],u)?d-1:d}return o(s,"center"),{left:i,center:s,right:a}}function y4e(){return 0}var hS=R(()=>{"use strict";cS();O$();o(td,"bisector");o(y4e,"zero")});function fS(t){return t===null?NaN:+t}var P$=R(()=>{"use strict";o(fS,"number")});var B$,F$,v4e,x4e,dS,z$=R(()=>{"use strict";cS();hS();P$();B$=td(ed),F$=B$.right,v4e=B$.left,x4e=td(fS).center,dS=F$});function G$({_intern:t,_key:e},r){let n=e(r);return t.has(n)?t.get(n):r}function b4e({_intern:t,_key:e},r){let n=e(r);return t.has(n)?t.get(n):(t.set(n,r),r)}function w4e({_intern:t,_key:e},r){let n=e(r);return t.has(n)&&(r=t.get(n),t.delete(n)),r}function T4e(t){return t!==null&&typeof t=="object"?t.valueOf():t}var wp,$$=R(()=>{"use strict";wp=class extends Map{static{o(this,"InternMap")}constructor(e,r=T4e){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:r}}),e!=null)for(let[n,i]of e)this.set(n,i)}get(e){return super.get(G$(this,e))}has(e){return super.has(G$(this,e))}set(e,r){return super.set(b4e(this,e),r)}delete(e){return super.delete(w4e(this,e))}};o(G$,"intern_get");o(b4e,"intern_set");o(w4e,"intern_delete");o(T4e,"keyof")});function R4(t,e,r){let n=(e-t)/Math.max(0,r),i=Math.floor(Math.log10(n)),a=n/Math.pow(10,i),s=a>=k4e?10:a>=E4e?5:a>=C4e?2:1,l,u,h;return i<0?(h=Math.pow(10,-i)/s,l=Math.round(t*h),u=Math.round(e*h),l/he&&--u,h=-h):(h=Math.pow(10,i)*s,l=Math.round(t/h),u=Math.round(e/h),l*he&&--u),u0))return[];if(t===e)return[t];let n=e=i))return[];let l=a-i+1,u=new Array(l);if(n)if(s<0)for(let h=0;h{"use strict";k4e=Math.sqrt(50),E4e=Math.sqrt(10),C4e=Math.sqrt(2);o(R4,"tickSpec");o(N4,"ticks");o(fy,"tickIncrement");o(Tp,"tickStep")});function M4(t,e){let r;if(e===void 0)for(let n of t)n!=null&&(r=n)&&(r=n);else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(r=i)&&(r=i)}return r}var U$=R(()=>{"use strict";o(M4,"max")});function I4(t,e){let r;if(e===void 0)for(let n of t)n!=null&&(r>n||r===void 0&&n>=n)&&(r=n);else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(r>i||r===void 0&&i>=i)&&(r=i)}return r}var H$=R(()=>{"use strict";o(I4,"min")});function O4(t,e,r){t=+t,e=+e,r=(i=arguments.length)<2?(e=t,t=0,1):i<3?1:+r;for(var n=-1,i=Math.max(0,Math.ceil((e-t)/r))|0,a=new Array(i);++n{"use strict";o(O4,"range")});var bh=R(()=>{"use strict";z$();hS();U$();H$();Y$();V$();$$()});function pS(t){return t}var W$=R(()=>{"use strict";o(pS,"default")});function S4e(t){return"translate("+t+",0)"}function A4e(t){return"translate(0,"+t+")"}function _4e(t){return e=>+t(e)}function L4e(t,e){return e=Math.max(0,t.bandwidth()-e*2)/2,t.round()&&(e=Math.round(e)),r=>+t(r)+e}function D4e(){return!this.__axis}function X$(t,e){var r=[],n=null,i=null,a=6,s=6,l=3,u=typeof window<"u"&&window.devicePixelRatio>1?0:.5,h=t===B4||t===P4?-1:1,f=t===P4||t===mS?"x":"y",d=t===B4||t===gS?S4e:A4e;function p(m){var g=n??(e.ticks?e.ticks.apply(e,r):e.domain()),y=i??(e.tickFormat?e.tickFormat.apply(e,r):pS),v=Math.max(a,0)+l,x=e.range(),b=+x[0]+u,w=+x[x.length-1]+u,S=(e.bandwidth?L4e:_4e)(e.copy(),u),T=m.selection?m.selection():m,E=T.selectAll(".domain").data([null]),_=T.selectAll(".tick").data(g,e).order(),A=_.exit(),L=_.enter().append("g").attr("class","tick"),M=_.select("line"),N=_.select("text");E=E.merge(E.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),_=_.merge(L),M=M.merge(L.append("line").attr("stroke","currentColor").attr(f+"2",h*a)),N=N.merge(L.append("text").attr("fill","currentColor").attr(f,h*v).attr("dy",t===B4?"0em":t===gS?"0.71em":"0.32em")),m!==T&&(E=E.transition(m),_=_.transition(m),M=M.transition(m),N=N.transition(m),A=A.transition(m).attr("opacity",q$).attr("transform",function(k){return isFinite(k=S(k))?d(k+u):this.getAttribute("transform")}),L.attr("opacity",q$).attr("transform",function(k){var I=this.parentNode.__axis;return d((I&&isFinite(I=I(k))?I:S(k))+u)})),A.remove(),E.attr("d",t===P4||t===mS?s?"M"+h*s+","+b+"H"+u+"V"+w+"H"+h*s:"M"+u+","+b+"V"+w:s?"M"+b+","+h*s+"V"+u+"H"+w+"V"+h*s:"M"+b+","+u+"H"+w),_.attr("opacity",1).attr("transform",function(k){return d(S(k)+u)}),M.attr(f+"2",h*a),N.attr(f,h*v).text(y),T.filter(D4e).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",t===mS?"start":t===P4?"end":"middle"),T.each(function(){this.__axis=S})}return o(p,"axis"),p.scale=function(m){return arguments.length?(e=m,p):e},p.ticks=function(){return r=Array.from(arguments),p},p.tickArguments=function(m){return arguments.length?(r=m==null?[]:Array.from(m),p):r.slice()},p.tickValues=function(m){return arguments.length?(n=m==null?null:Array.from(m),p):n&&n.slice()},p.tickFormat=function(m){return arguments.length?(i=m,p):i},p.tickSize=function(m){return arguments.length?(a=s=+m,p):a},p.tickSizeInner=function(m){return arguments.length?(a=+m,p):a},p.tickSizeOuter=function(m){return arguments.length?(s=+m,p):s},p.tickPadding=function(m){return arguments.length?(l=+m,p):l},p.offset=function(m){return arguments.length?(u=+m,p):u},p}function yS(t){return X$(B4,t)}function vS(t){return X$(gS,t)}var B4,mS,gS,P4,q$,j$=R(()=>{"use strict";W$();B4=1,mS=2,gS=3,P4=4,q$=1e-6;o(S4e,"translateX");o(A4e,"translateY");o(_4e,"number");o(L4e,"center");o(D4e,"entering");o(X$,"axis");o(yS,"axisTop");o(vS,"axisBottom")});var K$=R(()=>{"use strict";j$()});function Z$(){for(var t=0,e=arguments.length,r={},n;t=0&&(n=r.slice(i+1),r=r.slice(0,i)),r&&!e.hasOwnProperty(r))throw new Error("unknown type: "+r);return{type:r,name:n}})}function M4e(t,e){for(var r=0,n=t.length,i;r{"use strict";R4e={value:o(()=>{},"value")};o(Z$,"dispatch");o(F4,"Dispatch");o(N4e,"parseTypenames");F4.prototype=Z$.prototype={constructor:F4,on:o(function(t,e){var r=this._,n=N4e(t+"",r),i,a=-1,s=n.length;if(arguments.length<2){for(;++a0)for(var r=new Array(i),n=0,i,a;n{"use strict";J$()});var z4,wS,TS=R(()=>{"use strict";z4="http://www.w3.org/1999/xhtml",wS={svg:"http://www.w3.org/2000/svg",xhtml:z4,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"}});function ic(t){var e=t+="",r=e.indexOf(":");return r>=0&&(e=t.slice(0,r))!=="xmlns"&&(t=t.slice(r+1)),wS.hasOwnProperty(e)?{space:wS[e],local:t}:t}var G4=R(()=>{"use strict";TS();o(ic,"default")});function I4e(t){return function(){var e=this.ownerDocument,r=this.namespaceURI;return r===z4&&e.documentElement.namespaceURI===z4?e.createElement(t):e.createElementNS(r,t)}}function O4e(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function dy(t){var e=ic(t);return(e.local?O4e:I4e)(e)}var kS=R(()=>{"use strict";G4();TS();o(I4e,"creatorInherit");o(O4e,"creatorFixed");o(dy,"default")});function P4e(){}function wh(t){return t==null?P4e:function(){return this.querySelector(t)}}var $4=R(()=>{"use strict";o(P4e,"none");o(wh,"default")});function ES(t){typeof t!="function"&&(t=wh(t));for(var e=this._groups,r=e.length,n=new Array(r),i=0;i{"use strict";hl();$4();o(ES,"default")});function CS(t){return t==null?[]:Array.isArray(t)?t:Array.from(t)}var tV=R(()=>{"use strict";o(CS,"array")});function B4e(){return[]}function kp(t){return t==null?B4e:function(){return this.querySelectorAll(t)}}var SS=R(()=>{"use strict";o(B4e,"empty");o(kp,"default")});function F4e(t){return function(){return CS(t.apply(this,arguments))}}function AS(t){typeof t=="function"?t=F4e(t):t=kp(t);for(var e=this._groups,r=e.length,n=[],i=[],a=0;a{"use strict";hl();tV();SS();o(F4e,"arrayAll");o(AS,"default")});function Ep(t){return function(){return this.matches(t)}}function V4(t){return function(e){return e.matches(t)}}var py=R(()=>{"use strict";o(Ep,"default");o(V4,"childMatcher")});function G4e(t){return function(){return z4e.call(this.children,t)}}function $4e(){return this.firstElementChild}function _S(t){return this.select(t==null?$4e:G4e(typeof t=="function"?t:V4(t)))}var z4e,nV=R(()=>{"use strict";py();z4e=Array.prototype.find;o(G4e,"childFind");o($4e,"childFirst");o(_S,"default")});function U4e(){return Array.from(this.children)}function H4e(t){return function(){return V4e.call(this.children,t)}}function LS(t){return this.selectAll(t==null?U4e:H4e(typeof t=="function"?t:V4(t)))}var V4e,iV=R(()=>{"use strict";py();V4e=Array.prototype.filter;o(U4e,"children");o(H4e,"childrenFilter");o(LS,"default")});function DS(t){typeof t!="function"&&(t=Ep(t));for(var e=this._groups,r=e.length,n=new Array(r),i=0;i{"use strict";hl();py();o(DS,"default")});function my(t){return new Array(t.length)}var RS=R(()=>{"use strict";o(my,"default")});function NS(){return new Zn(this._enter||this._groups.map(my),this._parents)}function gy(t,e){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=e}var MS=R(()=>{"use strict";RS();hl();o(NS,"default");o(gy,"EnterNode");gy.prototype={constructor:gy,appendChild:o(function(t){return this._parent.insertBefore(t,this._next)},"appendChild"),insertBefore:o(function(t,e){return this._parent.insertBefore(t,e)},"insertBefore"),querySelector:o(function(t){return this._parent.querySelector(t)},"querySelector"),querySelectorAll:o(function(t){return this._parent.querySelectorAll(t)},"querySelectorAll")}});function IS(t){return function(){return t}}var sV=R(()=>{"use strict";o(IS,"default")});function Y4e(t,e,r,n,i,a){for(var s=0,l,u=e.length,h=a.length;s=w&&(w=b+1);!(T=v[w])&&++w{"use strict";hl();MS();sV();o(Y4e,"bindIndex");o(W4e,"bindKey");o(q4e,"datum");o(OS,"default");o(X4e,"arraylike")});function PS(){return new Zn(this._exit||this._groups.map(my),this._parents)}var lV=R(()=>{"use strict";RS();hl();o(PS,"default")});function BS(t,e,r){var n=this.enter(),i=this,a=this.exit();return typeof t=="function"?(n=t(n),n&&(n=n.selection())):n=n.append(t+""),e!=null&&(i=e(i),i&&(i=i.selection())),r==null?a.remove():r(a),n&&i?n.merge(i).order():i}var cV=R(()=>{"use strict";o(BS,"default")});function FS(t){for(var e=t.selection?t.selection():t,r=this._groups,n=e._groups,i=r.length,a=n.length,s=Math.min(i,a),l=new Array(i),u=0;u{"use strict";hl();o(FS,"default")});function zS(){for(var t=this._groups,e=-1,r=t.length;++e=0;)(s=n[i])&&(a&&s.compareDocumentPosition(a)^4&&a.parentNode.insertBefore(s,a),a=s);return this}var hV=R(()=>{"use strict";o(zS,"default")});function GS(t){t||(t=j4e);function e(d,p){return d&&p?t(d.__data__,p.__data__):!d-!p}o(e,"compareNode");for(var r=this._groups,n=r.length,i=new Array(n),a=0;ae?1:t>=e?0:NaN}var fV=R(()=>{"use strict";hl();o(GS,"default");o(j4e,"ascending")});function $S(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this}var dV=R(()=>{"use strict";o($S,"default")});function VS(){return Array.from(this)}var pV=R(()=>{"use strict";o(VS,"default")});function US(){for(var t=this._groups,e=0,r=t.length;e{"use strict";o(US,"default")});function HS(){let t=0;for(let e of this)++t;return t}var gV=R(()=>{"use strict";o(HS,"default")});function YS(){return!this.node()}var yV=R(()=>{"use strict";o(YS,"default")});function WS(t){for(var e=this._groups,r=0,n=e.length;r{"use strict";o(WS,"default")});function K4e(t){return function(){this.removeAttribute(t)}}function Q4e(t){return function(){this.removeAttributeNS(t.space,t.local)}}function Z4e(t,e){return function(){this.setAttribute(t,e)}}function J4e(t,e){return function(){this.setAttributeNS(t.space,t.local,e)}}function e3e(t,e){return function(){var r=e.apply(this,arguments);r==null?this.removeAttribute(t):this.setAttribute(t,r)}}function t3e(t,e){return function(){var r=e.apply(this,arguments);r==null?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,r)}}function qS(t,e){var r=ic(t);if(arguments.length<2){var n=this.node();return r.local?n.getAttributeNS(r.space,r.local):n.getAttribute(r)}return this.each((e==null?r.local?Q4e:K4e:typeof e=="function"?r.local?t3e:e3e:r.local?J4e:Z4e)(r,e))}var xV=R(()=>{"use strict";G4();o(K4e,"attrRemove");o(Q4e,"attrRemoveNS");o(Z4e,"attrConstant");o(J4e,"attrConstantNS");o(e3e,"attrFunction");o(t3e,"attrFunctionNS");o(qS,"default")});function yy(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}var XS=R(()=>{"use strict";o(yy,"default")});function r3e(t){return function(){this.style.removeProperty(t)}}function n3e(t,e,r){return function(){this.style.setProperty(t,e,r)}}function i3e(t,e,r){return function(){var n=e.apply(this,arguments);n==null?this.style.removeProperty(t):this.style.setProperty(t,n,r)}}function jS(t,e,r){return arguments.length>1?this.each((e==null?r3e:typeof e=="function"?i3e:n3e)(t,e,r??"")):Th(this.node(),t)}function Th(t,e){return t.style.getPropertyValue(e)||yy(t).getComputedStyle(t,null).getPropertyValue(e)}var KS=R(()=>{"use strict";XS();o(r3e,"styleRemove");o(n3e,"styleConstant");o(i3e,"styleFunction");o(jS,"default");o(Th,"styleValue")});function a3e(t){return function(){delete this[t]}}function s3e(t,e){return function(){this[t]=e}}function o3e(t,e){return function(){var r=e.apply(this,arguments);r==null?delete this[t]:this[t]=r}}function QS(t,e){return arguments.length>1?this.each((e==null?a3e:typeof e=="function"?o3e:s3e)(t,e)):this.node()[t]}var bV=R(()=>{"use strict";o(a3e,"propertyRemove");o(s3e,"propertyConstant");o(o3e,"propertyFunction");o(QS,"default")});function wV(t){return t.trim().split(/^|\s+/)}function ZS(t){return t.classList||new TV(t)}function TV(t){this._node=t,this._names=wV(t.getAttribute("class")||"")}function kV(t,e){for(var r=ZS(t),n=-1,i=e.length;++n{"use strict";o(wV,"classArray");o(ZS,"classList");o(TV,"ClassList");TV.prototype={add:o(function(t){var e=this._names.indexOf(t);e<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},"add"),remove:o(function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},"remove"),contains:o(function(t){return this._names.indexOf(t)>=0},"contains")};o(kV,"classedAdd");o(EV,"classedRemove");o(l3e,"classedTrue");o(c3e,"classedFalse");o(u3e,"classedFunction");o(JS,"default")});function h3e(){this.textContent=""}function f3e(t){return function(){this.textContent=t}}function d3e(t){return function(){var e=t.apply(this,arguments);this.textContent=e??""}}function eA(t){return arguments.length?this.each(t==null?h3e:(typeof t=="function"?d3e:f3e)(t)):this.node().textContent}var SV=R(()=>{"use strict";o(h3e,"textRemove");o(f3e,"textConstant");o(d3e,"textFunction");o(eA,"default")});function p3e(){this.innerHTML=""}function m3e(t){return function(){this.innerHTML=t}}function g3e(t){return function(){var e=t.apply(this,arguments);this.innerHTML=e??""}}function tA(t){return arguments.length?this.each(t==null?p3e:(typeof t=="function"?g3e:m3e)(t)):this.node().innerHTML}var AV=R(()=>{"use strict";o(p3e,"htmlRemove");o(m3e,"htmlConstant");o(g3e,"htmlFunction");o(tA,"default")});function y3e(){this.nextSibling&&this.parentNode.appendChild(this)}function rA(){return this.each(y3e)}var _V=R(()=>{"use strict";o(y3e,"raise");o(rA,"default")});function v3e(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function nA(){return this.each(v3e)}var LV=R(()=>{"use strict";o(v3e,"lower");o(nA,"default")});function iA(t){var e=typeof t=="function"?t:dy(t);return this.select(function(){return this.appendChild(e.apply(this,arguments))})}var DV=R(()=>{"use strict";kS();o(iA,"default")});function x3e(){return null}function aA(t,e){var r=typeof t=="function"?t:dy(t),n=e==null?x3e:typeof e=="function"?e:wh(e);return this.select(function(){return this.insertBefore(r.apply(this,arguments),n.apply(this,arguments)||null)})}var RV=R(()=>{"use strict";kS();$4();o(x3e,"constantNull");o(aA,"default")});function b3e(){var t=this.parentNode;t&&t.removeChild(this)}function sA(){return this.each(b3e)}var NV=R(()=>{"use strict";o(b3e,"remove");o(sA,"default")});function w3e(){var t=this.cloneNode(!1),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function T3e(){var t=this.cloneNode(!0),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function oA(t){return this.select(t?T3e:w3e)}var MV=R(()=>{"use strict";o(w3e,"selection_cloneShallow");o(T3e,"selection_cloneDeep");o(oA,"default")});function lA(t){return arguments.length?this.property("__data__",t):this.node().__data__}var IV=R(()=>{"use strict";o(lA,"default")});function k3e(t){return function(e){t.call(this,e,this.__data__)}}function E3e(t){return t.trim().split(/^|\s+/).map(function(e){var r="",n=e.indexOf(".");return n>=0&&(r=e.slice(n+1),e=e.slice(0,n)),{type:e,name:r}})}function C3e(t){return function(){var e=this.__on;if(e){for(var r=0,n=-1,i=e.length,a;r{"use strict";o(k3e,"contextListener");o(E3e,"parseTypenames");o(C3e,"onRemove");o(S3e,"onAdd");o(cA,"default")});function PV(t,e,r){var n=yy(t),i=n.CustomEvent;typeof i=="function"?i=new i(e,r):(i=n.document.createEvent("Event"),r?(i.initEvent(e,r.bubbles,r.cancelable),i.detail=r.detail):i.initEvent(e,!1,!1)),t.dispatchEvent(i)}function A3e(t,e){return function(){return PV(this,t,e)}}function _3e(t,e){return function(){return PV(this,t,e.apply(this,arguments))}}function uA(t,e){return this.each((typeof e=="function"?_3e:A3e)(t,e))}var BV=R(()=>{"use strict";XS();o(PV,"dispatchEvent");o(A3e,"dispatchConstant");o(_3e,"dispatchFunction");o(uA,"default")});function*hA(){for(var t=this._groups,e=0,r=t.length;e{"use strict";o(hA,"default")});function Zn(t,e){this._groups=t,this._parents=e}function zV(){return new Zn([[document.documentElement]],fA)}function L3e(){return this}var fA,cu,hl=R(()=>{"use strict";eV();rV();nV();iV();aV();oV();MS();lV();cV();uV();hV();fV();dV();pV();mV();gV();yV();vV();xV();KS();bV();CV();SV();AV();_V();LV();DV();RV();NV();MV();IV();OV();BV();FV();fA=[null];o(Zn,"Selection");o(zV,"selection");o(L3e,"selection_selection");Zn.prototype=zV.prototype={constructor:Zn,select:ES,selectAll:AS,selectChild:_S,selectChildren:LS,filter:DS,data:OS,enter:NS,exit:PS,join:BS,merge:FS,selection:L3e,order:zS,sort:GS,call:$S,nodes:VS,node:US,size:HS,empty:YS,each:WS,attr:qS,style:jS,property:QS,classed:JS,text:eA,html:tA,raise:rA,lower:nA,append:iA,insert:aA,remove:sA,clone:oA,datum:lA,on:cA,dispatch:uA,[Symbol.iterator]:hA};cu=zV});function $e(t){return typeof t=="string"?new Zn([[document.querySelector(t)]],[document.documentElement]):new Zn([[t]],fA)}var GV=R(()=>{"use strict";hl();o($e,"default")});var fl=R(()=>{"use strict";py();G4();GV();hl();$4();SS();KS()});var $V=R(()=>{"use strict"});function kh(t,e,r){t.prototype=e.prototype=r,r.constructor=t}function Cp(t,e){var r=Object.create(t.prototype);for(var n in e)r[n]=e[n];return r}var dA=R(()=>{"use strict";o(kh,"default");o(Cp,"extend")});function Eh(){}function UV(){return this.rgb().formatHex()}function B3e(){return this.rgb().formatHex8()}function F3e(){return KV(this).formatHsl()}function HV(){return this.rgb().formatRgb()}function pl(t){var e,r;return t=(t+"").trim().toLowerCase(),(e=D3e.exec(t))?(r=e[1].length,e=parseInt(e[1],16),r===6?YV(e):r===3?new la(e>>8&15|e>>4&240,e>>4&15|e&240,(e&15)<<4|e&15,1):r===8?U4(e>>24&255,e>>16&255,e>>8&255,(e&255)/255):r===4?U4(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|e&240,((e&15)<<4|e&15)/255):null):(e=R3e.exec(t))?new la(e[1],e[2],e[3],1):(e=N3e.exec(t))?new la(e[1]*255/100,e[2]*255/100,e[3]*255/100,1):(e=M3e.exec(t))?U4(e[1],e[2],e[3],e[4]):(e=I3e.exec(t))?U4(e[1]*255/100,e[2]*255/100,e[3]*255/100,e[4]):(e=O3e.exec(t))?XV(e[1],e[2]/100,e[3]/100,1):(e=P3e.exec(t))?XV(e[1],e[2]/100,e[3]/100,e[4]):VV.hasOwnProperty(t)?YV(VV[t]):t==="transparent"?new la(NaN,NaN,NaN,0):null}function YV(t){return new la(t>>16&255,t>>8&255,t&255,1)}function U4(t,e,r,n){return n<=0&&(t=e=r=NaN),new la(t,e,r,n)}function mA(t){return t instanceof Eh||(t=pl(t)),t?(t=t.rgb(),new la(t.r,t.g,t.b,t.opacity)):new la}function Ap(t,e,r,n){return arguments.length===1?mA(t):new la(t,e,r,n??1)}function la(t,e,r,n){this.r=+t,this.g=+e,this.b=+r,this.opacity=+n}function WV(){return`#${rd(this.r)}${rd(this.g)}${rd(this.b)}`}function z3e(){return`#${rd(this.r)}${rd(this.g)}${rd(this.b)}${rd((isNaN(this.opacity)?1:this.opacity)*255)}`}function qV(){let t=W4(this.opacity);return`${t===1?"rgb(":"rgba("}${nd(this.r)}, ${nd(this.g)}, ${nd(this.b)}${t===1?")":`, ${t})`}`}function W4(t){return isNaN(t)?1:Math.max(0,Math.min(1,t))}function nd(t){return Math.max(0,Math.min(255,Math.round(t)||0))}function rd(t){return t=nd(t),(t<16?"0":"")+t.toString(16)}function XV(t,e,r,n){return n<=0?t=e=r=NaN:r<=0||r>=1?t=e=NaN:e<=0&&(t=NaN),new dl(t,e,r,n)}function KV(t){if(t instanceof dl)return new dl(t.h,t.s,t.l,t.opacity);if(t instanceof Eh||(t=pl(t)),!t)return new dl;if(t instanceof dl)return t;t=t.rgb();var e=t.r/255,r=t.g/255,n=t.b/255,i=Math.min(e,r,n),a=Math.max(e,r,n),s=NaN,l=a-i,u=(a+i)/2;return l?(e===a?s=(r-n)/l+(r0&&u<1?0:s,new dl(s,l,u,t.opacity)}function QV(t,e,r,n){return arguments.length===1?KV(t):new dl(t,e,r,n??1)}function dl(t,e,r,n){this.h=+t,this.s=+e,this.l=+r,this.opacity=+n}function jV(t){return t=(t||0)%360,t<0?t+360:t}function H4(t){return Math.max(0,Math.min(1,t||0))}function pA(t,e,r){return(t<60?e+(r-e)*t/60:t<180?r:t<240?e+(r-e)*(240-t)/60:e)*255}var vy,Y4,Sp,xy,ac,D3e,R3e,N3e,M3e,I3e,O3e,P3e,VV,gA=R(()=>{"use strict";dA();o(Eh,"Color");vy=.7,Y4=1/vy,Sp="\\s*([+-]?\\d+)\\s*",xy="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)\\s*",ac="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)%\\s*",D3e=/^#([0-9a-f]{3,8})$/,R3e=new RegExp(`^rgb\\(${Sp},${Sp},${Sp}\\)$`),N3e=new RegExp(`^rgb\\(${ac},${ac},${ac}\\)$`),M3e=new RegExp(`^rgba\\(${Sp},${Sp},${Sp},${xy}\\)$`),I3e=new RegExp(`^rgba\\(${ac},${ac},${ac},${xy}\\)$`),O3e=new RegExp(`^hsl\\(${xy},${ac},${ac}\\)$`),P3e=new RegExp(`^hsla\\(${xy},${ac},${ac},${xy}\\)$`),VV={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};kh(Eh,pl,{copy(t){return Object.assign(new this.constructor,this,t)},displayable(){return this.rgb().displayable()},hex:UV,formatHex:UV,formatHex8:B3e,formatHsl:F3e,formatRgb:HV,toString:HV});o(UV,"color_formatHex");o(B3e,"color_formatHex8");o(F3e,"color_formatHsl");o(HV,"color_formatRgb");o(pl,"color");o(YV,"rgbn");o(U4,"rgba");o(mA,"rgbConvert");o(Ap,"rgb");o(la,"Rgb");kh(la,Ap,Cp(Eh,{brighter(t){return t=t==null?Y4:Math.pow(Y4,t),new la(this.r*t,this.g*t,this.b*t,this.opacity)},darker(t){return t=t==null?vy:Math.pow(vy,t),new la(this.r*t,this.g*t,this.b*t,this.opacity)},rgb(){return this},clamp(){return new la(nd(this.r),nd(this.g),nd(this.b),W4(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:WV,formatHex:WV,formatHex8:z3e,formatRgb:qV,toString:qV}));o(WV,"rgb_formatHex");o(z3e,"rgb_formatHex8");o(qV,"rgb_formatRgb");o(W4,"clampa");o(nd,"clampi");o(rd,"hex");o(XV,"hsla");o(KV,"hslConvert");o(QV,"hsl");o(dl,"Hsl");kh(dl,QV,Cp(Eh,{brighter(t){return t=t==null?Y4:Math.pow(Y4,t),new dl(this.h,this.s,this.l*t,this.opacity)},darker(t){return t=t==null?vy:Math.pow(vy,t),new dl(this.h,this.s,this.l*t,this.opacity)},rgb(){var t=this.h%360+(this.h<0)*360,e=isNaN(t)||isNaN(this.s)?0:this.s,r=this.l,n=r+(r<.5?r:1-r)*e,i=2*r-n;return new la(pA(t>=240?t-240:t+120,i,n),pA(t,i,n),pA(t<120?t+240:t-120,i,n),this.opacity)},clamp(){return new dl(jV(this.h),H4(this.s),H4(this.l),W4(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){let t=W4(this.opacity);return`${t===1?"hsl(":"hsla("}${jV(this.h)}, ${H4(this.s)*100}%, ${H4(this.l)*100}%${t===1?")":`, ${t})`}`}}));o(jV,"clamph");o(H4,"clampt");o(pA,"hsl2rgb")});var ZV,JV,eU=R(()=>{"use strict";ZV=Math.PI/180,JV=180/Math.PI});function sU(t){if(t instanceof sc)return new sc(t.l,t.a,t.b,t.opacity);if(t instanceof uu)return oU(t);t instanceof la||(t=mA(t));var e=bA(t.r),r=bA(t.g),n=bA(t.b),i=yA((.2225045*e+.7168786*r+.0606169*n)/rU),a,s;return e===r&&r===n?a=s=i:(a=yA((.4360747*e+.3850649*r+.1430804*n)/tU),s=yA((.0139322*e+.0971045*r+.7141733*n)/nU)),new sc(116*i-16,500*(a-i),200*(i-s),t.opacity)}function wA(t,e,r,n){return arguments.length===1?sU(t):new sc(t,e,r,n??1)}function sc(t,e,r,n){this.l=+t,this.a=+e,this.b=+r,this.opacity=+n}function yA(t){return t>G3e?Math.pow(t,1/3):t/aU+iU}function vA(t){return t>_p?t*t*t:aU*(t-iU)}function xA(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function bA(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function $3e(t){if(t instanceof uu)return new uu(t.h,t.c,t.l,t.opacity);if(t instanceof sc||(t=sU(t)),t.a===0&&t.b===0)return new uu(NaN,0{"use strict";dA();gA();eU();q4=18,tU=.96422,rU=1,nU=.82521,iU=4/29,_p=6/29,aU=3*_p*_p,G3e=_p*_p*_p;o(sU,"labConvert");o(wA,"lab");o(sc,"Lab");kh(sc,wA,Cp(Eh,{brighter(t){return new sc(this.l+q4*(t??1),this.a,this.b,this.opacity)},darker(t){return new sc(this.l-q4*(t??1),this.a,this.b,this.opacity)},rgb(){var t=(this.l+16)/116,e=isNaN(this.a)?t:t+this.a/500,r=isNaN(this.b)?t:t-this.b/200;return e=tU*vA(e),t=rU*vA(t),r=nU*vA(r),new la(xA(3.1338561*e-1.6168667*t-.4906146*r),xA(-.9787684*e+1.9161415*t+.033454*r),xA(.0719453*e-.2289914*t+1.4052427*r),this.opacity)}}));o(yA,"xyz2lab");o(vA,"lab2xyz");o(xA,"lrgb2rgb");o(bA,"rgb2lrgb");o($3e,"hclConvert");o(by,"hcl");o(uu,"Hcl");o(oU,"hcl2lab");kh(uu,by,Cp(Eh,{brighter(t){return new uu(this.h,this.c,this.l+q4*(t??1),this.opacity)},darker(t){return new uu(this.h,this.c,this.l-q4*(t??1),this.opacity)},rgb(){return oU(this).rgb()}}))});var Lp=R(()=>{"use strict";gA();lU()});function TA(t,e,r,n,i){var a=t*t,s=a*t;return((1-3*t+3*a-s)*e+(4-6*a+3*s)*r+(1+3*t+3*a-3*s)*n+s*i)/6}function kA(t){var e=t.length-1;return function(r){var n=r<=0?r=0:r>=1?(r=1,e-1):Math.floor(r*e),i=t[n],a=t[n+1],s=n>0?t[n-1]:2*i-a,l=n{"use strict";o(TA,"basis");o(kA,"default")});function CA(t){var e=t.length;return function(r){var n=Math.floor(((r%=1)<0?++r:r)*e),i=t[(n+e-1)%e],a=t[n%e],s=t[(n+1)%e],l=t[(n+2)%e];return TA((r-n/e)*e,i,a,s,l)}}var cU=R(()=>{"use strict";EA();o(CA,"default")});var Dp,SA=R(()=>{"use strict";Dp=o(t=>()=>t,"default")});function uU(t,e){return function(r){return t+r*e}}function V3e(t,e,r){return t=Math.pow(t,r),e=Math.pow(e,r)-t,r=1/r,function(n){return Math.pow(t+n*e,r)}}function hU(t,e){var r=e-t;return r?uU(t,r>180||r<-180?r-360*Math.round(r/360):r):Dp(isNaN(t)?e:t)}function fU(t){return(t=+t)==1?hu:function(e,r){return r-e?V3e(e,r,t):Dp(isNaN(e)?r:e)}}function hu(t,e){var r=e-t;return r?uU(t,r):Dp(isNaN(t)?e:t)}var AA=R(()=>{"use strict";SA();o(uU,"linear");o(V3e,"exponential");o(hU,"hue");o(fU,"gamma");o(hu,"nogamma")});function dU(t){return function(e){var r=e.length,n=new Array(r),i=new Array(r),a=new Array(r),s,l;for(s=0;s{"use strict";Lp();EA();cU();AA();id=o(function t(e){var r=fU(e);function n(i,a){var s=r((i=Ap(i)).r,(a=Ap(a)).r),l=r(i.g,a.g),u=r(i.b,a.b),h=hu(i.opacity,a.opacity);return function(f){return i.r=s(f),i.g=l(f),i.b=u(f),i.opacity=h(f),i+""}}return o(n,"rgb"),n.gamma=t,n},"rgbGamma")(1);o(dU,"rgbSpline");U3e=dU(kA),H3e=dU(CA)});function LA(t,e){e||(e=[]);var r=t?Math.min(e.length,t.length):0,n=e.slice(),i;return function(a){for(i=0;i{"use strict";o(LA,"default");o(pU,"isNumberArray")});function gU(t,e){var r=e?e.length:0,n=t?Math.min(r,t.length):0,i=new Array(n),a=new Array(r),s;for(s=0;s{"use strict";X4();o(gU,"genericArray")});function DA(t,e){var r=new Date;return t=+t,e=+e,function(n){return r.setTime(t*(1-n)+e*n),r}}var vU=R(()=>{"use strict";o(DA,"default")});function ji(t,e){return t=+t,e=+e,function(r){return t*(1-r)+e*r}}var wy=R(()=>{"use strict";o(ji,"default")});function RA(t,e){var r={},n={},i;(t===null||typeof t!="object")&&(t={}),(e===null||typeof e!="object")&&(e={});for(i in e)i in t?r[i]=Ch(t[i],e[i]):n[i]=e[i];return function(a){for(i in r)n[i]=r[i](a);return n}}var xU=R(()=>{"use strict";X4();o(RA,"default")});function Y3e(t){return function(){return t}}function W3e(t){return function(e){return t(e)+""}}function Rp(t,e){var r=MA.lastIndex=NA.lastIndex=0,n,i,a,s=-1,l=[],u=[];for(t=t+"",e=e+"";(n=MA.exec(t))&&(i=NA.exec(e));)(a=i.index)>r&&(a=e.slice(r,a),l[s]?l[s]+=a:l[++s]=a),(n=n[0])===(i=i[0])?l[s]?l[s]+=i:l[++s]=i:(l[++s]=null,u.push({i:s,x:ji(n,i)})),r=NA.lastIndex;return r{"use strict";wy();MA=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,NA=new RegExp(MA.source,"g");o(Y3e,"zero");o(W3e,"one");o(Rp,"default")});function Ch(t,e){var r=typeof e,n;return e==null||r==="boolean"?Dp(e):(r==="number"?ji:r==="string"?(n=pl(e))?(e=n,id):Rp:e instanceof pl?id:e instanceof Date?DA:pU(e)?LA:Array.isArray(e)?gU:typeof e.valueOf!="function"&&typeof e.toString!="function"||isNaN(e)?RA:ji)(t,e)}var X4=R(()=>{"use strict";Lp();_A();yU();vU();wy();xU();IA();SA();mU();o(Ch,"default")});function j4(t,e){return t=+t,e=+e,function(r){return Math.round(t*(1-r)+e*r)}}var bU=R(()=>{"use strict";o(j4,"default")});function Q4(t,e,r,n,i,a){var s,l,u;return(s=Math.sqrt(t*t+e*e))&&(t/=s,e/=s),(u=t*r+e*n)&&(r-=t*u,n-=e*u),(l=Math.sqrt(r*r+n*n))&&(r/=l,n/=l,u/=l),t*n{"use strict";wU=180/Math.PI,K4={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};o(Q4,"default")});function kU(t){let e=new(typeof DOMMatrix=="function"?DOMMatrix:WebKitCSSMatrix)(t+"");return e.isIdentity?K4:Q4(e.a,e.b,e.c,e.d,e.e,e.f)}function EU(t){return t==null?K4:(Z4||(Z4=document.createElementNS("http://www.w3.org/2000/svg","g")),Z4.setAttribute("transform",t),(t=Z4.transform.baseVal.consolidate())?(t=t.matrix,Q4(t.a,t.b,t.c,t.d,t.e,t.f)):K4)}var Z4,CU=R(()=>{"use strict";TU();o(kU,"parseCss");o(EU,"parseSvg")});function SU(t,e,r,n){function i(h){return h.length?h.pop()+" ":""}o(i,"pop");function a(h,f,d,p,m,g){if(h!==d||f!==p){var y=m.push("translate(",null,e,null,r);g.push({i:y-4,x:ji(h,d)},{i:y-2,x:ji(f,p)})}else(d||p)&&m.push("translate("+d+e+p+r)}o(a,"translate");function s(h,f,d,p){h!==f?(h-f>180?f+=360:f-h>180&&(h+=360),p.push({i:d.push(i(d)+"rotate(",null,n)-2,x:ji(h,f)})):f&&d.push(i(d)+"rotate("+f+n)}o(s,"rotate");function l(h,f,d,p){h!==f?p.push({i:d.push(i(d)+"skewX(",null,n)-2,x:ji(h,f)}):f&&d.push(i(d)+"skewX("+f+n)}o(l,"skewX");function u(h,f,d,p,m,g){if(h!==d||f!==p){var y=m.push(i(m)+"scale(",null,",",null,")");g.push({i:y-4,x:ji(h,d)},{i:y-2,x:ji(f,p)})}else(d!==1||p!==1)&&m.push(i(m)+"scale("+d+","+p+")")}return o(u,"scale"),function(h,f){var d=[],p=[];return h=t(h),f=t(f),a(h.translateX,h.translateY,f.translateX,f.translateY,d,p),s(h.rotate,f.rotate,d,p),l(h.skewX,f.skewX,d,p),u(h.scaleX,h.scaleY,f.scaleX,f.scaleY,d,p),h=f=null,function(m){for(var g=-1,y=p.length,v;++g{"use strict";wy();CU();o(SU,"interpolateTransform");OA=SU(kU,"px, ","px)","deg)"),PA=SU(EU,", ",")",")")});function _U(t){return function(e,r){var n=t((e=by(e)).h,(r=by(r)).h),i=hu(e.c,r.c),a=hu(e.l,r.l),s=hu(e.opacity,r.opacity);return function(l){return e.h=n(l),e.c=i(l),e.l=a(l),e.opacity=s(l),e+""}}}var BA,q3e,LU=R(()=>{"use strict";Lp();AA();o(_U,"hcl");BA=_U(hU),q3e=_U(hu)});var Np=R(()=>{"use strict";X4();wy();bU();IA();AU();_A();LU()});function Ay(){return ad||(NU(X3e),ad=Cy.now()+t3)}function X3e(){ad=0}function Sy(){this._call=this._time=this._next=null}function r3(t,e,r){var n=new Sy;return n.restart(t,e,r),n}function MU(){Ay(),++Mp;for(var t=J4,e;t;)(e=ad-t._time)>=0&&t._call.call(void 0,e),t=t._next;--Mp}function DU(){ad=(e3=Cy.now())+t3,Mp=ky=0;try{MU()}finally{Mp=0,K3e(),ad=0}}function j3e(){var t=Cy.now(),e=t-e3;e>RU&&(t3-=e,e3=t)}function K3e(){for(var t,e=J4,r,n=1/0;e;)e._call?(n>e._time&&(n=e._time),t=e,e=e._next):(r=e._next,e._next=null,e=t?t._next=r:J4=r);Ey=t,FA(n)}function FA(t){if(!Mp){ky&&(ky=clearTimeout(ky));var e=t-ad;e>24?(t<1/0&&(ky=setTimeout(DU,t-Cy.now()-t3)),Ty&&(Ty=clearInterval(Ty))):(Ty||(e3=Cy.now(),Ty=setInterval(j3e,RU)),Mp=1,NU(DU))}}var Mp,ky,Ty,RU,J4,Ey,e3,ad,t3,Cy,NU,zA=R(()=>{"use strict";Mp=0,ky=0,Ty=0,RU=1e3,e3=0,ad=0,t3=0,Cy=typeof performance=="object"&&performance.now?performance:Date,NU=typeof window=="object"&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};o(Ay,"now");o(X3e,"clearNow");o(Sy,"Timer");Sy.prototype=r3.prototype={constructor:Sy,restart:o(function(t,e,r){if(typeof t!="function")throw new TypeError("callback is not a function");r=(r==null?Ay():+r)+(e==null?0:+e),!this._next&&Ey!==this&&(Ey?Ey._next=this:J4=this,Ey=this),this._call=t,this._time=r,FA()},"restart"),stop:o(function(){this._call&&(this._call=null,this._time=1/0,FA())},"stop")};o(r3,"timer");o(MU,"timerFlush");o(DU,"wake");o(j3e,"poke");o(K3e,"nap");o(FA,"sleep")});function _y(t,e,r){var n=new Sy;return e=e==null?0:+e,n.restart(i=>{n.stop(),t(i+e)},e,r),n}var IU=R(()=>{"use strict";zA();o(_y,"default")});var n3=R(()=>{"use strict";zA();IU()});function fu(t,e,r,n,i,a){var s=t.__transition;if(!s)t.__transition={};else if(r in s)return;J3e(t,r,{name:e,index:n,group:i,on:Q3e,tween:Z3e,time:a.time,delay:a.delay,duration:a.duration,ease:a.ease,timer:null,state:BU})}function Dy(t,e){var r=Mi(t,e);if(r.state>BU)throw new Error("too late; already scheduled");return r}function ca(t,e){var r=Mi(t,e);if(r.state>i3)throw new Error("too late; already running");return r}function Mi(t,e){var r=t.__transition;if(!r||!(r=r[e]))throw new Error("transition not found");return r}function J3e(t,e,r){var n=t.__transition,i;n[e]=r,r.timer=r3(a,0,r.time);function a(h){r.state=OU,r.timer.restart(s,r.delay,r.time),r.delay<=h&&s(h-r.delay)}o(a,"schedule");function s(h){var f,d,p,m;if(r.state!==OU)return u();for(f in n)if(m=n[f],m.name===r.name){if(m.state===i3)return _y(s);m.state===PU?(m.state=Ly,m.timer.stop(),m.on.call("interrupt",t,t.__data__,m.index,m.group),delete n[f]):+f{"use strict";bS();n3();Q3e=xS("start","end","cancel","interrupt"),Z3e=[],BU=0,OU=1,a3=2,i3=3,PU=4,s3=5,Ly=6;o(fu,"default");o(Dy,"init");o(ca,"set");o(Mi,"get");o(J3e,"create")});function Ry(t,e){var r=t.__transition,n,i,a=!0,s;if(r){e=e==null?null:e+"";for(s in r){if((n=r[s]).name!==e){a=!1;continue}i=n.state>a3&&n.state{"use strict";ys();o(Ry,"default")});function GA(t){return this.each(function(){Ry(this,t)})}var zU=R(()=>{"use strict";FU();o(GA,"default")});function e5e(t,e){var r,n;return function(){var i=ca(this,t),a=i.tween;if(a!==r){n=r=a;for(var s=0,l=n.length;s{"use strict";ys();o(e5e,"tweenRemove");o(t5e,"tweenFunction");o($A,"default");o(Ip,"tweenValue")});function My(t,e){var r;return(typeof e=="number"?ji:e instanceof pl?id:(r=pl(e))?(e=r,id):Rp)(t,e)}var VA=R(()=>{"use strict";Lp();Np();o(My,"default")});function r5e(t){return function(){this.removeAttribute(t)}}function n5e(t){return function(){this.removeAttributeNS(t.space,t.local)}}function i5e(t,e,r){var n,i=r+"",a;return function(){var s=this.getAttribute(t);return s===i?null:s===n?a:a=e(n=s,r)}}function a5e(t,e,r){var n,i=r+"",a;return function(){var s=this.getAttributeNS(t.space,t.local);return s===i?null:s===n?a:a=e(n=s,r)}}function s5e(t,e,r){var n,i,a;return function(){var s,l=r(this),u;return l==null?void this.removeAttribute(t):(s=this.getAttribute(t),u=l+"",s===u?null:s===n&&u===i?a:(i=u,a=e(n=s,l)))}}function o5e(t,e,r){var n,i,a;return function(){var s,l=r(this),u;return l==null?void this.removeAttributeNS(t.space,t.local):(s=this.getAttributeNS(t.space,t.local),u=l+"",s===u?null:s===n&&u===i?a:(i=u,a=e(n=s,l)))}}function UA(t,e){var r=ic(t),n=r==="transform"?PA:My;return this.attrTween(t,typeof e=="function"?(r.local?o5e:s5e)(r,n,Ip(this,"attr."+t,e)):e==null?(r.local?n5e:r5e)(r):(r.local?a5e:i5e)(r,n,e))}var GU=R(()=>{"use strict";Np();fl();Ny();VA();o(r5e,"attrRemove");o(n5e,"attrRemoveNS");o(i5e,"attrConstant");o(a5e,"attrConstantNS");o(s5e,"attrFunction");o(o5e,"attrFunctionNS");o(UA,"default")});function l5e(t,e){return function(r){this.setAttribute(t,e.call(this,r))}}function c5e(t,e){return function(r){this.setAttributeNS(t.space,t.local,e.call(this,r))}}function u5e(t,e){var r,n;function i(){var a=e.apply(this,arguments);return a!==n&&(r=(n=a)&&c5e(t,a)),r}return o(i,"tween"),i._value=e,i}function h5e(t,e){var r,n;function i(){var a=e.apply(this,arguments);return a!==n&&(r=(n=a)&&l5e(t,a)),r}return o(i,"tween"),i._value=e,i}function HA(t,e){var r="attr."+t;if(arguments.length<2)return(r=this.tween(r))&&r._value;if(e==null)return this.tween(r,null);if(typeof e!="function")throw new Error;var n=ic(t);return this.tween(r,(n.local?u5e:h5e)(n,e))}var $U=R(()=>{"use strict";fl();o(l5e,"attrInterpolate");o(c5e,"attrInterpolateNS");o(u5e,"attrTweenNS");o(h5e,"attrTween");o(HA,"default")});function f5e(t,e){return function(){Dy(this,t).delay=+e.apply(this,arguments)}}function d5e(t,e){return e=+e,function(){Dy(this,t).delay=e}}function YA(t){var e=this._id;return arguments.length?this.each((typeof t=="function"?f5e:d5e)(e,t)):Mi(this.node(),e).delay}var VU=R(()=>{"use strict";ys();o(f5e,"delayFunction");o(d5e,"delayConstant");o(YA,"default")});function p5e(t,e){return function(){ca(this,t).duration=+e.apply(this,arguments)}}function m5e(t,e){return e=+e,function(){ca(this,t).duration=e}}function WA(t){var e=this._id;return arguments.length?this.each((typeof t=="function"?p5e:m5e)(e,t)):Mi(this.node(),e).duration}var UU=R(()=>{"use strict";ys();o(p5e,"durationFunction");o(m5e,"durationConstant");o(WA,"default")});function g5e(t,e){if(typeof e!="function")throw new Error;return function(){ca(this,t).ease=e}}function qA(t){var e=this._id;return arguments.length?this.each(g5e(e,t)):Mi(this.node(),e).ease}var HU=R(()=>{"use strict";ys();o(g5e,"easeConstant");o(qA,"default")});function y5e(t,e){return function(){var r=e.apply(this,arguments);if(typeof r!="function")throw new Error;ca(this,t).ease=r}}function XA(t){if(typeof t!="function")throw new Error;return this.each(y5e(this._id,t))}var YU=R(()=>{"use strict";ys();o(y5e,"easeVarying");o(XA,"default")});function jA(t){typeof t!="function"&&(t=Ep(t));for(var e=this._groups,r=e.length,n=new Array(r),i=0;i{"use strict";fl();sd();o(jA,"default")});function KA(t){if(t._id!==this._id)throw new Error;for(var e=this._groups,r=t._groups,n=e.length,i=r.length,a=Math.min(n,i),s=new Array(n),l=0;l{"use strict";sd();o(KA,"default")});function v5e(t){return(t+"").trim().split(/^|\s+/).every(function(e){var r=e.indexOf(".");return r>=0&&(e=e.slice(0,r)),!e||e==="start"})}function x5e(t,e,r){var n,i,a=v5e(e)?Dy:ca;return function(){var s=a(this,t),l=s.on;l!==n&&(i=(n=l).copy()).on(e,r),s.on=i}}function QA(t,e){var r=this._id;return arguments.length<2?Mi(this.node(),r).on.on(t):this.each(x5e(r,t,e))}var XU=R(()=>{"use strict";ys();o(v5e,"start");o(x5e,"onFunction");o(QA,"default")});function b5e(t){return function(){var e=this.parentNode;for(var r in this.__transition)if(+r!==t)return;e&&e.removeChild(this)}}function ZA(){return this.on("end.remove",b5e(this._id))}var jU=R(()=>{"use strict";o(b5e,"removeFunction");o(ZA,"default")});function JA(t){var e=this._name,r=this._id;typeof t!="function"&&(t=wh(t));for(var n=this._groups,i=n.length,a=new Array(i),s=0;s{"use strict";fl();sd();ys();o(JA,"default")});function e8(t){var e=this._name,r=this._id;typeof t!="function"&&(t=kp(t));for(var n=this._groups,i=n.length,a=[],s=[],l=0;l{"use strict";fl();sd();ys();o(e8,"default")});function t8(){return new w5e(this._groups,this._parents)}var w5e,ZU=R(()=>{"use strict";fl();w5e=cu.prototype.constructor;o(t8,"default")});function T5e(t,e){var r,n,i;return function(){var a=Th(this,t),s=(this.style.removeProperty(t),Th(this,t));return a===s?null:a===r&&s===n?i:i=e(r=a,n=s)}}function JU(t){return function(){this.style.removeProperty(t)}}function k5e(t,e,r){var n,i=r+"",a;return function(){var s=Th(this,t);return s===i?null:s===n?a:a=e(n=s,r)}}function E5e(t,e,r){var n,i,a;return function(){var s=Th(this,t),l=r(this),u=l+"";return l==null&&(u=l=(this.style.removeProperty(t),Th(this,t))),s===u?null:s===n&&u===i?a:(i=u,a=e(n=s,l))}}function C5e(t,e){var r,n,i,a="style."+e,s="end."+a,l;return function(){var u=ca(this,t),h=u.on,f=u.value[a]==null?l||(l=JU(e)):void 0;(h!==r||i!==f)&&(n=(r=h).copy()).on(s,i=f),u.on=n}}function r8(t,e,r){var n=(t+="")=="transform"?OA:My;return e==null?this.styleTween(t,T5e(t,n)).on("end.style."+t,JU(t)):typeof e=="function"?this.styleTween(t,E5e(t,n,Ip(this,"style."+t,e))).each(C5e(this._id,t)):this.styleTween(t,k5e(t,n,e),r).on("end.style."+t,null)}var eH=R(()=>{"use strict";Np();fl();ys();Ny();VA();o(T5e,"styleNull");o(JU,"styleRemove");o(k5e,"styleConstant");o(E5e,"styleFunction");o(C5e,"styleMaybeRemove");o(r8,"default")});function S5e(t,e,r){return function(n){this.style.setProperty(t,e.call(this,n),r)}}function A5e(t,e,r){var n,i;function a(){var s=e.apply(this,arguments);return s!==i&&(n=(i=s)&&S5e(t,s,r)),n}return o(a,"tween"),a._value=e,a}function n8(t,e,r){var n="style."+(t+="");if(arguments.length<2)return(n=this.tween(n))&&n._value;if(e==null)return this.tween(n,null);if(typeof e!="function")throw new Error;return this.tween(n,A5e(t,e,r??""))}var tH=R(()=>{"use strict";o(S5e,"styleInterpolate");o(A5e,"styleTween");o(n8,"default")});function _5e(t){return function(){this.textContent=t}}function L5e(t){return function(){var e=t(this);this.textContent=e??""}}function i8(t){return this.tween("text",typeof t=="function"?L5e(Ip(this,"text",t)):_5e(t==null?"":t+""))}var rH=R(()=>{"use strict";Ny();o(_5e,"textConstant");o(L5e,"textFunction");o(i8,"default")});function D5e(t){return function(e){this.textContent=t.call(this,e)}}function R5e(t){var e,r;function n(){var i=t.apply(this,arguments);return i!==r&&(e=(r=i)&&D5e(i)),e}return o(n,"tween"),n._value=t,n}function a8(t){var e="text";if(arguments.length<1)return(e=this.tween(e))&&e._value;if(t==null)return this.tween(e,null);if(typeof t!="function")throw new Error;return this.tween(e,R5e(t))}var nH=R(()=>{"use strict";o(D5e,"textInterpolate");o(R5e,"textTween");o(a8,"default")});function s8(){for(var t=this._name,e=this._id,r=o3(),n=this._groups,i=n.length,a=0;a{"use strict";sd();ys();o(s8,"default")});function o8(){var t,e,r=this,n=r._id,i=r.size();return new Promise(function(a,s){var l={value:s},u={value:o(function(){--i===0&&a()},"value")};r.each(function(){var h=ca(this,n),f=h.on;f!==t&&(e=(t=f).copy(),e._.cancel.push(l),e._.interrupt.push(l),e._.end.push(u)),h.on=e}),i===0&&a()})}var aH=R(()=>{"use strict";ys();o(o8,"default")});function Ka(t,e,r,n){this._groups=t,this._parents=e,this._name=r,this._id=n}function sH(t){return cu().transition(t)}function o3(){return++N5e}var N5e,du,sd=R(()=>{"use strict";fl();GU();$U();VU();UU();HU();YU();WU();qU();XU();jU();KU();QU();ZU();eH();tH();rH();nH();iH();Ny();aH();N5e=0;o(Ka,"Transition");o(sH,"transition");o(o3,"newId");du=cu.prototype;Ka.prototype=sH.prototype={constructor:Ka,select:JA,selectAll:e8,selectChild:du.selectChild,selectChildren:du.selectChildren,filter:jA,merge:KA,selection:t8,transition:s8,call:du.call,nodes:du.nodes,node:du.node,size:du.size,empty:du.empty,each:du.each,on:QA,attr:UA,attrTween:HA,style:r8,styleTween:n8,text:i8,textTween:a8,remove:ZA,tween:$A,delay:YA,duration:WA,ease:qA,easeVarying:XA,end:o8,[Symbol.iterator]:du[Symbol.iterator]}});function l3(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}var oH=R(()=>{"use strict";o(l3,"cubicInOut")});var l8=R(()=>{"use strict";oH()});function I5e(t,e){for(var r;!(r=t.__transition)||!(r=r[e]);)if(!(t=t.parentNode))throw new Error(`transition ${e} not found`);return r}function c8(t){var e,r;t instanceof Ka?(e=t._id,t=t._name):(e=o3(),(r=M5e).time=Ay(),t=t==null?null:t+"");for(var n=this._groups,i=n.length,a=0;a{"use strict";sd();ys();l8();n3();M5e={time:null,delay:0,duration:250,ease:l3};o(I5e,"inherit");o(c8,"default")});var cH=R(()=>{"use strict";fl();zU();lH();cu.prototype.interrupt=GA;cu.prototype.transition=c8});var c3=R(()=>{"use strict";cH()});var uH=R(()=>{"use strict"});var hH=R(()=>{"use strict"});var fH=R(()=>{"use strict"});function dH(t){return[+t[0],+t[1]]}function O5e(t){return[dH(t[0]),dH(t[1])]}function u8(t){return{type:t}}var Kpt,Qpt,Zpt,Jpt,emt,tmt,pH=R(()=>{"use strict";c3();uH();hH();fH();({abs:Kpt,max:Qpt,min:Zpt}=Math);o(dH,"number1");o(O5e,"number2");Jpt={name:"x",handles:["w","e"].map(u8),input:o(function(t,e){return t==null?null:[[+t[0],e[0][1]],[+t[1],e[1][1]]]},"input"),output:o(function(t){return t&&[t[0][0],t[1][0]]},"output")},emt={name:"y",handles:["n","s"].map(u8),input:o(function(t,e){return t==null?null:[[e[0][0],+t[0]],[e[1][0],+t[1]]]},"input"),output:o(function(t){return t&&[t[0][1],t[1][1]]},"output")},tmt={name:"xy",handles:["n","w","e","s","nw","ne","sw","se"].map(u8),input:o(function(t){return t==null?null:O5e(t)},"input"),output:o(function(t){return t},"output")};o(u8,"type")});var mH=R(()=>{"use strict";pH()});function gH(t){this._+=t[0];for(let e=1,r=t.length;e=0))throw new Error(`invalid digits: ${t}`);if(e>15)return gH;let r=10**e;return function(n){this._+=n[0];for(let i=1,a=n.length;i{"use strict";h8=Math.PI,f8=2*h8,od=1e-6,P5e=f8-od;o(gH,"append");o(B5e,"appendRound");ld=class{static{o(this,"Path")}constructor(e){this._x0=this._y0=this._x1=this._y1=null,this._="",this._append=e==null?gH:B5e(e)}moveTo(e,r){this._append`M${this._x0=this._x1=+e},${this._y0=this._y1=+r}`}closePath(){this._x1!==null&&(this._x1=this._x0,this._y1=this._y0,this._append`Z`)}lineTo(e,r){this._append`L${this._x1=+e},${this._y1=+r}`}quadraticCurveTo(e,r,n,i){this._append`Q${+e},${+r},${this._x1=+n},${this._y1=+i}`}bezierCurveTo(e,r,n,i,a,s){this._append`C${+e},${+r},${+n},${+i},${this._x1=+a},${this._y1=+s}`}arcTo(e,r,n,i,a){if(e=+e,r=+r,n=+n,i=+i,a=+a,a<0)throw new Error(`negative radius: ${a}`);let s=this._x1,l=this._y1,u=n-e,h=i-r,f=s-e,d=l-r,p=f*f+d*d;if(this._x1===null)this._append`M${this._x1=e},${this._y1=r}`;else if(p>od)if(!(Math.abs(d*u-h*f)>od)||!a)this._append`L${this._x1=e},${this._y1=r}`;else{let m=n-s,g=i-l,y=u*u+h*h,v=m*m+g*g,x=Math.sqrt(y),b=Math.sqrt(p),w=a*Math.tan((h8-Math.acos((y+p-v)/(2*x*b)))/2),S=w/b,T=w/x;Math.abs(S-1)>od&&this._append`L${e+S*f},${r+S*d}`,this._append`A${a},${a},0,0,${+(d*m>f*g)},${this._x1=e+T*u},${this._y1=r+T*h}`}}arc(e,r,n,i,a,s){if(e=+e,r=+r,n=+n,s=!!s,n<0)throw new Error(`negative radius: ${n}`);let l=n*Math.cos(i),u=n*Math.sin(i),h=e+l,f=r+u,d=1^s,p=s?i-a:a-i;this._x1===null?this._append`M${h},${f}`:(Math.abs(this._x1-h)>od||Math.abs(this._y1-f)>od)&&this._append`L${h},${f}`,n&&(p<0&&(p=p%f8+f8),p>P5e?this._append`A${n},${n},0,1,${d},${e-l},${r-u}A${n},${n},0,1,${d},${this._x1=h},${this._y1=f}`:p>od&&this._append`A${n},${n},0,${+(p>=h8)},${d},${this._x1=e+n*Math.cos(a)},${this._y1=r+n*Math.sin(a)}`)}rect(e,r,n,i){this._append`M${this._x0=this._x1=+e},${this._y0=this._y1=+r}h${n=+n}v${+i}h${-n}Z`}toString(){return this._}};o(yH,"path");yH.prototype=ld.prototype});var d8=R(()=>{"use strict";vH()});var xH=R(()=>{"use strict"});var bH=R(()=>{"use strict"});var wH=R(()=>{"use strict"});var TH=R(()=>{"use strict"});var kH=R(()=>{"use strict"});var EH=R(()=>{"use strict"});var CH=R(()=>{"use strict"});function p8(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)}function cd(t,e){if((r=(t=e?t.toExponential(e-1):t.toExponential()).indexOf("e"))<0)return null;var r,n=t.slice(0,r);return[n.length>1?n[0]+n.slice(2):n,+t.slice(r+1)]}var Iy=R(()=>{"use strict";o(p8,"default");o(cd,"formatDecimalParts")});function ml(t){return t=cd(Math.abs(t)),t?t[1]:NaN}var Oy=R(()=>{"use strict";Iy();o(ml,"default")});function m8(t,e){return function(r,n){for(var i=r.length,a=[],s=0,l=t[0],u=0;i>0&&l>0&&(u+l+1>n&&(l=Math.max(1,n-u)),a.push(r.substring(i-=l,i+l)),!((u+=l+1)>n));)l=t[s=(s+1)%t.length];return a.reverse().join(e)}}var SH=R(()=>{"use strict";o(m8,"default")});function g8(t){return function(e){return e.replace(/[0-9]/g,function(r){return t[+r]})}}var AH=R(()=>{"use strict";o(g8,"default")});function Sh(t){if(!(e=F5e.exec(t)))throw new Error("invalid format: "+t);var e;return new u3({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}function u3(t){this.fill=t.fill===void 0?" ":t.fill+"",this.align=t.align===void 0?">":t.align+"",this.sign=t.sign===void 0?"-":t.sign+"",this.symbol=t.symbol===void 0?"":t.symbol+"",this.zero=!!t.zero,this.width=t.width===void 0?void 0:+t.width,this.comma=!!t.comma,this.precision=t.precision===void 0?void 0:+t.precision,this.trim=!!t.trim,this.type=t.type===void 0?"":t.type+""}var F5e,y8=R(()=>{"use strict";F5e=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;o(Sh,"formatSpecifier");Sh.prototype=u3.prototype;o(u3,"FormatSpecifier");u3.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(this.width===void 0?"":Math.max(1,this.width|0))+(this.comma?",":"")+(this.precision===void 0?"":"."+Math.max(0,this.precision|0))+(this.trim?"~":"")+this.type}});function v8(t){e:for(var e=t.length,r=1,n=-1,i;r0&&(n=0);break}return n>0?t.slice(0,n)+t.slice(i+1):t}var _H=R(()=>{"use strict";o(v8,"default")});function b8(t,e){var r=cd(t,e);if(!r)return t+"";var n=r[0],i=r[1],a=i-(x8=Math.max(-8,Math.min(8,Math.floor(i/3)))*3)+1,s=n.length;return a===s?n:a>s?n+new Array(a-s+1).join("0"):a>0?n.slice(0,a)+"."+n.slice(a):"0."+new Array(1-a).join("0")+cd(t,Math.max(0,e+a-1))[0]}var x8,w8=R(()=>{"use strict";Iy();o(b8,"default")});function h3(t,e){var r=cd(t,e);if(!r)return t+"";var n=r[0],i=r[1];return i<0?"0."+new Array(-i).join("0")+n:n.length>i+1?n.slice(0,i+1)+"."+n.slice(i+1):n+new Array(i-n.length+2).join("0")}var LH=R(()=>{"use strict";Iy();o(h3,"default")});var T8,DH=R(()=>{"use strict";Iy();w8();LH();T8={"%":o((t,e)=>(t*100).toFixed(e),"%"),b:o(t=>Math.round(t).toString(2),"b"),c:o(t=>t+"","c"),d:p8,e:o((t,e)=>t.toExponential(e),"e"),f:o((t,e)=>t.toFixed(e),"f"),g:o((t,e)=>t.toPrecision(e),"g"),o:o(t=>Math.round(t).toString(8),"o"),p:o((t,e)=>h3(t*100,e),"p"),r:h3,s:b8,X:o(t=>Math.round(t).toString(16).toUpperCase(),"X"),x:o(t=>Math.round(t).toString(16),"x")}});function f3(t){return t}var RH=R(()=>{"use strict";o(f3,"default")});function k8(t){var e=t.grouping===void 0||t.thousands===void 0?f3:m8(NH.call(t.grouping,Number),t.thousands+""),r=t.currency===void 0?"":t.currency[0]+"",n=t.currency===void 0?"":t.currency[1]+"",i=t.decimal===void 0?".":t.decimal+"",a=t.numerals===void 0?f3:g8(NH.call(t.numerals,String)),s=t.percent===void 0?"%":t.percent+"",l=t.minus===void 0?"\u2212":t.minus+"",u=t.nan===void 0?"NaN":t.nan+"";function h(d){d=Sh(d);var p=d.fill,m=d.align,g=d.sign,y=d.symbol,v=d.zero,x=d.width,b=d.comma,w=d.precision,S=d.trim,T=d.type;T==="n"?(b=!0,T="g"):T8[T]||(w===void 0&&(w=12),S=!0,T="g"),(v||p==="0"&&m==="=")&&(v=!0,p="0",m="=");var E=y==="$"?r:y==="#"&&/[boxX]/.test(T)?"0"+T.toLowerCase():"",_=y==="$"?n:/[%p]/.test(T)?s:"",A=T8[T],L=/[defgprs%]/.test(T);w=w===void 0?6:/[gprs]/.test(T)?Math.max(1,Math.min(21,w)):Math.max(0,Math.min(20,w));function M(N){var k=E,I=_,C,O,D;if(T==="c")I=A(N)+I,N="";else{N=+N;var P=N<0||1/N<0;if(N=isNaN(N)?u:A(Math.abs(N),w),S&&(N=v8(N)),P&&+N==0&&g!=="+"&&(P=!1),k=(P?g==="("?g:l:g==="-"||g==="("?"":g)+k,I=(T==="s"?MH[8+x8/3]:"")+I+(P&&g==="("?")":""),L){for(C=-1,O=N.length;++CD||D>57){I=(D===46?i+N.slice(C+1):N.slice(C))+I,N=N.slice(0,C);break}}}b&&!v&&(N=e(N,1/0));var F=k.length+N.length+I.length,B=F>1)+k+N+I+B.slice(F);break;default:N=B+k+N+I;break}return a(N)}return o(M,"format"),M.toString=function(){return d+""},M}o(h,"newFormat");function f(d,p){var m=h((d=Sh(d),d.type="f",d)),g=Math.max(-8,Math.min(8,Math.floor(ml(p)/3)))*3,y=Math.pow(10,-g),v=MH[8+g/3];return function(x){return m(y*x)+v}}return o(f,"formatPrefix"),{format:h,formatPrefix:f}}var NH,MH,IH=R(()=>{"use strict";Oy();SH();AH();y8();_H();DH();w8();RH();NH=Array.prototype.map,MH=["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"];o(k8,"default")});function E8(t){return d3=k8(t),p3=d3.format,m3=d3.formatPrefix,d3}var d3,p3,m3,OH=R(()=>{"use strict";IH();E8({thousands:",",grouping:[3],currency:["$",""]});o(E8,"defaultLocale")});function g3(t){return Math.max(0,-ml(Math.abs(t)))}var PH=R(()=>{"use strict";Oy();o(g3,"default")});function y3(t,e){return Math.max(0,Math.max(-8,Math.min(8,Math.floor(ml(e)/3)))*3-ml(Math.abs(t)))}var BH=R(()=>{"use strict";Oy();o(y3,"default")});function v3(t,e){return t=Math.abs(t),e=Math.abs(e)-t,Math.max(0,ml(e)-ml(t))+1}var FH=R(()=>{"use strict";Oy();o(v3,"default")});var C8=R(()=>{"use strict";OH();y8();PH();BH();FH()});var zH=R(()=>{"use strict"});var GH=R(()=>{"use strict"});var $H=R(()=>{"use strict"});var VH=R(()=>{"use strict"});function Ah(t,e){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(e).domain(t);break}return this}var Py=R(()=>{"use strict";o(Ah,"initRange")});function pu(){var t=new wp,e=[],r=[],n=S8;function i(a){let s=t.get(a);if(s===void 0){if(n!==S8)return n;t.set(a,s=e.push(a)-1)}return r[s%r.length]}return o(i,"scale"),i.domain=function(a){if(!arguments.length)return e.slice();e=[],t=new wp;for(let s of a)t.has(s)||t.set(s,e.push(s)-1);return i},i.range=function(a){return arguments.length?(r=Array.from(a),i):r.slice()},i.unknown=function(a){return arguments.length?(n=a,i):n},i.copy=function(){return pu(e,r).unknown(n)},Ah.apply(i,arguments),i}var S8,A8=R(()=>{"use strict";bh();Py();S8=Symbol("implicit");o(pu,"ordinal")});function Op(){var t=pu().unknown(void 0),e=t.domain,r=t.range,n=0,i=1,a,s,l=!1,u=0,h=0,f=.5;delete t.unknown;function d(){var p=e().length,m=i{"use strict";bh();Py();A8();o(Op,"band")});function _8(t){return function(){return t}}var HH=R(()=>{"use strict";o(_8,"constants")});function L8(t){return+t}var YH=R(()=>{"use strict";o(L8,"number")});function Pp(t){return t}function D8(t,e){return(e-=t=+t)?function(r){return(r-t)/e}:_8(isNaN(e)?NaN:.5)}function z5e(t,e){var r;return t>e&&(r=t,t=e,e=r),function(n){return Math.max(t,Math.min(e,n))}}function G5e(t,e,r){var n=t[0],i=t[1],a=e[0],s=e[1];return i2?$5e:G5e,u=h=null,d}o(f,"rescale");function d(p){return p==null||isNaN(p=+p)?a:(u||(u=l(t.map(n),e,r)))(n(s(p)))}return o(d,"scale"),d.invert=function(p){return s(i((h||(h=l(e,t.map(n),ji)))(p)))},d.domain=function(p){return arguments.length?(t=Array.from(p,L8),f()):t.slice()},d.range=function(p){return arguments.length?(e=Array.from(p),f()):e.slice()},d.rangeRound=function(p){return e=Array.from(p),r=j4,f()},d.clamp=function(p){return arguments.length?(s=p?!0:Pp,f()):s!==Pp},d.interpolate=function(p){return arguments.length?(r=p,f()):r},d.unknown=function(p){return arguments.length?(a=p,d):a},function(p,m){return n=p,i=m,f()}}function By(){return V5e()(Pp,Pp)}var WH,R8=R(()=>{"use strict";bh();Np();HH();YH();WH=[0,1];o(Pp,"identity");o(D8,"normalize");o(z5e,"clamper");o(G5e,"bimap");o($5e,"polymap");o(x3,"copy");o(V5e,"transformer");o(By,"continuous")});function N8(t,e,r,n){var i=Tp(t,e,r),a;switch(n=Sh(n??",f"),n.type){case"s":{var s=Math.max(Math.abs(t),Math.abs(e));return n.precision==null&&!isNaN(a=y3(i,s))&&(n.precision=a),m3(n,s)}case"":case"e":case"g":case"p":case"r":{n.precision==null&&!isNaN(a=v3(i,Math.max(Math.abs(t),Math.abs(e))))&&(n.precision=a-(n.type==="e"));break}case"f":case"%":{n.precision==null&&!isNaN(a=g3(i))&&(n.precision=a-(n.type==="%")*2);break}}return p3(n)}var qH=R(()=>{"use strict";bh();C8();o(N8,"tickFormat")});function U5e(t){var e=t.domain;return t.ticks=function(r){var n=e();return N4(n[0],n[n.length-1],r??10)},t.tickFormat=function(r,n){var i=e();return N8(i[0],i[i.length-1],r??10,n)},t.nice=function(r){r==null&&(r=10);var n=e(),i=0,a=n.length-1,s=n[i],l=n[a],u,h,f=10;for(l0;){if(h=fy(s,l,r),h===u)return n[i]=s,n[a]=l,e(n);if(h>0)s=Math.floor(s/h)*h,l=Math.ceil(l/h)*h;else if(h<0)s=Math.ceil(s*h)/h,l=Math.floor(l*h)/h;else break;u=h}return t},t}function gl(){var t=By();return t.copy=function(){return x3(t,gl())},Ah.apply(t,arguments),U5e(t)}var XH=R(()=>{"use strict";bh();R8();Py();qH();o(U5e,"linearish");o(gl,"linear")});function M8(t,e){t=t.slice();var r=0,n=t.length-1,i=t[r],a=t[n],s;return a{"use strict";o(M8,"nice")});function dn(t,e,r,n){function i(a){return t(a=arguments.length===0?new Date:new Date(+a)),a}return o(i,"interval"),i.floor=a=>(t(a=new Date(+a)),a),i.ceil=a=>(t(a=new Date(a-1)),e(a,1),t(a),a),i.round=a=>{let s=i(a),l=i.ceil(a);return a-s(e(a=new Date(+a),s==null?1:Math.floor(s)),a),i.range=(a,s,l)=>{let u=[];if(a=i.ceil(a),l=l==null?1:Math.floor(l),!(a0))return u;let h;do u.push(h=new Date(+a)),e(a,l),t(a);while(hdn(s=>{if(s>=s)for(;t(s),!a(s);)s.setTime(s-1)},(s,l)=>{if(s>=s)if(l<0)for(;++l<=0;)for(;e(s,-1),!a(s););else for(;--l>=0;)for(;e(s,1),!a(s););}),r&&(i.count=(a,s)=>(I8.setTime(+a),O8.setTime(+s),t(I8),t(O8),Math.floor(r(I8,O8))),i.every=a=>(a=Math.floor(a),!isFinite(a)||!(a>0)?null:a>1?i.filter(n?s=>n(s)%a===0:s=>i.count(0,s)%a===0):i)),i}var I8,O8,mu=R(()=>{"use strict";I8=new Date,O8=new Date;o(dn,"timeInterval")});var oc,KH,P8=R(()=>{"use strict";mu();oc=dn(()=>{},(t,e)=>{t.setTime(+t+e)},(t,e)=>e-t);oc.every=t=>(t=Math.floor(t),!isFinite(t)||!(t>0)?null:t>1?dn(e=>{e.setTime(Math.floor(e/t)*t)},(e,r)=>{e.setTime(+e+r*t)},(e,r)=>(r-e)/t):oc);KH=oc.range});var Ks,QH,B8=R(()=>{"use strict";mu();Ks=dn(t=>{t.setTime(t-t.getMilliseconds())},(t,e)=>{t.setTime(+t+e*1e3)},(t,e)=>(e-t)/1e3,t=>t.getUTCSeconds()),QH=Ks.range});var gu,H5e,b3,Y5e,F8=R(()=>{"use strict";mu();gu=dn(t=>{t.setTime(t-t.getMilliseconds()-t.getSeconds()*1e3)},(t,e)=>{t.setTime(+t+e*6e4)},(t,e)=>(e-t)/6e4,t=>t.getMinutes()),H5e=gu.range,b3=dn(t=>{t.setUTCSeconds(0,0)},(t,e)=>{t.setTime(+t+e*6e4)},(t,e)=>(e-t)/6e4,t=>t.getUTCMinutes()),Y5e=b3.range});var yu,W5e,w3,q5e,z8=R(()=>{"use strict";mu();yu=dn(t=>{t.setTime(t-t.getMilliseconds()-t.getSeconds()*1e3-t.getMinutes()*6e4)},(t,e)=>{t.setTime(+t+e*36e5)},(t,e)=>(e-t)/36e5,t=>t.getHours()),W5e=yu.range,w3=dn(t=>{t.setUTCMinutes(0,0,0)},(t,e)=>{t.setTime(+t+e*36e5)},(t,e)=>(e-t)/36e5,t=>t.getUTCHours()),q5e=w3.range});var Do,X5e,zy,j5e,T3,K5e,G8=R(()=>{"use strict";mu();Do=dn(t=>t.setHours(0,0,0,0),(t,e)=>t.setDate(t.getDate()+e),(t,e)=>(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*6e4)/864e5,t=>t.getDate()-1),X5e=Do.range,zy=dn(t=>{t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCDate(t.getUTCDate()+e)},(t,e)=>(e-t)/864e5,t=>t.getUTCDate()-1),j5e=zy.range,T3=dn(t=>{t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCDate(t.getUTCDate()+e)},(t,e)=>(e-t)/864e5,t=>Math.floor(t/864e5)),K5e=T3.range});function fd(t){return dn(e=>{e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)},(e,r)=>{e.setDate(e.getDate()+r*7)},(e,r)=>(r-e-(r.getTimezoneOffset()-e.getTimezoneOffset())*6e4)/6048e5)}function dd(t){return dn(e=>{e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)},(e,r)=>{e.setUTCDate(e.getUTCDate()+r*7)},(e,r)=>(r-e)/6048e5)}var yl,_h,k3,E3,cc,C3,S3,JH,Q5e,Z5e,J5e,ewe,twe,rwe,pd,Bp,eY,tY,Lh,rY,nY,iY,nwe,iwe,awe,swe,owe,lwe,$8=R(()=>{"use strict";mu();o(fd,"timeWeekday");yl=fd(0),_h=fd(1),k3=fd(2),E3=fd(3),cc=fd(4),C3=fd(5),S3=fd(6),JH=yl.range,Q5e=_h.range,Z5e=k3.range,J5e=E3.range,ewe=cc.range,twe=C3.range,rwe=S3.range;o(dd,"utcWeekday");pd=dd(0),Bp=dd(1),eY=dd(2),tY=dd(3),Lh=dd(4),rY=dd(5),nY=dd(6),iY=pd.range,nwe=Bp.range,iwe=eY.range,awe=tY.range,swe=Lh.range,owe=rY.range,lwe=nY.range});var vu,cwe,A3,uwe,V8=R(()=>{"use strict";mu();vu=dn(t=>{t.setDate(1),t.setHours(0,0,0,0)},(t,e)=>{t.setMonth(t.getMonth()+e)},(t,e)=>e.getMonth()-t.getMonth()+(e.getFullYear()-t.getFullYear())*12,t=>t.getMonth()),cwe=vu.range,A3=dn(t=>{t.setUTCDate(1),t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCMonth(t.getUTCMonth()+e)},(t,e)=>e.getUTCMonth()-t.getUTCMonth()+(e.getUTCFullYear()-t.getUTCFullYear())*12,t=>t.getUTCMonth()),uwe=A3.range});var Qs,hwe,vl,fwe,U8=R(()=>{"use strict";mu();Qs=dn(t=>{t.setMonth(0,1),t.setHours(0,0,0,0)},(t,e)=>{t.setFullYear(t.getFullYear()+e)},(t,e)=>e.getFullYear()-t.getFullYear(),t=>t.getFullYear());Qs.every=t=>!isFinite(t=Math.floor(t))||!(t>0)?null:dn(e=>{e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)},(e,r)=>{e.setFullYear(e.getFullYear()+r*t)});hwe=Qs.range,vl=dn(t=>{t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCFullYear(t.getUTCFullYear()+e)},(t,e)=>e.getUTCFullYear()-t.getUTCFullYear(),t=>t.getUTCFullYear());vl.every=t=>!isFinite(t=Math.floor(t))||!(t>0)?null:dn(e=>{e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)},(e,r)=>{e.setUTCFullYear(e.getUTCFullYear()+r*t)});fwe=vl.range});function sY(t,e,r,n,i,a){let s=[[Ks,1,1e3],[Ks,5,5*1e3],[Ks,15,15*1e3],[Ks,30,30*1e3],[a,1,6e4],[a,5,5*6e4],[a,15,15*6e4],[a,30,30*6e4],[i,1,36e5],[i,3,3*36e5],[i,6,6*36e5],[i,12,12*36e5],[n,1,864e5],[n,2,2*864e5],[r,1,6048e5],[e,1,2592e6],[e,3,3*2592e6],[t,1,31536e6]];function l(h,f,d){let p=fv).right(s,p);if(m===s.length)return t.every(Tp(h/31536e6,f/31536e6,d));if(m===0)return oc.every(Math.max(Tp(h,f,d),1));let[g,y]=s[p/s[m-1][2]{"use strict";bh();P8();B8();F8();z8();G8();$8();V8();U8();o(sY,"ticker");[pwe,mwe]=sY(vl,A3,pd,T3,w3,b3),[H8,Y8]=sY(Qs,vu,yl,Do,yu,gu)});var _3=R(()=>{"use strict";P8();B8();F8();z8();G8();$8();V8();U8();oY()});function W8(t){if(0<=t.y&&t.y<100){var e=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return e.setFullYear(t.y),e}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function q8(t){if(0<=t.y&&t.y<100){var e=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return e.setUTCFullYear(t.y),e}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function Gy(t,e,r){return{y:t,m:e,d:r,H:0,M:0,S:0,L:0}}function X8(t){var e=t.dateTime,r=t.date,n=t.time,i=t.periods,a=t.days,s=t.shortDays,l=t.months,u=t.shortMonths,h=$y(i),f=Vy(i),d=$y(a),p=Vy(a),m=$y(s),g=Vy(s),y=$y(l),v=Vy(l),x=$y(u),b=Vy(u),w={a:P,A:F,b:B,B:$,c:null,d:dY,e:dY,f:Fwe,g:Xwe,G:Kwe,H:Owe,I:Pwe,j:Bwe,L:vY,m:zwe,M:Gwe,p:z,q:Y,Q:gY,s:yY,S:$we,u:Vwe,U:Uwe,V:Hwe,w:Ywe,W:Wwe,x:null,X:null,y:qwe,Y:jwe,Z:Qwe,"%":mY},S={a:Q,A:X,b:ie,B:j,c:null,d:pY,e:pY,f:tTe,g:hTe,G:dTe,H:Zwe,I:Jwe,j:eTe,L:bY,m:rTe,M:nTe,p:J,q:Z,Q:gY,s:yY,S:iTe,u:aTe,U:sTe,V:oTe,w:lTe,W:cTe,x:null,X:null,y:uTe,Y:fTe,Z:pTe,"%":mY},T={a:M,A:N,b:k,B:I,c:C,d:hY,e:hY,f:Rwe,g:uY,G:cY,H:fY,I:fY,j:Awe,L:Dwe,m:Swe,M:_we,p:L,q:Cwe,Q:Mwe,s:Iwe,S:Lwe,u:bwe,U:wwe,V:Twe,w:xwe,W:kwe,x:O,X:D,y:uY,Y:cY,Z:Ewe,"%":Nwe};w.x=E(r,w),w.X=E(n,w),w.c=E(e,w),S.x=E(r,S),S.X=E(n,S),S.c=E(e,S);function E(H,q){return function(K){var se=[],ce=-1,ue=0,te=H.length,De,oe,ke;for(K instanceof Date||(K=new Date(+K));++ce53)return null;"w"in se||(se.w=1),"Z"in se?(ue=q8(Gy(se.y,0,1)),te=ue.getUTCDay(),ue=te>4||te===0?Bp.ceil(ue):Bp(ue),ue=zy.offset(ue,(se.V-1)*7),se.y=ue.getUTCFullYear(),se.m=ue.getUTCMonth(),se.d=ue.getUTCDate()+(se.w+6)%7):(ue=W8(Gy(se.y,0,1)),te=ue.getDay(),ue=te>4||te===0?_h.ceil(ue):_h(ue),ue=Do.offset(ue,(se.V-1)*7),se.y=ue.getFullYear(),se.m=ue.getMonth(),se.d=ue.getDate()+(se.w+6)%7)}else("W"in se||"U"in se)&&("w"in se||(se.w="u"in se?se.u%7:"W"in se?1:0),te="Z"in se?q8(Gy(se.y,0,1)).getUTCDay():W8(Gy(se.y,0,1)).getDay(),se.m=0,se.d="W"in se?(se.w+6)%7+se.W*7-(te+5)%7:se.w+se.U*7-(te+6)%7);return"Z"in se?(se.H+=se.Z/100|0,se.M+=se.Z%100,q8(se)):W8(se)}}o(_,"newParse");function A(H,q,K,se){for(var ce=0,ue=q.length,te=K.length,De,oe;ce=te)return-1;if(De=q.charCodeAt(ce++),De===37){if(De=q.charAt(ce++),oe=T[De in lY?q.charAt(ce++):De],!oe||(se=oe(H,K,se))<0)return-1}else if(De!=K.charCodeAt(se++))return-1}return se}o(A,"parseSpecifier");function L(H,q,K){var se=h.exec(q.slice(K));return se?(H.p=f.get(se[0].toLowerCase()),K+se[0].length):-1}o(L,"parsePeriod");function M(H,q,K){var se=m.exec(q.slice(K));return se?(H.w=g.get(se[0].toLowerCase()),K+se[0].length):-1}o(M,"parseShortWeekday");function N(H,q,K){var se=d.exec(q.slice(K));return se?(H.w=p.get(se[0].toLowerCase()),K+se[0].length):-1}o(N,"parseWeekday");function k(H,q,K){var se=x.exec(q.slice(K));return se?(H.m=b.get(se[0].toLowerCase()),K+se[0].length):-1}o(k,"parseShortMonth");function I(H,q,K){var se=y.exec(q.slice(K));return se?(H.m=v.get(se[0].toLowerCase()),K+se[0].length):-1}o(I,"parseMonth");function C(H,q,K){return A(H,e,q,K)}o(C,"parseLocaleDateTime");function O(H,q,K){return A(H,r,q,K)}o(O,"parseLocaleDate");function D(H,q,K){return A(H,n,q,K)}o(D,"parseLocaleTime");function P(H){return s[H.getDay()]}o(P,"formatShortWeekday");function F(H){return a[H.getDay()]}o(F,"formatWeekday");function B(H){return u[H.getMonth()]}o(B,"formatShortMonth");function $(H){return l[H.getMonth()]}o($,"formatMonth");function z(H){return i[+(H.getHours()>=12)]}o(z,"formatPeriod");function Y(H){return 1+~~(H.getMonth()/3)}o(Y,"formatQuarter");function Q(H){return s[H.getUTCDay()]}o(Q,"formatUTCShortWeekday");function X(H){return a[H.getUTCDay()]}o(X,"formatUTCWeekday");function ie(H){return u[H.getUTCMonth()]}o(ie,"formatUTCShortMonth");function j(H){return l[H.getUTCMonth()]}o(j,"formatUTCMonth");function J(H){return i[+(H.getUTCHours()>=12)]}o(J,"formatUTCPeriod");function Z(H){return 1+~~(H.getUTCMonth()/3)}return o(Z,"formatUTCQuarter"),{format:o(function(H){var q=E(H+="",w);return q.toString=function(){return H},q},"format"),parse:o(function(H){var q=_(H+="",!1);return q.toString=function(){return H},q},"parse"),utcFormat:o(function(H){var q=E(H+="",S);return q.toString=function(){return H},q},"utcFormat"),utcParse:o(function(H){var q=_(H+="",!0);return q.toString=function(){return H},q},"utcParse")}}function Pr(t,e,r){var n=t<0?"-":"",i=(n?-t:t)+"",a=i.length;return n+(a[e.toLowerCase(),r]))}function xwe(t,e,r){var n=Ki.exec(e.slice(r,r+1));return n?(t.w=+n[0],r+n[0].length):-1}function bwe(t,e,r){var n=Ki.exec(e.slice(r,r+1));return n?(t.u=+n[0],r+n[0].length):-1}function wwe(t,e,r){var n=Ki.exec(e.slice(r,r+2));return n?(t.U=+n[0],r+n[0].length):-1}function Twe(t,e,r){var n=Ki.exec(e.slice(r,r+2));return n?(t.V=+n[0],r+n[0].length):-1}function kwe(t,e,r){var n=Ki.exec(e.slice(r,r+2));return n?(t.W=+n[0],r+n[0].length):-1}function cY(t,e,r){var n=Ki.exec(e.slice(r,r+4));return n?(t.y=+n[0],r+n[0].length):-1}function uY(t,e,r){var n=Ki.exec(e.slice(r,r+2));return n?(t.y=+n[0]+(+n[0]>68?1900:2e3),r+n[0].length):-1}function Ewe(t,e,r){var n=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(r,r+6));return n?(t.Z=n[1]?0:-(n[2]+(n[3]||"00")),r+n[0].length):-1}function Cwe(t,e,r){var n=Ki.exec(e.slice(r,r+1));return n?(t.q=n[0]*3-3,r+n[0].length):-1}function Swe(t,e,r){var n=Ki.exec(e.slice(r,r+2));return n?(t.m=n[0]-1,r+n[0].length):-1}function hY(t,e,r){var n=Ki.exec(e.slice(r,r+2));return n?(t.d=+n[0],r+n[0].length):-1}function Awe(t,e,r){var n=Ki.exec(e.slice(r,r+3));return n?(t.m=0,t.d=+n[0],r+n[0].length):-1}function fY(t,e,r){var n=Ki.exec(e.slice(r,r+2));return n?(t.H=+n[0],r+n[0].length):-1}function _we(t,e,r){var n=Ki.exec(e.slice(r,r+2));return n?(t.M=+n[0],r+n[0].length):-1}function Lwe(t,e,r){var n=Ki.exec(e.slice(r,r+2));return n?(t.S=+n[0],r+n[0].length):-1}function Dwe(t,e,r){var n=Ki.exec(e.slice(r,r+3));return n?(t.L=+n[0],r+n[0].length):-1}function Rwe(t,e,r){var n=Ki.exec(e.slice(r,r+6));return n?(t.L=Math.floor(n[0]/1e3),r+n[0].length):-1}function Nwe(t,e,r){var n=gwe.exec(e.slice(r,r+1));return n?r+n[0].length:-1}function Mwe(t,e,r){var n=Ki.exec(e.slice(r));return n?(t.Q=+n[0],r+n[0].length):-1}function Iwe(t,e,r){var n=Ki.exec(e.slice(r));return n?(t.s=+n[0],r+n[0].length):-1}function dY(t,e){return Pr(t.getDate(),e,2)}function Owe(t,e){return Pr(t.getHours(),e,2)}function Pwe(t,e){return Pr(t.getHours()%12||12,e,2)}function Bwe(t,e){return Pr(1+Do.count(Qs(t),t),e,3)}function vY(t,e){return Pr(t.getMilliseconds(),e,3)}function Fwe(t,e){return vY(t,e)+"000"}function zwe(t,e){return Pr(t.getMonth()+1,e,2)}function Gwe(t,e){return Pr(t.getMinutes(),e,2)}function $we(t,e){return Pr(t.getSeconds(),e,2)}function Vwe(t){var e=t.getDay();return e===0?7:e}function Uwe(t,e){return Pr(yl.count(Qs(t)-1,t),e,2)}function xY(t){var e=t.getDay();return e>=4||e===0?cc(t):cc.ceil(t)}function Hwe(t,e){return t=xY(t),Pr(cc.count(Qs(t),t)+(Qs(t).getDay()===4),e,2)}function Ywe(t){return t.getDay()}function Wwe(t,e){return Pr(_h.count(Qs(t)-1,t),e,2)}function qwe(t,e){return Pr(t.getFullYear()%100,e,2)}function Xwe(t,e){return t=xY(t),Pr(t.getFullYear()%100,e,2)}function jwe(t,e){return Pr(t.getFullYear()%1e4,e,4)}function Kwe(t,e){var r=t.getDay();return t=r>=4||r===0?cc(t):cc.ceil(t),Pr(t.getFullYear()%1e4,e,4)}function Qwe(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+Pr(e/60|0,"0",2)+Pr(e%60,"0",2)}function pY(t,e){return Pr(t.getUTCDate(),e,2)}function Zwe(t,e){return Pr(t.getUTCHours(),e,2)}function Jwe(t,e){return Pr(t.getUTCHours()%12||12,e,2)}function eTe(t,e){return Pr(1+zy.count(vl(t),t),e,3)}function bY(t,e){return Pr(t.getUTCMilliseconds(),e,3)}function tTe(t,e){return bY(t,e)+"000"}function rTe(t,e){return Pr(t.getUTCMonth()+1,e,2)}function nTe(t,e){return Pr(t.getUTCMinutes(),e,2)}function iTe(t,e){return Pr(t.getUTCSeconds(),e,2)}function aTe(t){var e=t.getUTCDay();return e===0?7:e}function sTe(t,e){return Pr(pd.count(vl(t)-1,t),e,2)}function wY(t){var e=t.getUTCDay();return e>=4||e===0?Lh(t):Lh.ceil(t)}function oTe(t,e){return t=wY(t),Pr(Lh.count(vl(t),t)+(vl(t).getUTCDay()===4),e,2)}function lTe(t){return t.getUTCDay()}function cTe(t,e){return Pr(Bp.count(vl(t)-1,t),e,2)}function uTe(t,e){return Pr(t.getUTCFullYear()%100,e,2)}function hTe(t,e){return t=wY(t),Pr(t.getUTCFullYear()%100,e,2)}function fTe(t,e){return Pr(t.getUTCFullYear()%1e4,e,4)}function dTe(t,e){var r=t.getUTCDay();return t=r>=4||r===0?Lh(t):Lh.ceil(t),Pr(t.getUTCFullYear()%1e4,e,4)}function pTe(){return"+0000"}function mY(){return"%"}function gY(t){return+t}function yY(t){return Math.floor(+t/1e3)}var lY,Ki,gwe,ywe,TY=R(()=>{"use strict";_3();o(W8,"localDate");o(q8,"utcDate");o(Gy,"newDate");o(X8,"formatLocale");lY={"-":"",_:" ",0:"0"},Ki=/^\s*\d+/,gwe=/^%/,ywe=/[\\^$*+?|[\]().{}]/g;o(Pr,"pad");o(vwe,"requote");o($y,"formatRe");o(Vy,"formatLookup");o(xwe,"parseWeekdayNumberSunday");o(bwe,"parseWeekdayNumberMonday");o(wwe,"parseWeekNumberSunday");o(Twe,"parseWeekNumberISO");o(kwe,"parseWeekNumberMonday");o(cY,"parseFullYear");o(uY,"parseYear");o(Ewe,"parseZone");o(Cwe,"parseQuarter");o(Swe,"parseMonthNumber");o(hY,"parseDayOfMonth");o(Awe,"parseDayOfYear");o(fY,"parseHour24");o(_we,"parseMinutes");o(Lwe,"parseSeconds");o(Dwe,"parseMilliseconds");o(Rwe,"parseMicroseconds");o(Nwe,"parseLiteralPercent");o(Mwe,"parseUnixTimestamp");o(Iwe,"parseUnixTimestampSeconds");o(dY,"formatDayOfMonth");o(Owe,"formatHour24");o(Pwe,"formatHour12");o(Bwe,"formatDayOfYear");o(vY,"formatMilliseconds");o(Fwe,"formatMicroseconds");o(zwe,"formatMonthNumber");o(Gwe,"formatMinutes");o($we,"formatSeconds");o(Vwe,"formatWeekdayNumberMonday");o(Uwe,"formatWeekNumberSunday");o(xY,"dISO");o(Hwe,"formatWeekNumberISO");o(Ywe,"formatWeekdayNumberSunday");o(Wwe,"formatWeekNumberMonday");o(qwe,"formatYear");o(Xwe,"formatYearISO");o(jwe,"formatFullYear");o(Kwe,"formatFullYearISO");o(Qwe,"formatZone");o(pY,"formatUTCDayOfMonth");o(Zwe,"formatUTCHour24");o(Jwe,"formatUTCHour12");o(eTe,"formatUTCDayOfYear");o(bY,"formatUTCMilliseconds");o(tTe,"formatUTCMicroseconds");o(rTe,"formatUTCMonthNumber");o(nTe,"formatUTCMinutes");o(iTe,"formatUTCSeconds");o(aTe,"formatUTCWeekdayNumberMonday");o(sTe,"formatUTCWeekNumberSunday");o(wY,"UTCdISO");o(oTe,"formatUTCWeekNumberISO");o(lTe,"formatUTCWeekdayNumberSunday");o(cTe,"formatUTCWeekNumberMonday");o(uTe,"formatUTCYear");o(hTe,"formatUTCYearISO");o(fTe,"formatUTCFullYear");o(dTe,"formatUTCFullYearISO");o(pTe,"formatUTCZone");o(mY,"formatLiteralPercent");o(gY,"formatUnixTimestamp");o(yY,"formatUnixTimestampSeconds")});function j8(t){return Fp=X8(t),md=Fp.format,kY=Fp.parse,EY=Fp.utcFormat,CY=Fp.utcParse,Fp}var Fp,md,kY,EY,CY,SY=R(()=>{"use strict";TY();j8({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});o(j8,"defaultLocale")});var K8=R(()=>{"use strict";SY()});function mTe(t){return new Date(t)}function gTe(t){return t instanceof Date?+t:+new Date(+t)}function AY(t,e,r,n,i,a,s,l,u,h){var f=By(),d=f.invert,p=f.domain,m=h(".%L"),g=h(":%S"),y=h("%I:%M"),v=h("%I %p"),x=h("%a %d"),b=h("%b %d"),w=h("%B"),S=h("%Y");function T(E){return(u(E){"use strict";_3();K8();R8();Py();jH();o(mTe,"date");o(gTe,"number");o(AY,"calendar");o(L3,"time")});var LY=R(()=>{"use strict";UH();XH();A8();_Y()});function Q8(t){for(var e=t.length/6|0,r=new Array(e),n=0;n{"use strict";o(Q8,"default")});var Z8,RY=R(()=>{"use strict";DY();Z8=Q8("4e79a7f28e2ce1575976b7b259a14fedc949af7aa1ff9da79c755fbab0ab")});var NY=R(()=>{"use strict";RY()});function Nn(t){return o(function(){return t},"constant")}var D3=R(()=>{"use strict";o(Nn,"default")});function IY(t){return t>1?0:t<-1?zp:Math.acos(t)}function e_(t){return t>=1?Uy:t<=-1?-Uy:Math.asin(t)}var J8,ua,Dh,MY,R3,xl,gd,Qi,zp,Uy,Gp,N3=R(()=>{"use strict";J8=Math.abs,ua=Math.atan2,Dh=Math.cos,MY=Math.max,R3=Math.min,xl=Math.sin,gd=Math.sqrt,Qi=1e-12,zp=Math.PI,Uy=zp/2,Gp=2*zp;o(IY,"acos");o(e_,"asin")});function M3(t){let e=3;return t.digits=function(r){if(!arguments.length)return e;if(r==null)e=null;else{let n=Math.floor(r);if(!(n>=0))throw new RangeError(`invalid digits: ${r}`);e=n}return t},()=>new ld(e)}var t_=R(()=>{"use strict";d8();o(M3,"withPath")});function yTe(t){return t.innerRadius}function vTe(t){return t.outerRadius}function xTe(t){return t.startAngle}function bTe(t){return t.endAngle}function wTe(t){return t&&t.padAngle}function TTe(t,e,r,n,i,a,s,l){var u=r-t,h=n-e,f=s-i,d=l-a,p=d*u-f*h;if(!(p*pC*C+O*O&&(A=M,L=N),{cx:A,cy:L,x01:-f,y01:-d,x11:A*(i/T-1),y11:L*(i/T-1)}}function bl(){var t=yTe,e=vTe,r=Nn(0),n=null,i=xTe,a=bTe,s=wTe,l=null,u=M3(h);function h(){var f,d,p=+t.apply(this,arguments),m=+e.apply(this,arguments),g=i.apply(this,arguments)-Uy,y=a.apply(this,arguments)-Uy,v=J8(y-g),x=y>g;if(l||(l=f=u()),mQi))l.moveTo(0,0);else if(v>Gp-Qi)l.moveTo(m*Dh(g),m*xl(g)),l.arc(0,0,m,g,y,!x),p>Qi&&(l.moveTo(p*Dh(y),p*xl(y)),l.arc(0,0,p,y,g,x));else{var b=g,w=y,S=g,T=y,E=v,_=v,A=s.apply(this,arguments)/2,L=A>Qi&&(n?+n.apply(this,arguments):gd(p*p+m*m)),M=R3(J8(m-p)/2,+r.apply(this,arguments)),N=M,k=M,I,C;if(L>Qi){var O=e_(L/p*xl(A)),D=e_(L/m*xl(A));(E-=O*2)>Qi?(O*=x?1:-1,S+=O,T-=O):(E=0,S=T=(g+y)/2),(_-=D*2)>Qi?(D*=x?1:-1,b+=D,w-=D):(_=0,b=w=(g+y)/2)}var P=m*Dh(b),F=m*xl(b),B=p*Dh(T),$=p*xl(T);if(M>Qi){var z=m*Dh(w),Y=m*xl(w),Q=p*Dh(S),X=p*xl(S),ie;if(vQi?k>Qi?(I=I3(Q,X,P,F,m,k,x),C=I3(z,Y,B,$,m,k,x),l.moveTo(I.cx+I.x01,I.cy+I.y01),kQi)||!(E>Qi)?l.lineTo(B,$):N>Qi?(I=I3(B,$,z,Y,p,-N,x),C=I3(P,F,Q,X,p,-N,x),l.lineTo(I.cx+I.x01,I.cy+I.y01),N{"use strict";D3();N3();t_();o(yTe,"arcInnerRadius");o(vTe,"arcOuterRadius");o(xTe,"arcStartAngle");o(bTe,"arcEndAngle");o(wTe,"arcPadAngle");o(TTe,"intersect");o(I3,"cornerTangents");o(bl,"default")});function Hy(t){return typeof t=="object"&&"length"in t?t:Array.from(t)}var Dyt,r_=R(()=>{"use strict";Dyt=Array.prototype.slice;o(Hy,"default")});function PY(t){this._context=t}function xu(t){return new PY(t)}var n_=R(()=>{"use strict";o(PY,"Linear");PY.prototype={areaStart:o(function(){this._line=0},"areaStart"),areaEnd:o(function(){this._line=NaN},"areaEnd"),lineStart:o(function(){this._point=0},"lineStart"),lineEnd:o(function(){(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},"lineEnd"),point:o(function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:this._context.lineTo(t,e);break}},"point")};o(xu,"default")});function BY(t){return t[0]}function FY(t){return t[1]}var zY=R(()=>{"use strict";o(BY,"x");o(FY,"y")});function ha(t,e){var r=Nn(!0),n=null,i=xu,a=null,s=M3(l);t=typeof t=="function"?t:t===void 0?BY:Nn(t),e=typeof e=="function"?e:e===void 0?FY:Nn(e);function l(u){var h,f=(u=Hy(u)).length,d,p=!1,m;for(n==null&&(a=i(m=s())),h=0;h<=f;++h)!(h{"use strict";r_();D3();n_();t_();zY();o(ha,"default")});function i_(t,e){return et?1:e>=t?0:NaN}var $Y=R(()=>{"use strict";o(i_,"default")});function a_(t){return t}var VY=R(()=>{"use strict";o(a_,"default")});function O3(){var t=a_,e=i_,r=null,n=Nn(0),i=Nn(Gp),a=Nn(0);function s(l){var u,h=(l=Hy(l)).length,f,d,p=0,m=new Array(h),g=new Array(h),y=+n.apply(this,arguments),v=Math.min(Gp,Math.max(-Gp,i.apply(this,arguments)-y)),x,b=Math.min(Math.abs(v)/h,a.apply(this,arguments)),w=b*(v<0?-1:1),S;for(u=0;u0&&(p+=S);for(e!=null?m.sort(function(T,E){return e(g[T],g[E])}):r!=null&&m.sort(function(T,E){return r(l[T],l[E])}),u=0,d=p?(v-h*w)/p:0;u0?S*d:0)+w,g[f]={data:l[f],index:u,value:S,startAngle:y,endAngle:x,padAngle:b};return g}return o(s,"pie"),s.value=function(l){return arguments.length?(t=typeof l=="function"?l:Nn(+l),s):t},s.sortValues=function(l){return arguments.length?(e=l,r=null,s):e},s.sort=function(l){return arguments.length?(r=l,e=null,s):r},s.startAngle=function(l){return arguments.length?(n=typeof l=="function"?l:Nn(+l),s):n},s.endAngle=function(l){return arguments.length?(i=typeof l=="function"?l:Nn(+l),s):i},s.padAngle=function(l){return arguments.length?(a=typeof l=="function"?l:Nn(+l),s):a},s}var UY=R(()=>{"use strict";r_();D3();$Y();VY();N3();o(O3,"default")});function s_(t){return new P3(t,!0)}function o_(t){return new P3(t,!1)}var P3,HY=R(()=>{"use strict";P3=class{static{o(this,"Bump")}constructor(e,r){this._context=e,this._x=r}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line}point(e,r){switch(e=+e,r=+r,this._point){case 0:{this._point=1,this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break}case 1:this._point=2;default:{this._x?this._context.bezierCurveTo(this._x0=(this._x0+e)/2,this._y0,this._x0,r,e,r):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+r)/2,e,this._y0,e,r);break}}this._x0=e,this._y0=r}};o(s_,"bumpX");o(o_,"bumpY")});function Zs(){}var Yy=R(()=>{"use strict";o(Zs,"default")});function $p(t,e,r){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+e)/6,(t._y0+4*t._y1+r)/6)}function Wy(t){this._context=t}function vs(t){return new Wy(t)}var qy=R(()=>{"use strict";o($p,"point");o(Wy,"Basis");Wy.prototype={areaStart:o(function(){this._line=0},"areaStart"),areaEnd:o(function(){this._line=NaN},"areaEnd"),lineStart:o(function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},"lineStart"),lineEnd:o(function(){switch(this._point){case 3:$p(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},"lineEnd"),point:o(function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:$p(this,t,e);break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e},"point")};o(vs,"default")});function YY(t){this._context=t}function B3(t){return new YY(t)}var WY=R(()=>{"use strict";Yy();qy();o(YY,"BasisClosed");YY.prototype={areaStart:Zs,areaEnd:Zs,lineStart:o(function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},"lineStart"),lineEnd:o(function(){switch(this._point){case 1:{this._context.moveTo(this._x2,this._y2),this._context.closePath();break}case 2:{this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break}case 3:{this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4);break}}},"lineEnd"),point:o(function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x2=t,this._y2=e;break;case 1:this._point=2,this._x3=t,this._y3=e;break;case 2:this._point=3,this._x4=t,this._y4=e,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+e)/6);break;default:$p(this,t,e);break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e},"point")};o(B3,"default")});function qY(t){this._context=t}function F3(t){return new qY(t)}var XY=R(()=>{"use strict";qy();o(qY,"BasisOpen");qY.prototype={areaStart:o(function(){this._line=0},"areaStart"),areaEnd:o(function(){this._line=NaN},"areaEnd"),lineStart:o(function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},"lineStart"),lineEnd:o(function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},"lineEnd"),point:o(function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var r=(this._x0+4*this._x1+t)/6,n=(this._y0+4*this._y1+e)/6;this._line?this._context.lineTo(r,n):this._context.moveTo(r,n);break;case 3:this._point=4;default:$p(this,t,e);break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e},"point")};o(F3,"default")});function jY(t,e){this._basis=new Wy(t),this._beta=e}var l_,KY=R(()=>{"use strict";qy();o(jY,"Bundle");jY.prototype={lineStart:o(function(){this._x=[],this._y=[],this._basis.lineStart()},"lineStart"),lineEnd:o(function(){var t=this._x,e=this._y,r=t.length-1;if(r>0)for(var n=t[0],i=e[0],a=t[r]-n,s=e[r]-i,l=-1,u;++l<=r;)u=l/r,this._basis.point(this._beta*t[l]+(1-this._beta)*(n+u*a),this._beta*e[l]+(1-this._beta)*(i+u*s));this._x=this._y=null,this._basis.lineEnd()},"lineEnd"),point:o(function(t,e){this._x.push(+t),this._y.push(+e)},"point")};l_=o(function t(e){function r(n){return e===1?new Wy(n):new jY(n,e)}return o(r,"bundle"),r.beta=function(n){return t(+n)},r},"custom")(.85)});function Vp(t,e,r){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-e),t._y2+t._k*(t._y1-r),t._x2,t._y2)}function z3(t,e){this._context=t,this._k=(1-e)/6}var c_,Xy=R(()=>{"use strict";o(Vp,"point");o(z3,"Cardinal");z3.prototype={areaStart:o(function(){this._line=0},"areaStart"),areaEnd:o(function(){this._line=NaN},"areaEnd"),lineStart:o(function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},"lineStart"),lineEnd:o(function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:Vp(this,this._x1,this._y1);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},"lineEnd"),point:o(function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2,this._x1=t,this._y1=e;break;case 2:this._point=3;default:Vp(this,t,e);break}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e},"point")};c_=o(function t(e){function r(n){return new z3(n,e)}return o(r,"cardinal"),r.tension=function(n){return t(+n)},r},"custom")(0)});function G3(t,e){this._context=t,this._k=(1-e)/6}var u_,h_=R(()=>{"use strict";Yy();Xy();o(G3,"CardinalClosed");G3.prototype={areaStart:Zs,areaEnd:Zs,lineStart:o(function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},"lineStart"),lineEnd:o(function(){switch(this._point){case 1:{this._context.moveTo(this._x3,this._y3),this._context.closePath();break}case 2:{this._context.lineTo(this._x3,this._y3),this._context.closePath();break}case 3:{this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5);break}}},"lineEnd"),point:o(function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:Vp(this,t,e);break}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e},"point")};u_=o(function t(e){function r(n){return new G3(n,e)}return o(r,"cardinal"),r.tension=function(n){return t(+n)},r},"custom")(0)});function $3(t,e){this._context=t,this._k=(1-e)/6}var f_,d_=R(()=>{"use strict";Xy();o($3,"CardinalOpen");$3.prototype={areaStart:o(function(){this._line=0},"areaStart"),areaEnd:o(function(){this._line=NaN},"areaEnd"),lineStart:o(function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},"lineStart"),lineEnd:o(function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},"lineEnd"),point:o(function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Vp(this,t,e);break}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e},"point")};f_=o(function t(e){function r(n){return new $3(n,e)}return o(r,"cardinal"),r.tension=function(n){return t(+n)},r},"custom")(0)});function jy(t,e,r){var n=t._x1,i=t._y1,a=t._x2,s=t._y2;if(t._l01_a>Qi){var l=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,u=3*t._l01_a*(t._l01_a+t._l12_a);n=(n*l-t._x0*t._l12_2a+t._x2*t._l01_2a)/u,i=(i*l-t._y0*t._l12_2a+t._y2*t._l01_2a)/u}if(t._l23_a>Qi){var h=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,f=3*t._l23_a*(t._l23_a+t._l12_a);a=(a*h+t._x1*t._l23_2a-e*t._l12_2a)/f,s=(s*h+t._y1*t._l23_2a-r*t._l12_2a)/f}t._context.bezierCurveTo(n,i,a,s,t._x2,t._y2)}function QY(t,e){this._context=t,this._alpha=e}var p_,V3=R(()=>{"use strict";N3();Xy();o(jy,"point");o(QY,"CatmullRom");QY.prototype={areaStart:o(function(){this._line=0},"areaStart"),areaEnd:o(function(){this._line=NaN},"areaEnd"),lineStart:o(function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},"lineStart"),lineEnd:o(function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},"lineEnd"),point:o(function(t,e){if(t=+t,e=+e,this._point){var r=this._x2-t,n=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(r*r+n*n,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3;default:jy(this,t,e);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e},"point")};p_=o(function t(e){function r(n){return e?new QY(n,e):new z3(n,0)}return o(r,"catmullRom"),r.alpha=function(n){return t(+n)},r},"custom")(.5)});function ZY(t,e){this._context=t,this._alpha=e}var m_,JY=R(()=>{"use strict";h_();Yy();V3();o(ZY,"CatmullRomClosed");ZY.prototype={areaStart:Zs,areaEnd:Zs,lineStart:o(function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},"lineStart"),lineEnd:o(function(){switch(this._point){case 1:{this._context.moveTo(this._x3,this._y3),this._context.closePath();break}case 2:{this._context.lineTo(this._x3,this._y3),this._context.closePath();break}case 3:{this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5);break}}},"lineEnd"),point:o(function(t,e){if(t=+t,e=+e,this._point){var r=this._x2-t,n=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(r*r+n*n,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:jy(this,t,e);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e},"point")};m_=o(function t(e){function r(n){return e?new ZY(n,e):new G3(n,0)}return o(r,"catmullRom"),r.alpha=function(n){return t(+n)},r},"custom")(.5)});function eW(t,e){this._context=t,this._alpha=e}var g_,tW=R(()=>{"use strict";d_();V3();o(eW,"CatmullRomOpen");eW.prototype={areaStart:o(function(){this._line=0},"areaStart"),areaEnd:o(function(){this._line=NaN},"areaEnd"),lineStart:o(function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},"lineStart"),lineEnd:o(function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},"lineEnd"),point:o(function(t,e){if(t=+t,e=+e,this._point){var r=this._x2-t,n=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(r*r+n*n,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:jy(this,t,e);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e},"point")};g_=o(function t(e){function r(n){return e?new eW(n,e):new $3(n,0)}return o(r,"catmullRom"),r.alpha=function(n){return t(+n)},r},"custom")(.5)});function rW(t){this._context=t}function U3(t){return new rW(t)}var nW=R(()=>{"use strict";Yy();o(rW,"LinearClosed");rW.prototype={areaStart:Zs,areaEnd:Zs,lineStart:o(function(){this._point=0},"lineStart"),lineEnd:o(function(){this._point&&this._context.closePath()},"lineEnd"),point:o(function(t,e){t=+t,e=+e,this._point?this._context.lineTo(t,e):(this._point=1,this._context.moveTo(t,e))},"point")};o(U3,"default")});function iW(t){return t<0?-1:1}function aW(t,e,r){var n=t._x1-t._x0,i=e-t._x1,a=(t._y1-t._y0)/(n||i<0&&-0),s=(r-t._y1)/(i||n<0&&-0),l=(a*i+s*n)/(n+i);return(iW(a)+iW(s))*Math.min(Math.abs(a),Math.abs(s),.5*Math.abs(l))||0}function sW(t,e){var r=t._x1-t._x0;return r?(3*(t._y1-t._y0)/r-e)/2:e}function y_(t,e,r){var n=t._x0,i=t._y0,a=t._x1,s=t._y1,l=(a-n)/3;t._context.bezierCurveTo(n+l,i+l*e,a-l,s-l*r,a,s)}function H3(t){this._context=t}function oW(t){this._context=new lW(t)}function lW(t){this._context=t}function v_(t){return new H3(t)}function x_(t){return new oW(t)}var cW=R(()=>{"use strict";o(iW,"sign");o(aW,"slope3");o(sW,"slope2");o(y_,"point");o(H3,"MonotoneX");H3.prototype={areaStart:o(function(){this._line=0},"areaStart"),areaEnd:o(function(){this._line=NaN},"areaEnd"),lineStart:o(function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},"lineStart"),lineEnd:o(function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:y_(this,this._t0,sW(this,this._t0));break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},"lineEnd"),point:o(function(t,e){var r=NaN;if(t=+t,e=+e,!(t===this._x1&&e===this._y1)){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,y_(this,sW(this,r=aW(this,t,e)),r);break;default:y_(this,this._t0,r=aW(this,t,e));break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e,this._t0=r}},"point")};o(oW,"MonotoneY");(oW.prototype=Object.create(H3.prototype)).point=function(t,e){H3.prototype.point.call(this,e,t)};o(lW,"ReflectContext");lW.prototype={moveTo:o(function(t,e){this._context.moveTo(e,t)},"moveTo"),closePath:o(function(){this._context.closePath()},"closePath"),lineTo:o(function(t,e){this._context.lineTo(e,t)},"lineTo"),bezierCurveTo:o(function(t,e,r,n,i,a){this._context.bezierCurveTo(e,t,n,r,a,i)},"bezierCurveTo")};o(v_,"monotoneX");o(x_,"monotoneY")});function hW(t){this._context=t}function uW(t){var e,r=t.length-1,n,i=new Array(r),a=new Array(r),s=new Array(r);for(i[0]=0,a[0]=2,s[0]=t[0]+2*t[1],e=1;e=0;--e)i[e]=(s[e]-i[e+1])/a[e];for(a[r-1]=(t[r]+i[r-1])/2,e=0;e{"use strict";o(hW,"Natural");hW.prototype={areaStart:o(function(){this._line=0},"areaStart"),areaEnd:o(function(){this._line=NaN},"areaEnd"),lineStart:o(function(){this._x=[],this._y=[]},"lineStart"),lineEnd:o(function(){var t=this._x,e=this._y,r=t.length;if(r)if(this._line?this._context.lineTo(t[0],e[0]):this._context.moveTo(t[0],e[0]),r===2)this._context.lineTo(t[1],e[1]);else for(var n=uW(t),i=uW(e),a=0,s=1;s{"use strict";o(W3,"Step");W3.prototype={areaStart:o(function(){this._line=0},"areaStart"),areaEnd:o(function(){this._line=NaN},"areaEnd"),lineStart:o(function(){this._x=this._y=NaN,this._point=0},"lineStart"),lineEnd:o(function(){0=0&&(this._t=1-this._t,this._line=1-this._line)},"lineEnd"),point:o(function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:{if(this._t<=0)this._context.lineTo(this._x,e),this._context.lineTo(t,e);else{var r=this._x*(1-this._t)+t*this._t;this._context.lineTo(r,this._y),this._context.lineTo(r,e)}break}}this._x=t,this._y=e},"point")};o(q3,"default");o(b_,"stepBefore");o(w_,"stepAfter")});var pW=R(()=>{"use strict";OY();GY();UY();WY();XY();qy();HY();KY();h_();d_();Xy();JY();tW();V3();nW();n_();cW();fW();dW()});var mW=R(()=>{"use strict"});var gW=R(()=>{"use strict"});function Rh(t,e,r){this.k=t,this.x=e,this.y=r}function k_(t){for(;!t.__zoom;)if(!(t=t.parentNode))return T_;return t.__zoom}var T_,E_=R(()=>{"use strict";o(Rh,"Transform");Rh.prototype={constructor:Rh,scale:o(function(t){return t===1?this:new Rh(this.k*t,this.x,this.y)},"scale"),translate:o(function(t,e){return t===0&e===0?this:new Rh(this.k,this.x+this.k*t,this.y+this.k*e)},"translate"),apply:o(function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},"apply"),applyX:o(function(t){return t*this.k+this.x},"applyX"),applyY:o(function(t){return t*this.k+this.y},"applyY"),invert:o(function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},"invert"),invertX:o(function(t){return(t-this.x)/this.k},"invertX"),invertY:o(function(t){return(t-this.y)/this.k},"invertY"),rescaleX:o(function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},"rescaleX"),rescaleY:o(function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},"rescaleY"),toString:o(function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"},"toString")};T_=new Rh(1,0,0);k_.prototype=Rh.prototype;o(k_,"transform")});var yW=R(()=>{"use strict"});var vW=R(()=>{"use strict";c3();mW();gW();E_();yW()});var xW=R(()=>{"use strict";vW();E_()});var Zt=R(()=>{"use strict";bh();K$();mH();xH();Lp();bH();wH();bS();$V();TH();l8();kH();CH();C8();zH();GH();Np();d8();$H();EH();VH();LY();NY();fl();pW();_3();K8();n3();c3();xW()});var bW=gi(Zi=>{"use strict";Object.defineProperty(Zi,"__esModule",{value:!0});Zi.BLANK_URL=Zi.relativeFirstCharacters=Zi.whitespaceEscapeCharsRegex=Zi.urlSchemeRegex=Zi.ctrlCharactersRegex=Zi.htmlCtrlEntityRegex=Zi.htmlEntitiesRegex=Zi.invalidProtocolRegex=void 0;Zi.invalidProtocolRegex=/^([^\w]*)(javascript|data|vbscript)/im;Zi.htmlEntitiesRegex=/&#(\w+)(^\w|;)?/g;Zi.htmlCtrlEntityRegex=/&(newline|tab);/gi;Zi.ctrlCharactersRegex=/[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim;Zi.urlSchemeRegex=/^.+(:|:)/gim;Zi.whitespaceEscapeCharsRegex=/(\\|%5[cC])((%(6[eE]|72|74))|[nrt])/g;Zi.relativeFirstCharacters=[".","/"];Zi.BLANK_URL="about:blank"});var Up=gi(X3=>{"use strict";Object.defineProperty(X3,"__esModule",{value:!0});X3.sanitizeUrl=void 0;var Na=bW();function kTe(t){return Na.relativeFirstCharacters.indexOf(t[0])>-1}o(kTe,"isRelativeUrlWithoutProtocol");function ETe(t){var e=t.replace(Na.ctrlCharactersRegex,"");return e.replace(Na.htmlEntitiesRegex,function(r,n){return String.fromCharCode(n)})}o(ETe,"decodeHtmlCharacters");function CTe(t){return URL.canParse(t)}o(CTe,"isValidUrl");function wW(t){try{return decodeURIComponent(t)}catch{return t}}o(wW,"decodeURI");function STe(t){if(!t)return Na.BLANK_URL;var e,r=wW(t.trim());do r=ETe(r).replace(Na.htmlCtrlEntityRegex,"").replace(Na.ctrlCharactersRegex,"").replace(Na.whitespaceEscapeCharsRegex,"").trim(),r=wW(r),e=r.match(Na.ctrlCharactersRegex)||r.match(Na.htmlEntitiesRegex)||r.match(Na.htmlCtrlEntityRegex)||r.match(Na.whitespaceEscapeCharsRegex);while(e&&e.length>0);var n=r;if(!n)return Na.BLANK_URL;if(kTe(n))return n;var i=n.trimStart(),a=i.match(Na.urlSchemeRegex);if(!a)return n;var s=a[0].toLowerCase().trim();if(Na.invalidProtocolRegex.test(s))return Na.BLANK_URL;var l=i.replace(/\\/g,"/");if(s==="mailto:"||s.includes("://"))return l;if(s==="http:"||s==="https:"){if(!CTe(l))return Na.BLANK_URL;var u=new URL(l);return u.protocol=u.protocol.toLowerCase(),u.hostname=u.hostname.toLowerCase(),u.toString()}return l}o(STe,"sanitizeUrl");X3.sanitizeUrl=STe});var C_,yd,j3,TW,kW,EW,wl,Ky,Qy=R(()=>{"use strict";C_=Xi(Up(),1);rr();yd=o((t,e)=>{let r=t.append("rect");if(r.attr("x",e.x),r.attr("y",e.y),r.attr("fill",e.fill),r.attr("stroke",e.stroke),r.attr("width",e.width),r.attr("height",e.height),e.name&&r.attr("name",e.name),e.rx&&r.attr("rx",e.rx),e.ry&&r.attr("ry",e.ry),e.attrs!==void 0)for(let n in e.attrs)r.attr(n,e.attrs[n]);return e.class&&r.attr("class",e.class),r},"drawRect"),j3=o((t,e)=>{let r={x:e.startx,y:e.starty,width:e.stopx-e.startx,height:e.stopy-e.starty,fill:e.fill,stroke:e.stroke,class:"rect"};yd(t,r).lower()},"drawBackgroundRect"),TW=o((t,e)=>{let r=e.text.replace(Qf," "),n=t.append("text");n.attr("x",e.x),n.attr("y",e.y),n.attr("class","legend"),n.style("text-anchor",e.anchor),e.class&&n.attr("class",e.class);let i=n.append("tspan");return i.attr("x",e.x+e.textMargin*2),i.text(r),n},"drawText"),kW=o((t,e,r,n)=>{let i=t.append("image");i.attr("x",e),i.attr("y",r);let a=(0,C_.sanitizeUrl)(n);i.attr("xlink:href",a)},"drawImage"),EW=o((t,e,r,n)=>{let i=t.append("use");i.attr("x",e),i.attr("y",r);let a=(0,C_.sanitizeUrl)(n);i.attr("xlink:href",`#${a}`)},"drawEmbeddedImage"),wl=o(()=>({x:0,y:0,width:100,height:100,fill:"#EDF2AE",stroke:"#666",anchor:"start",rx:0,ry:0}),"getNoteRect"),Ky=o(()=>({x:0,y:0,width:100,height:100,"text-anchor":"start",style:"#666",textMargin:0,rx:0,ry:0,tspan:!0}),"getTextObj")});var CW,S_,SW,ATe,_Te,LTe,DTe,RTe,NTe,MTe,ITe,OTe,PTe,BTe,FTe,bu,Tl,AW=R(()=>{"use strict";rr();Qy();CW=Xi(Up(),1),S_=o(function(t,e){return yd(t,e)},"drawRect"),SW=o(function(t,e,r,n,i,a){let s=t.append("image");s.attr("width",e),s.attr("height",r),s.attr("x",n),s.attr("y",i);let l=a.startsWith("data:image/png;base64")?a:(0,CW.sanitizeUrl)(a);s.attr("xlink:href",l)},"drawImage"),ATe=o((t,e,r)=>{let n=t.append("g"),i=0;for(let a of e){let s=a.textColor?a.textColor:"#444444",l=a.lineColor?a.lineColor:"#444444",u=a.offsetX?parseInt(a.offsetX):0,h=a.offsetY?parseInt(a.offsetY):0,f="";if(i===0){let p=n.append("line");p.attr("x1",a.startPoint.x),p.attr("y1",a.startPoint.y),p.attr("x2",a.endPoint.x),p.attr("y2",a.endPoint.y),p.attr("stroke-width","1"),p.attr("stroke",l),p.style("fill","none"),a.type!=="rel_b"&&p.attr("marker-end","url("+f+"#arrowhead)"),(a.type==="birel"||a.type==="rel_b")&&p.attr("marker-start","url("+f+"#arrowend)"),i=-1}else{let p=n.append("path");p.attr("fill","none").attr("stroke-width","1").attr("stroke",l).attr("d","Mstartx,starty Qcontrolx,controly stopx,stopy ".replaceAll("startx",a.startPoint.x).replaceAll("starty",a.startPoint.y).replaceAll("controlx",a.startPoint.x+(a.endPoint.x-a.startPoint.x)/2-(a.endPoint.x-a.startPoint.x)/4).replaceAll("controly",a.startPoint.y+(a.endPoint.y-a.startPoint.y)/2).replaceAll("stopx",a.endPoint.x).replaceAll("stopy",a.endPoint.y)),a.type!=="rel_b"&&p.attr("marker-end","url("+f+"#arrowhead)"),(a.type==="birel"||a.type==="rel_b")&&p.attr("marker-start","url("+f+"#arrowend)")}let d=r.messageFont();bu(r)(a.label.text,n,Math.min(a.startPoint.x,a.endPoint.x)+Math.abs(a.endPoint.x-a.startPoint.x)/2+u,Math.min(a.startPoint.y,a.endPoint.y)+Math.abs(a.endPoint.y-a.startPoint.y)/2+h,a.label.width,a.label.height,{fill:s},d),a.techn&&a.techn.text!==""&&(d=r.messageFont(),bu(r)("["+a.techn.text+"]",n,Math.min(a.startPoint.x,a.endPoint.x)+Math.abs(a.endPoint.x-a.startPoint.x)/2+u,Math.min(a.startPoint.y,a.endPoint.y)+Math.abs(a.endPoint.y-a.startPoint.y)/2+r.messageFontSize+5+h,Math.max(a.label.width,a.techn.width),a.techn.height,{fill:s,"font-style":"italic"},d))}},"drawRels"),_Te=o(function(t,e,r){let n=t.append("g"),i=e.bgColor?e.bgColor:"none",a=e.borderColor?e.borderColor:"#444444",s=e.fontColor?e.fontColor:"black",l={"stroke-width":1,"stroke-dasharray":"7.0,7.0"};e.nodeType&&(l={"stroke-width":1});let u={x:e.x,y:e.y,fill:i,stroke:a,width:e.width,height:e.height,rx:2.5,ry:2.5,attrs:l};S_(n,u);let h=r.boundaryFont();h.fontWeight="bold",h.fontSize=h.fontSize+2,h.fontColor=s,bu(r)(e.label.text,n,e.x,e.y+e.label.Y,e.width,e.height,{fill:"#444444"},h),e.type&&e.type.text!==""&&(h=r.boundaryFont(),h.fontColor=s,bu(r)(e.type.text,n,e.x,e.y+e.type.Y,e.width,e.height,{fill:"#444444"},h)),e.descr&&e.descr.text!==""&&(h=r.boundaryFont(),h.fontSize=h.fontSize-2,h.fontColor=s,bu(r)(e.descr.text,n,e.x,e.y+e.descr.Y,e.width,e.height,{fill:"#444444"},h))},"drawBoundary"),LTe=o(function(t,e,r){let n=e.bgColor?e.bgColor:r[e.typeC4Shape.text+"_bg_color"],i=e.borderColor?e.borderColor:r[e.typeC4Shape.text+"_border_color"],a=e.fontColor?e.fontColor:"#FFFFFF",s="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAIAAADYYG7QAAACD0lEQVR4Xu2YoU4EMRCGT+4j8Ai8AhaH4QHgAUjQuFMECUgMIUgwJAgMhgQsAYUiJCiQIBBY+EITsjfTdme6V24v4c8vyGbb+ZjOtN0bNcvjQXmkH83WvYBWto6PLm6v7p7uH1/w2fXD+PBycX1Pv2l3IdDm/vn7x+dXQiAubRzoURa7gRZWd0iGRIiJbOnhnfYBQZNJjNbuyY2eJG8fkDE3bbG4ep6MHUAsgYxmE3nVs6VsBWJSGccsOlFPmLIViMzLOB7pCVO2AtHJMohH7Fh6zqitQK7m0rJvAVYgGcEpe//PLdDz65sM4pF9N7ICcXDKIB5Nv6j7tD0NoSdM2QrU9Gg0ewE1LqBhHR3BBdvj2vapnidjHxD/q6vd7Pvhr31AwcY8eXMTXAKECZZJFXuEq27aLgQK5uLMohCenGGuGewOxSjBvYBqeG6B+Nqiblggdjnc+ZXDy+FNFpFzw76O3UBAROuXh6FoiAcf5g9eTvUgzy0nWg6I8cXHRUpg5bOVBCo+KDpFajOf23GgPme7RSQ+lacIENUgJ6gg1k6HjgOlqnLqip4tEuhv0hNEMXUD0clyXE3p6pZA0S2nnvTlXwLJEZWlb7cTQH1+USgTN4VhAenm/wea1OCAOmqo6fE1WCb9WSKBah+rbUWPWAmE2Rvk0ApiB45eOyNAzU8xcTvj8KvkKEoOaIYeHNA3ZuygAvFMUO0AAAAASUVORK5CYII=";switch(e.typeC4Shape.text){case"person":s="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAIAAADYYG7QAAACD0lEQVR4Xu2YoU4EMRCGT+4j8Ai8AhaH4QHgAUjQuFMECUgMIUgwJAgMhgQsAYUiJCiQIBBY+EITsjfTdme6V24v4c8vyGbb+ZjOtN0bNcvjQXmkH83WvYBWto6PLm6v7p7uH1/w2fXD+PBycX1Pv2l3IdDm/vn7x+dXQiAubRzoURa7gRZWd0iGRIiJbOnhnfYBQZNJjNbuyY2eJG8fkDE3bbG4ep6MHUAsgYxmE3nVs6VsBWJSGccsOlFPmLIViMzLOB7pCVO2AtHJMohH7Fh6zqitQK7m0rJvAVYgGcEpe//PLdDz65sM4pF9N7ICcXDKIB5Nv6j7tD0NoSdM2QrU9Gg0ewE1LqBhHR3BBdvj2vapnidjHxD/q6vd7Pvhr31AwcY8eXMTXAKECZZJFXuEq27aLgQK5uLMohCenGGuGewOxSjBvYBqeG6B+Nqiblggdjnc+ZXDy+FNFpFzw76O3UBAROuXh6FoiAcf5g9eTvUgzy0nWg6I8cXHRUpg5bOVBCo+KDpFajOf23GgPme7RSQ+lacIENUgJ6gg1k6HjgOlqnLqip4tEuhv0hNEMXUD0clyXE3p6pZA0S2nnvTlXwLJEZWlb7cTQH1+USgTN4VhAenm/wea1OCAOmqo6fE1WCb9WSKBah+rbUWPWAmE2Rvk0ApiB45eOyNAzU8xcTvj8KvkKEoOaIYeHNA3ZuygAvFMUO0AAAAASUVORK5CYII=";break;case"external_person":s="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAIAAADYYG7QAAAB6ElEQVR4Xu2YLY+EMBCG9+dWr0aj0Wg0Go1Go0+j8Xdv2uTCvv1gpt0ebHKPuhDaeW4605Z9mJvx4AdXUyTUdd08z+u6flmWZRnHsWkafk9DptAwDPu+f0eAYtu2PEaGWuj5fCIZrBAC2eLBAnRCsEkkxmeaJp7iDJ2QMDdHsLg8SxKFEJaAo8lAXnmuOFIhTMpxxKATebo4UiFknuNo4OniSIXQyRxEA3YsnjGCVEjVXD7yLUAqxBGUyPv/Y4W2beMgGuS7kVQIBycH0fD+oi5pezQETxdHKmQKGk1eQEYldK+jw5GxPfZ9z7Mk0Qnhf1W1m3w//EUn5BDmSZsbR44QQLBEqrBHqOrmSKaQAxdnLArCrxZcM7A7ZKs4ioRq8LFC+NpC3WCBJsvpVw5edm9iEXFuyNfxXAgSwfrFQ1c0iNda8AdejvUgnktOtJQQxmcfFzGglc5WVCj7oDgFqU18boeFSs52CUh8LE8BIVQDT1ABrB0HtgSEYlX5doJnCwv9TXocKCaKbnwhdDKPq4lf3SwU3HLq4V/+WYhHVMa/3b4IlfyikAduCkcBc7mQ3/z/Qq/cTuikhkzB12Ae/mcJC9U+Vo8Ej1gWAtgbeGgFsAMHr50BIWOLCbezvhpBFUdY6EJuJ/QDW0XoMX60zZ0AAAAASUVORK5CYII=";break}let l=t.append("g");l.attr("class","person-man");let u=wl();switch(e.typeC4Shape.text){case"person":case"external_person":case"system":case"external_system":case"container":case"external_container":case"component":case"external_component":u.x=e.x,u.y=e.y,u.fill=n,u.width=e.width,u.height=e.height,u.stroke=i,u.rx=2.5,u.ry=2.5,u.attrs={"stroke-width":.5},S_(l,u);break;case"system_db":case"external_system_db":case"container_db":case"external_container_db":case"component_db":case"external_component_db":l.append("path").attr("fill",n).attr("stroke-width","0.5").attr("stroke",i).attr("d","Mstartx,startyc0,-10 half,-10 half,-10c0,0 half,0 half,10l0,heightc0,10 -half,10 -half,10c0,0 -half,0 -half,-10l0,-height".replaceAll("startx",e.x).replaceAll("starty",e.y).replaceAll("half",e.width/2).replaceAll("height",e.height)),l.append("path").attr("fill","none").attr("stroke-width","0.5").attr("stroke",i).attr("d","Mstartx,startyc0,10 half,10 half,10c0,0 half,0 half,-10".replaceAll("startx",e.x).replaceAll("starty",e.y).replaceAll("half",e.width/2));break;case"system_queue":case"external_system_queue":case"container_queue":case"external_container_queue":case"component_queue":case"external_component_queue":l.append("path").attr("fill",n).attr("stroke-width","0.5").attr("stroke",i).attr("d","Mstartx,startylwidth,0c5,0 5,half 5,halfc0,0 0,half -5,halfl-width,0c-5,0 -5,-half -5,-halfc0,0 0,-half 5,-half".replaceAll("startx",e.x).replaceAll("starty",e.y).replaceAll("width",e.width).replaceAll("half",e.height/2)),l.append("path").attr("fill","none").attr("stroke-width","0.5").attr("stroke",i).attr("d","Mstartx,startyc-5,0 -5,half -5,halfc0,half 5,half 5,half".replaceAll("startx",e.x+e.width).replaceAll("starty",e.y).replaceAll("half",e.height/2));break}let h=FTe(r,e.typeC4Shape.text);switch(l.append("text").attr("fill",a).attr("font-family",h.fontFamily).attr("font-size",h.fontSize-2).attr("font-style","italic").attr("lengthAdjust","spacing").attr("textLength",e.typeC4Shape.width).attr("x",e.x+e.width/2-e.typeC4Shape.width/2).attr("y",e.y+e.typeC4Shape.Y).text("<<"+e.typeC4Shape.text+">>"),e.typeC4Shape.text){case"person":case"external_person":SW(l,48,48,e.x+e.width/2-24,e.y+e.image.Y,s);break}let f=r[e.typeC4Shape.text+"Font"]();return f.fontWeight="bold",f.fontSize=f.fontSize+2,f.fontColor=a,bu(r)(e.label.text,l,e.x,e.y+e.label.Y,e.width,e.height,{fill:a},f),f=r[e.typeC4Shape.text+"Font"](),f.fontColor=a,e.techn&&e.techn?.text!==""?bu(r)(e.techn.text,l,e.x,e.y+e.techn.Y,e.width,e.height,{fill:a,"font-style":"italic"},f):e.type&&e.type.text!==""&&bu(r)(e.type.text,l,e.x,e.y+e.type.Y,e.width,e.height,{fill:a,"font-style":"italic"},f),e.descr&&e.descr.text!==""&&(f=r.personFont(),f.fontColor=a,bu(r)(e.descr.text,l,e.x,e.y+e.descr.Y,e.width,e.height,{fill:a},f)),e.height},"drawC4Shape"),DTe=o(function(t){t.append("defs").append("symbol").attr("id","database").attr("fill-rule","evenodd").attr("clip-rule","evenodd").append("path").attr("transform","scale(.5)").attr("d","M12.258.001l.256.004.255.005.253.008.251.01.249.012.247.015.246.016.242.019.241.02.239.023.236.024.233.027.231.028.229.031.225.032.223.034.22.036.217.038.214.04.211.041.208.043.205.045.201.046.198.048.194.05.191.051.187.053.183.054.18.056.175.057.172.059.168.06.163.061.16.063.155.064.15.066.074.033.073.033.071.034.07.034.069.035.068.035.067.035.066.035.064.036.064.036.062.036.06.036.06.037.058.037.058.037.055.038.055.038.053.038.052.038.051.039.05.039.048.039.047.039.045.04.044.04.043.04.041.04.04.041.039.041.037.041.036.041.034.041.033.042.032.042.03.042.029.042.027.042.026.043.024.043.023.043.021.043.02.043.018.044.017.043.015.044.013.044.012.044.011.045.009.044.007.045.006.045.004.045.002.045.001.045v17l-.001.045-.002.045-.004.045-.006.045-.007.045-.009.044-.011.045-.012.044-.013.044-.015.044-.017.043-.018.044-.02.043-.021.043-.023.043-.024.043-.026.043-.027.042-.029.042-.03.042-.032.042-.033.042-.034.041-.036.041-.037.041-.039.041-.04.041-.041.04-.043.04-.044.04-.045.04-.047.039-.048.039-.05.039-.051.039-.052.038-.053.038-.055.038-.055.038-.058.037-.058.037-.06.037-.06.036-.062.036-.064.036-.064.036-.066.035-.067.035-.068.035-.069.035-.07.034-.071.034-.073.033-.074.033-.15.066-.155.064-.16.063-.163.061-.168.06-.172.059-.175.057-.18.056-.183.054-.187.053-.191.051-.194.05-.198.048-.201.046-.205.045-.208.043-.211.041-.214.04-.217.038-.22.036-.223.034-.225.032-.229.031-.231.028-.233.027-.236.024-.239.023-.241.02-.242.019-.246.016-.247.015-.249.012-.251.01-.253.008-.255.005-.256.004-.258.001-.258-.001-.256-.004-.255-.005-.253-.008-.251-.01-.249-.012-.247-.015-.245-.016-.243-.019-.241-.02-.238-.023-.236-.024-.234-.027-.231-.028-.228-.031-.226-.032-.223-.034-.22-.036-.217-.038-.214-.04-.211-.041-.208-.043-.204-.045-.201-.046-.198-.048-.195-.05-.19-.051-.187-.053-.184-.054-.179-.056-.176-.057-.172-.059-.167-.06-.164-.061-.159-.063-.155-.064-.151-.066-.074-.033-.072-.033-.072-.034-.07-.034-.069-.035-.068-.035-.067-.035-.066-.035-.064-.036-.063-.036-.062-.036-.061-.036-.06-.037-.058-.037-.057-.037-.056-.038-.055-.038-.053-.038-.052-.038-.051-.039-.049-.039-.049-.039-.046-.039-.046-.04-.044-.04-.043-.04-.041-.04-.04-.041-.039-.041-.037-.041-.036-.041-.034-.041-.033-.042-.032-.042-.03-.042-.029-.042-.027-.042-.026-.043-.024-.043-.023-.043-.021-.043-.02-.043-.018-.044-.017-.043-.015-.044-.013-.044-.012-.044-.011-.045-.009-.044-.007-.045-.006-.045-.004-.045-.002-.045-.001-.045v-17l.001-.045.002-.045.004-.045.006-.045.007-.045.009-.044.011-.045.012-.044.013-.044.015-.044.017-.043.018-.044.02-.043.021-.043.023-.043.024-.043.026-.043.027-.042.029-.042.03-.042.032-.042.033-.042.034-.041.036-.041.037-.041.039-.041.04-.041.041-.04.043-.04.044-.04.046-.04.046-.039.049-.039.049-.039.051-.039.052-.038.053-.038.055-.038.056-.038.057-.037.058-.037.06-.037.061-.036.062-.036.063-.036.064-.036.066-.035.067-.035.068-.035.069-.035.07-.034.072-.034.072-.033.074-.033.151-.066.155-.064.159-.063.164-.061.167-.06.172-.059.176-.057.179-.056.184-.054.187-.053.19-.051.195-.05.198-.048.201-.046.204-.045.208-.043.211-.041.214-.04.217-.038.22-.036.223-.034.226-.032.228-.031.231-.028.234-.027.236-.024.238-.023.241-.02.243-.019.245-.016.247-.015.249-.012.251-.01.253-.008.255-.005.256-.004.258-.001.258.001zm-9.258 20.499v.01l.001.021.003.021.004.022.005.021.006.022.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.023.018.024.019.024.021.024.022.025.023.024.024.025.052.049.056.05.061.051.066.051.07.051.075.051.079.052.084.052.088.052.092.052.097.052.102.051.105.052.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.048.144.049.147.047.152.047.155.047.16.045.163.045.167.043.171.043.176.041.178.041.183.039.187.039.19.037.194.035.197.035.202.033.204.031.209.03.212.029.216.027.219.025.222.024.226.021.23.02.233.018.236.016.24.015.243.012.246.01.249.008.253.005.256.004.259.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.021.224-.024.22-.026.216-.027.212-.028.21-.031.205-.031.202-.034.198-.034.194-.036.191-.037.187-.039.183-.04.179-.04.175-.042.172-.043.168-.044.163-.045.16-.046.155-.046.152-.047.148-.048.143-.049.139-.049.136-.05.131-.05.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.053.083-.051.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.05.023-.024.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.023.01-.022.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.127l-.077.055-.08.053-.083.054-.085.053-.087.052-.09.052-.093.051-.095.05-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.045-.118.044-.12.043-.122.042-.124.042-.126.041-.128.04-.13.04-.132.038-.134.038-.135.037-.138.037-.139.035-.142.035-.143.034-.144.033-.147.032-.148.031-.15.03-.151.03-.153.029-.154.027-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.01-.179.008-.179.008-.181.006-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.006-.179-.008-.179-.008-.178-.01-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.027-.153-.029-.151-.03-.15-.03-.148-.031-.146-.032-.145-.033-.143-.034-.141-.035-.14-.035-.137-.037-.136-.037-.134-.038-.132-.038-.13-.04-.128-.04-.126-.041-.124-.042-.122-.042-.12-.044-.117-.043-.116-.045-.113-.045-.112-.046-.109-.047-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.05-.093-.052-.09-.051-.087-.052-.085-.053-.083-.054-.08-.054-.077-.054v4.127zm0-5.654v.011l.001.021.003.021.004.021.005.022.006.022.007.022.009.022.01.022.011.023.012.023.013.023.015.024.016.023.017.024.018.024.019.024.021.024.022.024.023.025.024.024.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.052.11.051.114.051.119.052.123.05.127.051.131.05.135.049.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.044.171.042.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.022.23.02.233.018.236.016.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.012.241-.015.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.048.139-.05.136-.049.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.051.051-.049.023-.025.023-.024.021-.025.02-.024.019-.024.018-.024.017-.024.015-.023.014-.023.013-.024.012-.022.01-.023.01-.023.008-.022.006-.022.006-.022.004-.021.004-.022.001-.021.001-.021v-4.139l-.077.054-.08.054-.083.054-.085.052-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.044-.118.044-.12.044-.122.042-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.035-.143.033-.144.033-.147.033-.148.031-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.009-.179.009-.179.007-.181.007-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.007-.179-.007-.179-.009-.178-.009-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.031-.146-.033-.145-.033-.143-.033-.141-.035-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.04-.126-.041-.124-.042-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.051-.093-.051-.09-.051-.087-.053-.085-.052-.083-.054-.08-.054-.077-.054v4.139zm0-5.666v.011l.001.02.003.022.004.021.005.022.006.021.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.024.018.023.019.024.021.025.022.024.023.024.024.025.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.051.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.043.171.043.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.021.23.02.233.018.236.017.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.013.241-.014.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.049.139-.049.136-.049.131-.051.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.049.023-.025.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.022.01-.023.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.153l-.077.054-.08.054-.083.053-.085.053-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.048-.105.048-.106.048-.109.046-.111.046-.114.046-.115.044-.118.044-.12.043-.122.043-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.034-.143.034-.144.033-.147.032-.148.032-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.024-.161.024-.162.023-.163.023-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.01-.178.01-.179.009-.179.007-.181.006-.182.006-.182.004-.184.003-.184.001-.185.001-.185-.001-.184-.001-.184-.003-.182-.004-.182-.006-.181-.006-.179-.007-.179-.009-.178-.01-.176-.01-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.023-.162-.023-.161-.024-.159-.024-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.032-.146-.032-.145-.033-.143-.034-.141-.034-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.041-.126-.041-.124-.041-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.048-.105-.048-.102-.048-.1-.05-.097-.049-.095-.051-.093-.051-.09-.052-.087-.052-.085-.053-.083-.053-.08-.054-.077-.054v4.153zm8.74-8.179l-.257.004-.254.005-.25.008-.247.011-.244.012-.241.014-.237.016-.233.018-.231.021-.226.022-.224.023-.22.026-.216.027-.212.028-.21.031-.205.032-.202.033-.198.034-.194.036-.191.038-.187.038-.183.04-.179.041-.175.042-.172.043-.168.043-.163.045-.16.046-.155.046-.152.048-.148.048-.143.048-.139.049-.136.05-.131.05-.126.051-.123.051-.118.051-.114.052-.11.052-.106.052-.101.052-.096.052-.092.052-.088.052-.083.052-.079.052-.074.051-.07.052-.065.051-.06.05-.056.05-.051.05-.023.025-.023.024-.021.024-.02.025-.019.024-.018.024-.017.023-.015.024-.014.023-.013.023-.012.023-.01.023-.01.022-.008.022-.006.023-.006.021-.004.022-.004.021-.001.021-.001.021.001.021.001.021.004.021.004.022.006.021.006.023.008.022.01.022.01.023.012.023.013.023.014.023.015.024.017.023.018.024.019.024.02.025.021.024.023.024.023.025.051.05.056.05.06.05.065.051.07.052.074.051.079.052.083.052.088.052.092.052.096.052.101.052.106.052.11.052.114.052.118.051.123.051.126.051.131.05.136.05.139.049.143.048.148.048.152.048.155.046.16.046.163.045.168.043.172.043.175.042.179.041.183.04.187.038.191.038.194.036.198.034.202.033.205.032.21.031.212.028.216.027.22.026.224.023.226.022.231.021.233.018.237.016.241.014.244.012.247.011.25.008.254.005.257.004.26.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.022.224-.023.22-.026.216-.027.212-.028.21-.031.205-.032.202-.033.198-.034.194-.036.191-.038.187-.038.183-.04.179-.041.175-.042.172-.043.168-.043.163-.045.16-.046.155-.046.152-.048.148-.048.143-.048.139-.049.136-.05.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.05.051-.05.023-.025.023-.024.021-.024.02-.025.019-.024.018-.024.017-.023.015-.024.014-.023.013-.023.012-.023.01-.023.01-.022.008-.022.006-.023.006-.021.004-.022.004-.021.001-.021.001-.021-.001-.021-.001-.021-.004-.021-.004-.022-.006-.021-.006-.023-.008-.022-.01-.022-.01-.023-.012-.023-.013-.023-.014-.023-.015-.024-.017-.023-.018-.024-.019-.024-.02-.025-.021-.024-.023-.024-.023-.025-.051-.05-.056-.05-.06-.05-.065-.051-.07-.052-.074-.051-.079-.052-.083-.052-.088-.052-.092-.052-.096-.052-.101-.052-.106-.052-.11-.052-.114-.052-.118-.051-.123-.051-.126-.051-.131-.05-.136-.05-.139-.049-.143-.048-.148-.048-.152-.048-.155-.046-.16-.046-.163-.045-.168-.043-.172-.043-.175-.042-.179-.041-.183-.04-.187-.038-.191-.038-.194-.036-.198-.034-.202-.033-.205-.032-.21-.031-.212-.028-.216-.027-.22-.026-.224-.023-.226-.022-.231-.021-.233-.018-.237-.016-.241-.014-.244-.012-.247-.011-.25-.008-.254-.005-.257-.004-.26-.001-.26.001z")},"insertDatabaseIcon"),RTe=o(function(t){t.append("defs").append("symbol").attr("id","computer").attr("width","24").attr("height","24").append("path").attr("transform","scale(.5)").attr("d","M2 2v13h20v-13h-20zm18 11h-16v-9h16v9zm-10.228 6l.466-1h3.524l.467 1h-4.457zm14.228 3h-24l2-6h2.104l-1.33 4h18.45l-1.297-4h2.073l2 6zm-5-10h-14v-7h14v7z")},"insertComputerIcon"),NTe=o(function(t){t.append("defs").append("symbol").attr("id","clock").attr("width","24").attr("height","24").append("path").attr("transform","scale(.5)").attr("d","M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5.848 12.459c.202.038.202.333.001.372-1.907.361-6.045 1.111-6.547 1.111-.719 0-1.301-.582-1.301-1.301 0-.512.77-5.447 1.125-7.445.034-.192.312-.181.343.014l.985 6.238 5.394 1.011z")},"insertClockIcon"),MTe=o(function(t){t.append("defs").append("marker").attr("id","arrowhead").attr("refX",9).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z")},"insertArrowHead"),ITe=o(function(t){t.append("defs").append("marker").attr("id","arrowend").attr("refX",1).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 10 0 L 0 5 L 10 10 z")},"insertArrowEnd"),OTe=o(function(t){t.append("defs").append("marker").attr("id","filled-head").attr("refX",18).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},"insertArrowFilledHead"),PTe=o(function(t){t.append("defs").append("marker").attr("id","sequencenumber").attr("refX",15).attr("refY",15).attr("markerWidth",60).attr("markerHeight",40).attr("orient","auto").append("circle").attr("cx",15).attr("cy",15).attr("r",6)},"insertDynamicNumber"),BTe=o(function(t){let r=t.append("defs").append("marker").attr("id","crosshead").attr("markerWidth",15).attr("markerHeight",8).attr("orient","auto").attr("refX",16).attr("refY",4);r.append("path").attr("fill","black").attr("stroke","#000000").style("stroke-dasharray","0, 0").attr("stroke-width","1px").attr("d","M 9,2 V 6 L16,4 Z"),r.append("path").attr("fill","none").attr("stroke","#000000").style("stroke-dasharray","0, 0").attr("stroke-width","1px").attr("d","M 0,1 L 6,7 M 6,1 L 0,7")},"insertArrowCrossHead"),FTe=o((t,e)=>({fontFamily:t[e+"FontFamily"],fontSize:t[e+"FontSize"],fontWeight:t[e+"FontWeight"]}),"getC4ShapeFont"),bu=function(){function t(i,a,s,l,u,h,f){let d=a.append("text").attr("x",s+u/2).attr("y",l+h/2+5).style("text-anchor","middle").text(i);n(d,f)}o(t,"byText");function e(i,a,s,l,u,h,f,d){let{fontSize:p,fontFamily:m,fontWeight:g}=d,y=i.split(We.lineBreakRegex);for(let v=0;v{"use strict";zTe=typeof global=="object"&&global&&global.Object===Object&&global,Q3=zTe});var GTe,$Te,Jn,Ro=R(()=>{"use strict";A_();GTe=typeof self=="object"&&self&&self.Object===Object&&self,$Te=Q3||GTe||Function("return this")(),Jn=$Te});var VTe,Ji,vd=R(()=>{"use strict";Ro();VTe=Jn.Symbol,Ji=VTe});function YTe(t){var e=UTe.call(t,Zy),r=t[Zy];try{t[Zy]=void 0;var n=!0}catch{}var i=HTe.call(t);return n&&(e?t[Zy]=r:delete t[Zy]),i}var _W,UTe,HTe,Zy,LW,DW=R(()=>{"use strict";vd();_W=Object.prototype,UTe=_W.hasOwnProperty,HTe=_W.toString,Zy=Ji?Ji.toStringTag:void 0;o(YTe,"getRawTag");LW=YTe});function XTe(t){return qTe.call(t)}var WTe,qTe,RW,NW=R(()=>{"use strict";WTe=Object.prototype,qTe=WTe.toString;o(XTe,"objectToString");RW=XTe});function QTe(t){return t==null?t===void 0?KTe:jTe:MW&&MW in Object(t)?LW(t):RW(t)}var jTe,KTe,MW,fa,wu=R(()=>{"use strict";vd();DW();NW();jTe="[object Null]",KTe="[object Undefined]",MW=Ji?Ji.toStringTag:void 0;o(QTe,"baseGetTag");fa=QTe});function ZTe(t){var e=typeof t;return t!=null&&(e=="object"||e=="function")}var pn,Js=R(()=>{"use strict";o(ZTe,"isObject");pn=ZTe});function nke(t){if(!pn(t))return!1;var e=fa(t);return e==eke||e==tke||e==JTe||e==rke}var JTe,eke,tke,rke,wi,Jy=R(()=>{"use strict";wu();Js();JTe="[object AsyncFunction]",eke="[object Function]",tke="[object GeneratorFunction]",rke="[object Proxy]";o(nke,"isFunction");wi=nke});var ike,Z3,IW=R(()=>{"use strict";Ro();ike=Jn["__core-js_shared__"],Z3=ike});function ake(t){return!!OW&&OW in t}var OW,PW,BW=R(()=>{"use strict";IW();OW=function(){var t=/[^.]+$/.exec(Z3&&Z3.keys&&Z3.keys.IE_PROTO||"");return t?"Symbol(src)_1."+t:""}();o(ake,"isMasked");PW=ake});function lke(t){if(t!=null){try{return oke.call(t)}catch{}try{return t+""}catch{}}return""}var ske,oke,Tu,__=R(()=>{"use strict";ske=Function.prototype,oke=ske.toString;o(lke,"toSource");Tu=lke});function gke(t){if(!pn(t)||PW(t))return!1;var e=wi(t)?mke:uke;return e.test(Tu(t))}var cke,uke,hke,fke,dke,pke,mke,FW,zW=R(()=>{"use strict";Jy();BW();Js();__();cke=/[\\^$.*+?()[\]{}|]/g,uke=/^\[object .+?Constructor\]$/,hke=Function.prototype,fke=Object.prototype,dke=hke.toString,pke=fke.hasOwnProperty,mke=RegExp("^"+dke.call(pke).replace(cke,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");o(gke,"baseIsNative");FW=gke});function yke(t,e){return t?.[e]}var GW,$W=R(()=>{"use strict";o(yke,"getValue");GW=yke});function vke(t,e){var r=GW(t,e);return FW(r)?r:void 0}var xs,Nh=R(()=>{"use strict";zW();$W();o(vke,"getNative");xs=vke});var xke,ku,ev=R(()=>{"use strict";Nh();xke=xs(Object,"create"),ku=xke});function bke(){this.__data__=ku?ku(null):{},this.size=0}var VW,UW=R(()=>{"use strict";ev();o(bke,"hashClear");VW=bke});function wke(t){var e=this.has(t)&&delete this.__data__[t];return this.size-=e?1:0,e}var HW,YW=R(()=>{"use strict";o(wke,"hashDelete");HW=wke});function Cke(t){var e=this.__data__;if(ku){var r=e[t];return r===Tke?void 0:r}return Eke.call(e,t)?e[t]:void 0}var Tke,kke,Eke,WW,qW=R(()=>{"use strict";ev();Tke="__lodash_hash_undefined__",kke=Object.prototype,Eke=kke.hasOwnProperty;o(Cke,"hashGet");WW=Cke});function _ke(t){var e=this.__data__;return ku?e[t]!==void 0:Ake.call(e,t)}var Ske,Ake,XW,jW=R(()=>{"use strict";ev();Ske=Object.prototype,Ake=Ske.hasOwnProperty;o(_ke,"hashHas");XW=_ke});function Dke(t,e){var r=this.__data__;return this.size+=this.has(t)?0:1,r[t]=ku&&e===void 0?Lke:e,this}var Lke,KW,QW=R(()=>{"use strict";ev();Lke="__lodash_hash_undefined__";o(Dke,"hashSet");KW=Dke});function Hp(t){var e=-1,r=t==null?0:t.length;for(this.clear();++e{"use strict";UW();YW();qW();jW();QW();o(Hp,"Hash");Hp.prototype.clear=VW;Hp.prototype.delete=HW;Hp.prototype.get=WW;Hp.prototype.has=XW;Hp.prototype.set=KW;L_=Hp});function Rke(){this.__data__=[],this.size=0}var JW,eq=R(()=>{"use strict";o(Rke,"listCacheClear");JW=Rke});function Nke(t,e){return t===e||t!==t&&e!==e}var No,xd=R(()=>{"use strict";o(Nke,"eq");No=Nke});function Mke(t,e){for(var r=t.length;r--;)if(No(t[r][0],e))return r;return-1}var Mh,tv=R(()=>{"use strict";xd();o(Mke,"assocIndexOf");Mh=Mke});function Pke(t){var e=this.__data__,r=Mh(e,t);if(r<0)return!1;var n=e.length-1;return r==n?e.pop():Oke.call(e,r,1),--this.size,!0}var Ike,Oke,tq,rq=R(()=>{"use strict";tv();Ike=Array.prototype,Oke=Ike.splice;o(Pke,"listCacheDelete");tq=Pke});function Bke(t){var e=this.__data__,r=Mh(e,t);return r<0?void 0:e[r][1]}var nq,iq=R(()=>{"use strict";tv();o(Bke,"listCacheGet");nq=Bke});function Fke(t){return Mh(this.__data__,t)>-1}var aq,sq=R(()=>{"use strict";tv();o(Fke,"listCacheHas");aq=Fke});function zke(t,e){var r=this.__data__,n=Mh(r,t);return n<0?(++this.size,r.push([t,e])):r[n][1]=e,this}var oq,lq=R(()=>{"use strict";tv();o(zke,"listCacheSet");oq=zke});function Yp(t){var e=-1,r=t==null?0:t.length;for(this.clear();++e{"use strict";eq();rq();iq();sq();lq();o(Yp,"ListCache");Yp.prototype.clear=JW;Yp.prototype.delete=tq;Yp.prototype.get=nq;Yp.prototype.has=aq;Yp.prototype.set=oq;Ih=Yp});var Gke,Oh,J3=R(()=>{"use strict";Nh();Ro();Gke=xs(Jn,"Map"),Oh=Gke});function $ke(){this.size=0,this.__data__={hash:new L_,map:new(Oh||Ih),string:new L_}}var cq,uq=R(()=>{"use strict";ZW();rv();J3();o($ke,"mapCacheClear");cq=$ke});function Vke(t){var e=typeof t;return e=="string"||e=="number"||e=="symbol"||e=="boolean"?t!=="__proto__":t===null}var hq,fq=R(()=>{"use strict";o(Vke,"isKeyable");hq=Vke});function Uke(t,e){var r=t.__data__;return hq(e)?r[typeof e=="string"?"string":"hash"]:r.map}var Ph,nv=R(()=>{"use strict";fq();o(Uke,"getMapData");Ph=Uke});function Hke(t){var e=Ph(this,t).delete(t);return this.size-=e?1:0,e}var dq,pq=R(()=>{"use strict";nv();o(Hke,"mapCacheDelete");dq=Hke});function Yke(t){return Ph(this,t).get(t)}var mq,gq=R(()=>{"use strict";nv();o(Yke,"mapCacheGet");mq=Yke});function Wke(t){return Ph(this,t).has(t)}var yq,vq=R(()=>{"use strict";nv();o(Wke,"mapCacheHas");yq=Wke});function qke(t,e){var r=Ph(this,t),n=r.size;return r.set(t,e),this.size+=r.size==n?0:1,this}var xq,bq=R(()=>{"use strict";nv();o(qke,"mapCacheSet");xq=qke});function Wp(t){var e=-1,r=t==null?0:t.length;for(this.clear();++e{"use strict";uq();pq();gq();vq();bq();o(Wp,"MapCache");Wp.prototype.clear=cq;Wp.prototype.delete=dq;Wp.prototype.get=mq;Wp.prototype.has=yq;Wp.prototype.set=xq;bd=Wp});function D_(t,e){if(typeof t!="function"||e!=null&&typeof e!="function")throw new TypeError(Xke);var r=o(function(){var n=arguments,i=e?e.apply(this,n):n[0],a=r.cache;if(a.has(i))return a.get(i);var s=t.apply(this,n);return r.cache=a.set(i,s)||a,s},"memoized");return r.cache=new(D_.Cache||bd),r}var Xke,qp,R_=R(()=>{"use strict";e5();Xke="Expected a function";o(D_,"memoize");D_.Cache=bd;qp=D_});function jke(){this.__data__=new Ih,this.size=0}var wq,Tq=R(()=>{"use strict";rv();o(jke,"stackClear");wq=jke});function Kke(t){var e=this.__data__,r=e.delete(t);return this.size=e.size,r}var kq,Eq=R(()=>{"use strict";o(Kke,"stackDelete");kq=Kke});function Qke(t){return this.__data__.get(t)}var Cq,Sq=R(()=>{"use strict";o(Qke,"stackGet");Cq=Qke});function Zke(t){return this.__data__.has(t)}var Aq,_q=R(()=>{"use strict";o(Zke,"stackHas");Aq=Zke});function eEe(t,e){var r=this.__data__;if(r instanceof Ih){var n=r.__data__;if(!Oh||n.length{"use strict";rv();J3();e5();Jke=200;o(eEe,"stackSet");Lq=eEe});function Xp(t){var e=this.__data__=new Ih(t);this.size=e.size}var uc,iv=R(()=>{"use strict";rv();Tq();Eq();Sq();_q();Dq();o(Xp,"Stack");Xp.prototype.clear=wq;Xp.prototype.delete=kq;Xp.prototype.get=Cq;Xp.prototype.has=Aq;Xp.prototype.set=Lq;uc=Xp});var tEe,jp,N_=R(()=>{"use strict";Nh();tEe=function(){try{var t=xs(Object,"defineProperty");return t({},"",{}),t}catch{}}(),jp=tEe});function rEe(t,e,r){e=="__proto__"&&jp?jp(t,e,{configurable:!0,enumerable:!0,value:r,writable:!0}):t[e]=r}var hc,Kp=R(()=>{"use strict";N_();o(rEe,"baseAssignValue");hc=rEe});function nEe(t,e,r){(r!==void 0&&!No(t[e],r)||r===void 0&&!(e in t))&&hc(t,e,r)}var av,M_=R(()=>{"use strict";Kp();xd();o(nEe,"assignMergeValue");av=nEe});function iEe(t){return function(e,r,n){for(var i=-1,a=Object(e),s=n(e),l=s.length;l--;){var u=s[t?l:++i];if(r(a[u],u,a)===!1)break}return e}}var Rq,Nq=R(()=>{"use strict";o(iEe,"createBaseFor");Rq=iEe});var aEe,Qp,t5=R(()=>{"use strict";Nq();aEe=Rq(),Qp=aEe});function oEe(t,e){if(e)return t.slice();var r=t.length,n=Oq?Oq(r):new t.constructor(r);return t.copy(n),n}var Pq,Mq,sEe,Iq,Oq,r5,I_=R(()=>{"use strict";Ro();Pq=typeof exports=="object"&&exports&&!exports.nodeType&&exports,Mq=Pq&&typeof module=="object"&&module&&!module.nodeType&&module,sEe=Mq&&Mq.exports===Pq,Iq=sEe?Jn.Buffer:void 0,Oq=Iq?Iq.allocUnsafe:void 0;o(oEe,"cloneBuffer");r5=oEe});var lEe,Zp,O_=R(()=>{"use strict";Ro();lEe=Jn.Uint8Array,Zp=lEe});function cEe(t){var e=new t.constructor(t.byteLength);return new Zp(e).set(new Zp(t)),e}var Jp,n5=R(()=>{"use strict";O_();o(cEe,"cloneArrayBuffer");Jp=cEe});function uEe(t,e){var r=e?Jp(t.buffer):t.buffer;return new t.constructor(r,t.byteOffset,t.length)}var i5,P_=R(()=>{"use strict";n5();o(uEe,"cloneTypedArray");i5=uEe});function hEe(t,e){var r=-1,n=t.length;for(e||(e=Array(n));++r{"use strict";o(hEe,"copyArray");a5=hEe});var Bq,fEe,Fq,zq=R(()=>{"use strict";Js();Bq=Object.create,fEe=function(){function t(){}return o(t,"object"),function(e){if(!pn(e))return{};if(Bq)return Bq(e);t.prototype=e;var r=new t;return t.prototype=void 0,r}}(),Fq=fEe});function dEe(t,e){return function(r){return t(e(r))}}var s5,F_=R(()=>{"use strict";o(dEe,"overArg");s5=dEe});var pEe,em,o5=R(()=>{"use strict";F_();pEe=s5(Object.getPrototypeOf,Object),em=pEe});function gEe(t){var e=t&&t.constructor,r=typeof e=="function"&&e.prototype||mEe;return t===r}var mEe,fc,tm=R(()=>{"use strict";mEe=Object.prototype;o(gEe,"isPrototype");fc=gEe});function yEe(t){return typeof t.constructor=="function"&&!fc(t)?Fq(em(t)):{}}var l5,z_=R(()=>{"use strict";zq();o5();tm();o(yEe,"initCloneObject");l5=yEe});function vEe(t){return t!=null&&typeof t=="object"}var Wn,Mo=R(()=>{"use strict";o(vEe,"isObjectLike");Wn=vEe});function bEe(t){return Wn(t)&&fa(t)==xEe}var xEe,G_,Gq=R(()=>{"use strict";wu();Mo();xEe="[object Arguments]";o(bEe,"baseIsArguments");G_=bEe});var $q,wEe,TEe,kEe,kl,rm=R(()=>{"use strict";Gq();Mo();$q=Object.prototype,wEe=$q.hasOwnProperty,TEe=$q.propertyIsEnumerable,kEe=G_(function(){return arguments}())?G_:function(t){return Wn(t)&&wEe.call(t,"callee")&&!TEe.call(t,"callee")},kl=kEe});var EEe,wt,Bn=R(()=>{"use strict";EEe=Array.isArray,wt=EEe});function SEe(t){return typeof t=="number"&&t>-1&&t%1==0&&t<=CEe}var CEe,nm,c5=R(()=>{"use strict";CEe=9007199254740991;o(SEe,"isLength");nm=SEe});function AEe(t){return t!=null&&nm(t.length)&&!wi(t)}var ei,Io=R(()=>{"use strict";Jy();c5();o(AEe,"isArrayLike");ei=AEe});function _Ee(t){return Wn(t)&&ei(t)}var wd,u5=R(()=>{"use strict";Io();Mo();o(_Ee,"isArrayLikeObject");wd=_Ee});function LEe(){return!1}var Vq,Uq=R(()=>{"use strict";o(LEe,"stubFalse");Vq=LEe});var Wq,Hq,DEe,Yq,REe,NEe,El,im=R(()=>{"use strict";Ro();Uq();Wq=typeof exports=="object"&&exports&&!exports.nodeType&&exports,Hq=Wq&&typeof module=="object"&&module&&!module.nodeType&&module,DEe=Hq&&Hq.exports===Wq,Yq=DEe?Jn.Buffer:void 0,REe=Yq?Yq.isBuffer:void 0,NEe=REe||Vq,El=NEe});function FEe(t){if(!Wn(t)||fa(t)!=MEe)return!1;var e=em(t);if(e===null)return!0;var r=PEe.call(e,"constructor")&&e.constructor;return typeof r=="function"&&r instanceof r&&qq.call(r)==BEe}var MEe,IEe,OEe,qq,PEe,BEe,Xq,jq=R(()=>{"use strict";wu();o5();Mo();MEe="[object Object]",IEe=Function.prototype,OEe=Object.prototype,qq=IEe.toString,PEe=OEe.hasOwnProperty,BEe=qq.call(Object);o(FEe,"isPlainObject");Xq=FEe});function c6e(t){return Wn(t)&&nm(t.length)&&!!Mn[fa(t)]}var zEe,GEe,$Ee,VEe,UEe,HEe,YEe,WEe,qEe,XEe,jEe,KEe,QEe,ZEe,JEe,e6e,t6e,r6e,n6e,i6e,a6e,s6e,o6e,l6e,Mn,Kq,Qq=R(()=>{"use strict";wu();c5();Mo();zEe="[object Arguments]",GEe="[object Array]",$Ee="[object Boolean]",VEe="[object Date]",UEe="[object Error]",HEe="[object Function]",YEe="[object Map]",WEe="[object Number]",qEe="[object Object]",XEe="[object RegExp]",jEe="[object Set]",KEe="[object String]",QEe="[object WeakMap]",ZEe="[object ArrayBuffer]",JEe="[object DataView]",e6e="[object Float32Array]",t6e="[object Float64Array]",r6e="[object Int8Array]",n6e="[object Int16Array]",i6e="[object Int32Array]",a6e="[object Uint8Array]",s6e="[object Uint8ClampedArray]",o6e="[object Uint16Array]",l6e="[object Uint32Array]",Mn={};Mn[e6e]=Mn[t6e]=Mn[r6e]=Mn[n6e]=Mn[i6e]=Mn[a6e]=Mn[s6e]=Mn[o6e]=Mn[l6e]=!0;Mn[zEe]=Mn[GEe]=Mn[ZEe]=Mn[$Ee]=Mn[JEe]=Mn[VEe]=Mn[UEe]=Mn[HEe]=Mn[YEe]=Mn[WEe]=Mn[qEe]=Mn[XEe]=Mn[jEe]=Mn[KEe]=Mn[QEe]=!1;o(c6e,"baseIsTypedArray");Kq=c6e});function u6e(t){return function(e){return t(e)}}var Oo,Td=R(()=>{"use strict";o(u6e,"baseUnary");Oo=u6e});var Zq,sv,h6e,$_,f6e,Po,ov=R(()=>{"use strict";A_();Zq=typeof exports=="object"&&exports&&!exports.nodeType&&exports,sv=Zq&&typeof module=="object"&&module&&!module.nodeType&&module,h6e=sv&&sv.exports===Zq,$_=h6e&&Q3.process,f6e=function(){try{var t=sv&&sv.require&&sv.require("util").types;return t||$_&&$_.binding&&$_.binding("util")}catch{}}(),Po=f6e});var Jq,d6e,Bh,lv=R(()=>{"use strict";Qq();Td();ov();Jq=Po&&Po.isTypedArray,d6e=Jq?Oo(Jq):Kq,Bh=d6e});function p6e(t,e){if(!(e==="constructor"&&typeof t[e]=="function")&&e!="__proto__")return t[e]}var cv,V_=R(()=>{"use strict";o(p6e,"safeGet");cv=p6e});function y6e(t,e,r){var n=t[e];(!(g6e.call(t,e)&&No(n,r))||r===void 0&&!(e in t))&&hc(t,e,r)}var m6e,g6e,dc,am=R(()=>{"use strict";Kp();xd();m6e=Object.prototype,g6e=m6e.hasOwnProperty;o(y6e,"assignValue");dc=y6e});function v6e(t,e,r,n){var i=!r;r||(r={});for(var a=-1,s=e.length;++a{"use strict";am();Kp();o(v6e,"copyObject");Bo=v6e});function x6e(t,e){for(var r=-1,n=Array(t);++r{"use strict";o(x6e,"baseTimes");eX=x6e});function T6e(t,e){var r=typeof t;return e=e??b6e,!!e&&(r=="number"||r!="symbol"&&w6e.test(t))&&t>-1&&t%1==0&&t{"use strict";b6e=9007199254740991,w6e=/^(?:0|[1-9]\d*)$/;o(T6e,"isIndex");Fh=T6e});function C6e(t,e){var r=wt(t),n=!r&&kl(t),i=!r&&!n&&El(t),a=!r&&!n&&!i&&Bh(t),s=r||n||i||a,l=s?eX(t.length,String):[],u=l.length;for(var h in t)(e||E6e.call(t,h))&&!(s&&(h=="length"||i&&(h=="offset"||h=="parent")||a&&(h=="buffer"||h=="byteLength"||h=="byteOffset")||Fh(h,u)))&&l.push(h);return l}var k6e,E6e,h5,U_=R(()=>{"use strict";tX();rm();Bn();im();uv();lv();k6e=Object.prototype,E6e=k6e.hasOwnProperty;o(C6e,"arrayLikeKeys");h5=C6e});function S6e(t){var e=[];if(t!=null)for(var r in Object(t))e.push(r);return e}var rX,nX=R(()=>{"use strict";o(S6e,"nativeKeysIn");rX=S6e});function L6e(t){if(!pn(t))return rX(t);var e=fc(t),r=[];for(var n in t)n=="constructor"&&(e||!_6e.call(t,n))||r.push(n);return r}var A6e,_6e,iX,aX=R(()=>{"use strict";Js();tm();nX();A6e=Object.prototype,_6e=A6e.hasOwnProperty;o(L6e,"baseKeysIn");iX=L6e});function D6e(t){return ei(t)?h5(t,!0):iX(t)}var bs,zh=R(()=>{"use strict";U_();aX();Io();o(D6e,"keysIn");bs=D6e});function R6e(t){return Bo(t,bs(t))}var sX,oX=R(()=>{"use strict";kd();zh();o(R6e,"toPlainObject");sX=R6e});function N6e(t,e,r,n,i,a,s){var l=cv(t,r),u=cv(e,r),h=s.get(u);if(h){av(t,r,h);return}var f=a?a(l,u,r+"",t,e,s):void 0,d=f===void 0;if(d){var p=wt(u),m=!p&&El(u),g=!p&&!m&&Bh(u);f=u,p||m||g?wt(l)?f=l:wd(l)?f=a5(l):m?(d=!1,f=r5(u,!0)):g?(d=!1,f=i5(u,!0)):f=[]:Xq(u)||kl(u)?(f=l,kl(l)?f=sX(l):(!pn(l)||wi(l))&&(f=l5(u))):d=!1}d&&(s.set(u,f),i(f,u,n,a,s),s.delete(u)),av(t,r,f)}var lX,cX=R(()=>{"use strict";M_();I_();P_();B_();z_();rm();Bn();u5();im();Jy();Js();jq();lv();V_();oX();o(N6e,"baseMergeDeep");lX=N6e});function uX(t,e,r,n,i){t!==e&&Qp(e,function(a,s){if(i||(i=new uc),pn(a))lX(t,e,s,r,uX,n,i);else{var l=n?n(cv(t,s),a,s+"",t,e,i):void 0;l===void 0&&(l=a),av(t,s,l)}},bs)}var hX,fX=R(()=>{"use strict";iv();M_();t5();cX();Js();zh();V_();o(uX,"baseMerge");hX=uX});function M6e(t){return t}var ea,Eu=R(()=>{"use strict";o(M6e,"identity");ea=M6e});function I6e(t,e,r){switch(r.length){case 0:return t.call(e);case 1:return t.call(e,r[0]);case 2:return t.call(e,r[0],r[1]);case 3:return t.call(e,r[0],r[1],r[2])}return t.apply(e,r)}var dX,pX=R(()=>{"use strict";o(I6e,"apply");dX=I6e});function O6e(t,e,r){return e=mX(e===void 0?t.length-1:e,0),function(){for(var n=arguments,i=-1,a=mX(n.length-e,0),s=Array(a);++i{"use strict";pX();mX=Math.max;o(O6e,"overRest");f5=O6e});function P6e(t){return function(){return t}}var ws,Y_=R(()=>{"use strict";o(P6e,"constant");ws=P6e});var B6e,gX,yX=R(()=>{"use strict";Y_();N_();Eu();B6e=jp?function(t,e){return jp(t,"toString",{configurable:!0,enumerable:!1,value:ws(e),writable:!0})}:ea,gX=B6e});function $6e(t){var e=0,r=0;return function(){var n=G6e(),i=z6e-(n-r);if(r=n,i>0){if(++e>=F6e)return arguments[0]}else e=0;return t.apply(void 0,arguments)}}var F6e,z6e,G6e,vX,xX=R(()=>{"use strict";F6e=800,z6e=16,G6e=Date.now;o($6e,"shortOut");vX=$6e});var V6e,d5,W_=R(()=>{"use strict";yX();xX();V6e=vX(gX),d5=V6e});function U6e(t,e){return d5(f5(t,e,ea),t+"")}var pc,sm=R(()=>{"use strict";Eu();H_();W_();o(U6e,"baseRest");pc=U6e});function H6e(t,e,r){if(!pn(r))return!1;var n=typeof e;return(n=="number"?ei(r)&&Fh(e,r.length):n=="string"&&e in r)?No(r[e],t):!1}var eo,Ed=R(()=>{"use strict";xd();Io();uv();Js();o(H6e,"isIterateeCall");eo=H6e});function Y6e(t){return pc(function(e,r){var n=-1,i=r.length,a=i>1?r[i-1]:void 0,s=i>2?r[2]:void 0;for(a=t.length>3&&typeof a=="function"?(i--,a):void 0,s&&eo(r[0],r[1],s)&&(a=i<3?void 0:a,i=1),e=Object(e);++n{"use strict";sm();Ed();o(Y6e,"createAssigner");p5=Y6e});var W6e,Gh,X_=R(()=>{"use strict";fX();q_();W6e=p5(function(t,e,r){hX(t,e,r)}),Gh=W6e});function om(t,e){if(!t)return e;let r=`curve${t.charAt(0).toUpperCase()+t.slice(1)}`;return q6e[r]??e}function Q6e(t,e){let r=t.trim();if(r)return e.securityLevel!=="loose"?(0,TX.sanitizeUrl)(r):r}function CX(t,e){return!t||!e?0:Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))}function J6e(t){let e,r=0;t.forEach(i=>{r+=CX(i,e),e=i});let n=r/2;return Q_(t,n)}function eCe(t){return t.length===1?t[0]:J6e(t)}function rCe(t,e,r){let n=structuredClone(r);V.info("our points",n),e!=="start_left"&&e!=="start_right"&&n.reverse();let i=25+t,a=Q_(n,i),s=10+t*.5,l=Math.atan2(n[0].y-a.y,n[0].x-a.x),u={x:0,y:0};return e==="start_left"?(u.x=Math.sin(l+Math.PI)*s+(n[0].x+a.x)/2,u.y=-Math.cos(l+Math.PI)*s+(n[0].y+a.y)/2):e==="end_right"?(u.x=Math.sin(l-Math.PI)*s+(n[0].x+a.x)/2-5,u.y=-Math.cos(l-Math.PI)*s+(n[0].y+a.y)/2-5):e==="end_left"?(u.x=Math.sin(l)*s+(n[0].x+a.x)/2-5,u.y=-Math.cos(l)*s+(n[0].y+a.y)/2-5):(u.x=Math.sin(l)*s+(n[0].x+a.x)/2,u.y=-Math.cos(l)*s+(n[0].y+a.y)/2),u}function lm(t){let e="",r="";for(let n of t)n!==void 0&&(n.startsWith("color:")||n.startsWith("text-align:")?r=r+n+";":e=e+n+";");return{style:e,labelStyle:r}}function nCe(t){let e="",r="0123456789abcdef",n=r.length;for(let i=0;i{"use strict";TX=Xi(Up(),1);Zt();rr();r7();ut();Hf();cp();R_();X_();Vb();K_="\u200B",q6e={curveBasis:vs,curveBasisClosed:B3,curveBasisOpen:F3,curveBumpX:s_,curveBumpY:o_,curveBundle:l_,curveCardinalClosed:u_,curveCardinalOpen:f_,curveCardinal:c_,curveCatmullRomClosed:m_,curveCatmullRomOpen:g_,curveCatmullRom:p_,curveLinear:xu,curveLinearClosed:U3,curveMonotoneX:v_,curveMonotoneY:x_,curveNatural:Y3,curveStep:q3,curveStepAfter:w_,curveStepBefore:b_},X6e=/\s*(?:(\w+)(?=:):|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi,j6e=o(function(t,e){let r=kX(t,/(?:init\b)|(?:initialize\b)/),n={};if(Array.isArray(r)){let s=r.map(l=>l.args);fp(s),n=On(n,[...s])}else n=r.args;if(!n)return;let i=lp(t,e),a="config";return n[a]!==void 0&&(i==="flowchart-v2"&&(i="flowchart"),n[i]=n[a],delete n[a]),n},"detectInit"),kX=o(function(t,e=null){try{let r=new RegExp(`[%]{2}(?![{]${X6e.source})(?=[}][%]{2}).* +`,"ig");t=t.trim().replace(r,"").replace(/'/gm,'"'),V.debug(`Detecting diagram directive${e!==null?" type:"+e:""} based on the text:${t}`);let n,i=[];for(;(n=Vf.exec(t))!==null;)if(n.index===Vf.lastIndex&&Vf.lastIndex++,n&&!e||e&&n[1]?.match(e)||e&&n[2]?.match(e)){let a=n[1]?n[1]:n[2],s=n[3]?n[3].trim():n[4]?JSON.parse(n[4].trim()):null;i.push({type:a,args:s})}return i.length===0?{type:t,args:null}:i.length===1?i[0]:i}catch(r){return V.error(`ERROR: ${r.message} - Unable to parse directive type: '${e}' based on the text: '${t}'`),{type:void 0,args:null}}},"detectDirective"),EX=o(function(t){return t.replace(Vf,"")},"removeDirectives"),K6e=o(function(t,e){for(let[r,n]of e.entries())if(n.match(t))return r;return-1},"isSubstringInArray");o(om,"interpolateToCurve");o(Q6e,"formatUrl");Z6e=o((t,...e)=>{let r=t.split("."),n=r.length-1,i=r[n],a=window;for(let s=0;s{let r=Math.pow(10,e);return Math.round(t*r)/r},"roundNumber"),Q_=o((t,e)=>{let r,n=e;for(let i of t){if(r){let a=CX(i,r);if(a=1)return{x:i.x,y:i.y};if(s>0&&s<1)return{x:bX((1-s)*r.x+s*i.x,5),y:bX((1-s)*r.y+s*i.y,5)}}}r=i}throw new Error("Could not find a suitable point for the given distance")},"calculatePoint"),tCe=o((t,e,r)=>{V.info(`our points ${JSON.stringify(e)}`),e[0]!==r&&(e=e.reverse());let i=Q_(e,25),a=t?10:5,s=Math.atan2(e[0].y-i.y,e[0].x-i.x),l={x:0,y:0};return l.x=Math.sin(s)*a+(e[0].x+i.x)/2,l.y=-Math.cos(s)*a+(e[0].y+i.y)/2,l},"calcCardinalityPosition");o(rCe,"calcTerminalLabelPosition");o(lm,"getStylesFromArray");wX=0,Z_=o(()=>(wX++,"id-"+Math.random().toString(36).substr(2,12)+"-"+wX),"generateId");o(nCe,"makeRandomHex");J_=o(t=>nCe(t.length),"random"),iCe=o(function(){return{x:0,y:0,fill:void 0,anchor:"start",style:"#666",width:100,height:100,textMargin:0,rx:0,ry:0,valign:void 0,text:""}},"getTextObj"),aCe=o(function(t,e){let r=e.text.replace(We.lineBreakRegex," "),[,n]=mc(e.fontSize),i=t.append("text");i.attr("x",e.x),i.attr("y",e.y),i.style("text-anchor",e.anchor),i.style("font-family",e.fontFamily),i.style("font-size",n),i.style("font-weight",e.fontWeight),i.attr("fill",e.fill),e.class!==void 0&&i.attr("class",e.class);let a=i.append("tspan");return a.attr("x",e.x+e.textMargin*2),a.attr("fill",e.fill),a.text(r),i},"drawSimpleText"),e9=qp((t,e,r)=>{if(!t||(r=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",joinWith:"
"},r),We.lineBreakRegex.test(t)))return t;let n=t.split(" ").filter(Boolean),i=[],a="";return n.forEach((s,l)=>{let u=Cl(`${s} `,r),h=Cl(a,r);if(u>e){let{hyphenatedStrings:p,remainingWord:m}=sCe(s,e,"-",r);i.push(a,...p),a=m}else h+u>=e?(i.push(a),a=s):a=[a,s].filter(Boolean).join(" ");l+1===n.length&&i.push(a)}),i.filter(s=>s!=="").join(r.joinWith)},(t,e,r)=>`${t}${e}${r.fontSize}${r.fontWeight}${r.fontFamily}${r.joinWith}`),sCe=qp((t,e,r="-",n)=>{n=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",margin:0},n);let i=[...t],a=[],s="";return i.forEach((l,u)=>{let h=`${s}${l}`;if(Cl(h,n)>=e){let d=u+1,p=i.length===d,m=`${h}${r}`;a.push(p?h:m),s=""}else s=h}),{hyphenatedStrings:a,remainingWord:s}},(t,e,r="-",n)=>`${t}${e}${r}${n.fontSize}${n.fontWeight}${n.fontFamily}`);o(g5,"calculateTextHeight");o(Cl,"calculateTextWidth");t9=qp((t,e)=>{let{fontSize:r=12,fontFamily:n="Arial",fontWeight:i=400}=e;if(!t)return{width:0,height:0};let[,a]=mc(r),s=["sans-serif",n],l=t.split(We.lineBreakRegex),u=[],h=$e("body");if(!h.remove)return{width:0,height:0,lineHeight:0};let f=h.append("svg");for(let p of s){let m=0,g={width:0,height:0,lineHeight:0};for(let y of l){let v=iCe();v.text=y||K_;let x=aCe(f,v).style("font-size",a).style("font-weight",i).style("font-family",p),b=(x._groups||x)[0][0].getBBox();if(b.width===0&&b.height===0)throw new Error("svg element not in render tree");g.width=Math.round(Math.max(g.width,b.width)),m=Math.round(b.height),g.height+=m,g.lineHeight=Math.round(Math.max(g.lineHeight,m))}u.push(g)}f.remove();let d=isNaN(u[1].height)||isNaN(u[1].width)||isNaN(u[1].lineHeight)||u[0].height>u[1].height&&u[0].width>u[1].width&&u[0].lineHeight>u[1].lineHeight?0:1;return u[d]},(t,e)=>`${t}${e.fontSize}${e.fontWeight}${e.fontFamily}`),j_=class{constructor(e=!1,r){this.count=0;this.count=r?r.length:0,this.next=e?()=>this.count++:()=>Date.now()}static{o(this,"InitIDGenerator")}},oCe=o(function(t){return m5=m5||document.createElement("div"),t=escape(t).replace(/%26/g,"&").replace(/%23/g,"#").replace(/%3B/g,";"),m5.innerHTML=t,unescape(m5.textContent)},"entityDecode");o(r9,"isDetailedError");lCe=o((t,e,r,n)=>{if(!n)return;let i=t.node()?.getBBox();i&&t.append("text").text(n).attr("x",i.x+i.width/2).attr("y",-r).attr("class",e)},"insertTitle"),mc=o(t=>{if(typeof t=="number")return[t,t+"px"];let e=parseInt(t??"",10);return Number.isNaN(e)?[void 0,void 0]:t===String(e)?[e,t+"px"]:[e,t]},"parseFontSize");o(Ts,"cleanAndMerge");Lt={assignWithDepth:On,wrapLabel:e9,calculateTextHeight:g5,calculateTextWidth:Cl,calculateTextDimensions:t9,cleanAndMerge:Ts,detectInit:j6e,detectDirective:kX,isSubstringInArray:K6e,interpolateToCurve:om,calcLabelPosition:eCe,calcCardinalityPosition:tCe,calcTerminalLabelPosition:rCe,formatUrl:Q6e,getStylesFromArray:lm,generateId:Z_,random:J_,runFunc:Z6e,entityDecode:oCe,insertTitle:lCe,parseFontSize:mc,InitIDGenerator:j_},SX=o(function(t){let e=t;return e=e.replace(/style.*:\S*#.*;/g,function(r){return r.substring(0,r.length-1)}),e=e.replace(/classDef.*:\S*#.*;/g,function(r){return r.substring(0,r.length-1)}),e=e.replace(/#\w+;/g,function(r){let n=r.substring(1,r.length-1);return/^\+?\d+$/.test(n)?"\uFB02\xB0\xB0"+n+"\xB6\xDF":"\uFB02\xB0"+n+"\xB6\xDF"}),e},"encodeEntities"),to=o(function(t){return t.replace(/fl°°/g,"&#").replace(/fl°/g,"&").replace(/¶ß/g,";")},"decodeEntities"),y5=o((t,e,{counter:r=0,prefix:n,suffix:i})=>`${n?`${n}_`:""}${t}_${e}_${r}${i?`_${i}`:""}`,"getEdgeId")});function Sl(t,e,r,n,i){if(!e[t].width)if(r)e[t].text=e9(e[t].text,i,n),e[t].textLines=e[t].text.split(We.lineBreakRegex).length,e[t].width=i,e[t].height=g5(e[t].text,n);else{let a=e[t].text.split(We.lineBreakRegex);e[t].textLines=a.length;let s=0;e[t].height=0,e[t].width=0;for(let l of a)e[t].width=Math.max(Cl(l,n),e[t].width),s=g5(l,n),e[t].height=e[t].height+s}}function RX(t,e,r,n,i){let a=new w5(i);a.data.widthLimit=r.data.widthLimit/Math.min(n9,n.length);for(let[s,l]of n.entries()){let u=0;l.image={width:0,height:0,Y:0},l.sprite&&(l.image.width=48,l.image.height=48,l.image.Y=u,u=l.image.Y+l.image.height);let h=l.wrap&&Nt.wrap,f=v5(Nt);if(f.fontSize=f.fontSize+2,f.fontWeight="bold",Sl("label",l,h,f,a.data.widthLimit),l.label.Y=u+8,u=l.label.Y+l.label.height,l.type&&l.type.text!==""){l.type.text="["+l.type.text+"]";let g=v5(Nt);Sl("type",l,h,g,a.data.widthLimit),l.type.Y=u+5,u=l.type.Y+l.type.height}if(l.descr&&l.descr.text!==""){let g=v5(Nt);g.fontSize=g.fontSize-2,Sl("descr",l,h,g,a.data.widthLimit),l.descr.Y=u+20,u=l.descr.Y+l.descr.height}if(s==0||s%n9===0){let g=r.data.startx+Nt.diagramMarginX,y=r.data.stopy+Nt.diagramMarginY+u;a.setData(g,g,y,y)}else{let g=a.data.stopx!==a.data.startx?a.data.stopx+Nt.diagramMarginX:a.data.startx,y=a.data.starty;a.setData(g,g,y,y)}a.name=l.alias;let d=i.db.getC4ShapeArray(l.alias),p=i.db.getC4ShapeKeys(l.alias);p.length>0&&DX(a,t,d,p),e=l.alias;let m=i.db.getBoundarys(e);m.length>0&&RX(t,e,a,m,i),l.alias!=="global"&&LX(t,l,a),r.data.stopy=Math.max(a.data.stopy+Nt.c4ShapeMargin,r.data.stopy),r.data.stopx=Math.max(a.data.stopx+Nt.c4ShapeMargin,r.data.stopx),x5=Math.max(x5,r.data.stopx),b5=Math.max(b5,r.data.stopy)}}var x5,b5,_X,n9,Nt,w5,i9,hv,v5,cCe,LX,DX,ks,AX,uCe,hCe,fCe,a9,NX=R(()=>{"use strict";Zt();AW();ut();VC();rr();lS();_t();cp();xr();Yn();x5=0,b5=0,_X=4,n9=2;U1.yy=hy;Nt={},w5=class{static{o(this,"Bounds")}constructor(e){this.name="",this.data={},this.data.startx=void 0,this.data.stopx=void 0,this.data.starty=void 0,this.data.stopy=void 0,this.data.widthLimit=void 0,this.nextData={},this.nextData.startx=void 0,this.nextData.stopx=void 0,this.nextData.starty=void 0,this.nextData.stopy=void 0,this.nextData.cnt=0,i9(e.db.getConfig())}setData(e,r,n,i){this.nextData.startx=this.data.startx=e,this.nextData.stopx=this.data.stopx=r,this.nextData.starty=this.data.starty=n,this.nextData.stopy=this.data.stopy=i}updateVal(e,r,n,i){e[r]===void 0?e[r]=n:e[r]=i(n,e[r])}insert(e){this.nextData.cnt=this.nextData.cnt+1;let r=this.nextData.startx===this.nextData.stopx?this.nextData.stopx+e.margin:this.nextData.stopx+e.margin*2,n=r+e.width,i=this.nextData.starty+e.margin*2,a=i+e.height;(r>=this.data.widthLimit||n>=this.data.widthLimit||this.nextData.cnt>_X)&&(r=this.nextData.startx+e.margin+Nt.nextLinePaddingX,i=this.nextData.stopy+e.margin*2,this.nextData.stopx=n=r+e.width,this.nextData.starty=this.nextData.stopy,this.nextData.stopy=a=i+e.height,this.nextData.cnt=1),e.x=r,e.y=i,this.updateVal(this.data,"startx",r,Math.min),this.updateVal(this.data,"starty",i,Math.min),this.updateVal(this.data,"stopx",n,Math.max),this.updateVal(this.data,"stopy",a,Math.max),this.updateVal(this.nextData,"startx",r,Math.min),this.updateVal(this.nextData,"starty",i,Math.min),this.updateVal(this.nextData,"stopx",n,Math.max),this.updateVal(this.nextData,"stopy",a,Math.max)}init(e){this.name="",this.data={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0,widthLimit:void 0},this.nextData={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0,cnt:0},i9(e.db.getConfig())}bumpLastMargin(e){this.data.stopx+=e,this.data.stopy+=e}},i9=o(function(t){On(Nt,t),t.fontFamily&&(Nt.personFontFamily=Nt.systemFontFamily=Nt.messageFontFamily=t.fontFamily),t.fontSize&&(Nt.personFontSize=Nt.systemFontSize=Nt.messageFontSize=t.fontSize),t.fontWeight&&(Nt.personFontWeight=Nt.systemFontWeight=Nt.messageFontWeight=t.fontWeight)},"setConf"),hv=o((t,e)=>({fontFamily:t[e+"FontFamily"],fontSize:t[e+"FontSize"],fontWeight:t[e+"FontWeight"]}),"c4ShapeFont"),v5=o(t=>({fontFamily:t.boundaryFontFamily,fontSize:t.boundaryFontSize,fontWeight:t.boundaryFontWeight}),"boundaryFont"),cCe=o(t=>({fontFamily:t.messageFontFamily,fontSize:t.messageFontSize,fontWeight:t.messageFontWeight}),"messageFont");o(Sl,"calcC4ShapeTextWH");LX=o(function(t,e,r){e.x=r.data.startx,e.y=r.data.starty,e.width=r.data.stopx-r.data.startx,e.height=r.data.stopy-r.data.starty,e.label.y=Nt.c4ShapeMargin-35;let n=e.wrap&&Nt.wrap,i=v5(Nt);i.fontSize=i.fontSize+2,i.fontWeight="bold";let a=Cl(e.label.text,i);Sl("label",e,n,i,a),Tl.drawBoundary(t,e,Nt)},"drawBoundary"),DX=o(function(t,e,r,n){let i=0;for(let a of n){i=0;let s=r[a],l=hv(Nt,s.typeC4Shape.text);switch(l.fontSize=l.fontSize-2,s.typeC4Shape.width=Cl("\xAB"+s.typeC4Shape.text+"\xBB",l),s.typeC4Shape.height=l.fontSize+2,s.typeC4Shape.Y=Nt.c4ShapePadding,i=s.typeC4Shape.Y+s.typeC4Shape.height-4,s.image={width:0,height:0,Y:0},s.typeC4Shape.text){case"person":case"external_person":s.image.width=48,s.image.height=48,s.image.Y=i,i=s.image.Y+s.image.height;break}s.sprite&&(s.image.width=48,s.image.height=48,s.image.Y=i,i=s.image.Y+s.image.height);let u=s.wrap&&Nt.wrap,h=Nt.width-Nt.c4ShapePadding*2,f=hv(Nt,s.typeC4Shape.text);if(f.fontSize=f.fontSize+2,f.fontWeight="bold",Sl("label",s,u,f,h),s.label.Y=i+8,i=s.label.Y+s.label.height,s.type&&s.type.text!==""){s.type.text="["+s.type.text+"]";let m=hv(Nt,s.typeC4Shape.text);Sl("type",s,u,m,h),s.type.Y=i+5,i=s.type.Y+s.type.height}else if(s.techn&&s.techn.text!==""){s.techn.text="["+s.techn.text+"]";let m=hv(Nt,s.techn.text);Sl("techn",s,u,m,h),s.techn.Y=i+5,i=s.techn.Y+s.techn.height}let d=i,p=s.label.width;if(s.descr&&s.descr.text!==""){let m=hv(Nt,s.typeC4Shape.text);Sl("descr",s,u,m,h),s.descr.Y=i+20,i=s.descr.Y+s.descr.height,p=Math.max(s.label.width,s.descr.width),d=i-s.descr.textLines*5}p=p+Nt.c4ShapePadding,s.width=Math.max(s.width||Nt.width,p,Nt.width),s.height=Math.max(s.height||Nt.height,d,Nt.height),s.margin=s.margin||Nt.c4ShapeMargin,t.insert(s),Tl.drawC4Shape(e,s,Nt)}t.bumpLastMargin(Nt.c4ShapeMargin)},"drawC4ShapeArray"),ks=class{static{o(this,"Point")}constructor(e,r){this.x=e,this.y=r}},AX=o(function(t,e){let r=t.x,n=t.y,i=e.x,a=e.y,s=r+t.width/2,l=n+t.height/2,u=Math.abs(r-i),h=Math.abs(n-a),f=h/u,d=t.height/t.width,p=null;return n==a&&ri?p=new ks(r,l):r==i&&na&&(p=new ks(s,n)),r>i&&n=f?p=new ks(r,l+f*t.width/2):p=new ks(s-u/h*t.height/2,n+t.height):r=f?p=new ks(r+t.width,l+f*t.width/2):p=new ks(s+u/h*t.height/2,n+t.height):ra?d>=f?p=new ks(r+t.width,l-f*t.width/2):p=new ks(s+t.height/2*u/h,n):r>i&&n>a&&(d>=f?p=new ks(r,l-t.width/2*f):p=new ks(s-t.height/2*u/h,n)),p},"getIntersectPoint"),uCe=o(function(t,e){let r={x:0,y:0};r.x=e.x+e.width/2,r.y=e.y+e.height/2;let n=AX(t,r);r.x=t.x+t.width/2,r.y=t.y+t.height/2;let i=AX(e,r);return{startPoint:n,endPoint:i}},"getIntersectPoints"),hCe=o(function(t,e,r,n){let i=0;for(let a of e){i=i+1;let s=a.wrap&&Nt.wrap,l=cCe(Nt);n.db.getC4Type()==="C4Dynamic"&&(a.label.text=i+": "+a.label.text);let h=Cl(a.label.text,l);Sl("label",a,s,l,h),a.techn&&a.techn.text!==""&&(h=Cl(a.techn.text,l),Sl("techn",a,s,l,h)),a.descr&&a.descr.text!==""&&(h=Cl(a.descr.text,l),Sl("descr",a,s,l,h));let f=r(a.from),d=r(a.to),p=uCe(f,d);a.startPoint=p.startPoint,a.endPoint=p.endPoint}Tl.drawRels(t,e,Nt)},"drawRels");o(RX,"drawInsideBoundary");fCe=o(function(t,e,r,n){Nt=de().c4;let i=de().securityLevel,a;i==="sandbox"&&(a=$e("#i"+e));let s=i==="sandbox"?$e(a.nodes()[0].contentDocument.body):$e("body"),l=n.db;n.db.setWrap(Nt.wrap),_X=l.getC4ShapeInRow(),n9=l.getC4BoundaryInRow(),V.debug(`C:${JSON.stringify(Nt,null,2)}`);let u=i==="sandbox"?s.select(`[id="${e}"]`):$e(`[id="${e}"]`);Tl.insertComputerIcon(u),Tl.insertDatabaseIcon(u),Tl.insertClockIcon(u);let h=new w5(n);h.setData(Nt.diagramMarginX,Nt.diagramMarginX,Nt.diagramMarginY,Nt.diagramMarginY),h.data.widthLimit=screen.availWidth,x5=Nt.diagramMarginX,b5=Nt.diagramMarginY;let f=n.db.getTitle(),d=n.db.getBoundarys("");RX(u,"",h,d,n),Tl.insertArrowHead(u),Tl.insertArrowEnd(u),Tl.insertArrowCrossHead(u),Tl.insertArrowFilledHead(u),hCe(u,n.db.getRels(),n.db.getC4Shape,n),h.data.stopx=x5,h.data.stopy=b5;let p=h.data,g=p.stopy-p.starty+2*Nt.diagramMarginY,v=p.stopx-p.startx+2*Nt.diagramMarginX;f&&u.append("text").text(f).attr("x",(p.stopx-p.startx)/2-4*Nt.diagramMarginX).attr("y",p.starty+Nt.diagramMarginY),Sr(u,g,v,Nt.useMaxWidth);let x=f?60:0;u.attr("viewBox",p.startx-Nt.diagramMarginX+" -"+(Nt.diagramMarginY+x)+" "+v+" "+(g+x)),V.debug("models:",p)},"draw"),a9={drawPersonOrSystemArray:DX,drawBoundary:LX,setConf:i9,draw:fCe}});var dCe,MX,IX=R(()=>{"use strict";dCe=o(t=>`.person { + stroke: ${t.personBorder}; + fill: ${t.personBkg}; + } +`,"getStyles"),MX=dCe});var OX={};hr(OX,{diagram:()=>pCe});var pCe,PX=R(()=>{"use strict";VC();lS();NX();IX();pCe={parser:rz,db:hy,renderer:a9,styles:MX,init:o(({c4:t,wrap:e})=>{a9.setConf(t),hy.setWrap(e)},"init")}});function o9(t){let e=[];for(let r of t){let n=dv.get(r);n?.styles&&(e=[...e,...n.styles??[]].map(i=>i.trim())),n?.textStyles&&(e=[...e,...n.textStyles??[]].map(i=>i.trim()))}return e}var vCe,zX,cm,$h,Es,dv,Cu,l9,c9,T5,s9,Fo,k5,E5,C5,S5,xCe,bCe,wCe,TCe,kCe,ECe,CCe,u9,SCe,ACe,_Ce,GX,LCe,DCe,h9,$X,VX,RCe,UX,NCe,MCe,ICe,OCe,PCe,fv,HX,YX,BCe,FCe,WX,zCe,GCe,$Ce,VCe,UCe,qX,XX,HCe,YCe,WCe,qCe,XCe,jCe,A5,f9=R(()=>{"use strict";Zt();xr();_t();rr();ut();bi();vCe="flowchart-",zX=0,cm=de(),$h=new Map,Es=[],dv=new Map,Cu=[],l9=new Map,c9=new Map,T5=0,s9=!0,E5=[],C5=o(t=>We.sanitizeText(t,cm),"sanitizeText"),S5=o(function(t){for(let e of $h.values())if(e.id===t)return e.domId;return t},"lookUpDomId"),xCe=o(function(t,e,r,n,i,a,s={}){if(!t||t.trim().length===0)return;let l,u=$h.get(t);u===void 0&&(u={id:t,labelType:"text",domId:vCe+t+"-"+zX,styles:[],classes:[]},$h.set(t,u)),zX++,e!==void 0?(cm=de(),l=C5(e.text.trim()),u.labelType=e.type,l.startsWith('"')&&l.endsWith('"')&&(l=l.substring(1,l.length-1)),u.text=l):u.text===void 0&&(u.text=t),r!==void 0&&(u.type=r),n?.forEach(function(h){u.styles.push(h)}),i?.forEach(function(h){u.classes.push(h)}),a!==void 0&&(u.dir=a),u.props===void 0?u.props=s:s!==void 0&&Object.assign(u.props,s)},"addVertex"),bCe=o(function(t,e,r){let a={start:t,end:e,type:void 0,text:"",labelType:"text"};V.info("abc78 Got edge...",a);let s=r.text;if(s!==void 0&&(a.text=C5(s.text.trim()),a.text.startsWith('"')&&a.text.endsWith('"')&&(a.text=a.text.substring(1,a.text.length-1)),a.labelType=s.type),r!==void 0&&(a.type=r.type,a.stroke=r.stroke,a.length=r.length>10?10:r.length),Es.length<(cm.maxEdges??500))V.info("Pushing edge..."),Es.push(a);else throw new Error(`Edge limit exceeded. ${Es.length} edges found, but the limit is ${cm.maxEdges}. + +Initialize mermaid with maxEdges set to a higher number to allow more edges. +You cannot set this config via configuration inside the diagram as it is a secure config. +You have to call mermaid.initialize.`)},"addSingleLink"),wCe=o(function(t,e,r){V.info("addLink",t,e,r);for(let n of t)for(let i of e)bCe(n,i,r)},"addLink"),TCe=o(function(t,e){t.forEach(function(r){r==="default"?Es.defaultInterpolate=e:Es[r].interpolate=e})},"updateLinkInterpolate"),kCe=o(function(t,e){t.forEach(function(r){if(typeof r=="number"&&r>=Es.length)throw new Error(`The index ${r} for linkStyle is out of bounds. Valid indices for linkStyle are between 0 and ${Es.length-1}. (Help: Ensure that the index is within the range of existing edges.)`);r==="default"?Es.defaultStyle=e:(Es[r].style=e,(Es[r]?.style?.length??0)>0&&!Es[r]?.style?.some(n=>n?.startsWith("fill"))&&Es[r]?.style?.push("fill:none"))})},"updateLink"),ECe=o(function(t,e){t.split(",").forEach(function(r){let n=dv.get(r);n===void 0&&(n={id:r,styles:[],textStyles:[]},dv.set(r,n)),e?.forEach(function(i){if(/color/.exec(i)){let a=i.replace("fill","bgFill");n.textStyles.push(a)}n.styles.push(i)})})},"addClass"),CCe=o(function(t){Fo=t,/.*/.exec(Fo)&&(Fo="LR"),/.*v/.exec(Fo)&&(Fo="TB"),Fo==="TD"&&(Fo="TB")},"setDirection"),u9=o(function(t,e){for(let r of t.split(",")){let n=$h.get(r);n&&n.classes.push(e);let i=l9.get(r);i&&i.classes.push(e)}},"setClass"),SCe=o(function(t,e){if(e!==void 0){e=C5(e);for(let r of t.split(","))c9.set(k5==="gen-1"?S5(r):r,e)}},"setTooltip"),ACe=o(function(t,e,r){let n=S5(t);if(de().securityLevel!=="loose"||e===void 0)return;let i=[];if(typeof r=="string"){i=r.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(let s=0;s")),i.classed("hover",!0)}).on("mouseout",function(){e.transition().duration(500).style("opacity",0),$e(this).classed("hover",!1)})},"setupToolTips");E5.push(UX);NCe=o(function(t="gen-1"){$h=new Map,dv=new Map,Es=[],E5=[UX],Cu=[],l9=new Map,T5=0,c9=new Map,s9=!0,k5=t,cm=de(),vr()},"clear"),MCe=o(t=>{k5=t||"gen-2"},"setGen"),ICe=o(function(){return"fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;"},"defaultStyle"),OCe=o(function(t,e,r){let n=t.text.trim(),i=r.text;t===r&&/\s/.exec(r.text)&&(n=void 0);function a(h){let f={boolean:{},number:{},string:{}},d=[],p;return{nodeList:h.filter(function(g){let y=typeof g;return g.stmt&&g.stmt==="dir"?(p=g.value,!1):g.trim()===""?!1:y in f?f[y].hasOwnProperty(g)?!1:f[y][g]=!0:d.includes(g)?!1:d.push(g)}),dir:p}}o(a,"uniq");let{nodeList:s,dir:l}=a(e.flat());if(k5==="gen-1")for(let h=0;h2e3)return{result:!1,count:0};if(HX[fv]=e,Cu[e].id===t)return{result:!0,count:0};let n=0,i=1;for(;n=0){let s=YX(t,a);if(s.result)return{result:!0,count:i+s.count};i=i+s.count}n=n+1}return{result:!1,count:i}},"indexNodes2"),BCe=o(function(t){return HX[t]},"getDepthFirstPos"),FCe=o(function(){fv=-1,Cu.length>0&&YX("none",Cu.length-1)},"indexNodes"),WX=o(function(){return Cu},"getSubGraphs"),zCe=o(()=>s9?(s9=!1,!0):!1,"firstGraph"),GCe=o(t=>{let e=t.trim(),r="arrow_open";switch(e[0]){case"<":r="arrow_point",e=e.slice(1);break;case"x":r="arrow_cross",e=e.slice(1);break;case"o":r="arrow_circle",e=e.slice(1);break}let n="normal";return e.includes("=")&&(n="thick"),e.includes(".")&&(n="dotted"),{type:r,stroke:n}},"destructStartLink"),$Ce=o((t,e)=>{let r=e.length,n=0;for(let i=0;i{let e=t.trim(),r=e.slice(0,-1),n="arrow_open";switch(e.slice(-1)){case"x":n="arrow_cross",e.startsWith("x")&&(n="double_"+n,r=r.slice(1));break;case">":n="arrow_point",e.startsWith("<")&&(n="double_"+n,r=r.slice(1));break;case"o":n="arrow_circle",e.startsWith("o")&&(n="double_"+n,r=r.slice(1));break}let i="normal",a=r.length-1;r.startsWith("=")&&(i="thick"),r.startsWith("~")&&(i="invisible");let s=$Ce(".",r);return s&&(i="dotted",a=s),{type:n,stroke:i,length:a}},"destructEndLink"),UCe=o((t,e)=>{let r=VCe(t),n;if(e){if(n=GCe(e),n.stroke!==r.stroke)return{type:"INVALID",stroke:"INVALID"};if(n.type==="arrow_open")n.type=r.type;else{if(n.type!==r.type)return{type:"INVALID",stroke:"INVALID"};n.type="double_"+n.type}return n.type==="double_arrow"&&(n.type="double_arrow_point"),n.length=r.length,n}return r},"destructLink"),qX=o((t,e)=>{for(let r of t)if(r.nodes.includes(e))return!0;return!1},"exists"),XX=o((t,e)=>{let r=[];return t.nodes.forEach((n,i)=>{qX(e,n)||r.push(t.nodes[i])}),{nodes:r}},"makeUniq"),HCe={firstGraph:zCe},YCe=o(t=>t.type==="square"?"squareRect":t.type==="round"?"roundedRect":t.type??"squareRect","getTypeFromVertex"),WCe=o((t,e)=>t.find(r=>r.id===e),"findNode"),qCe=o(t=>{let e="none",r="arrow_point";switch(t){case"arrow_point":case"arrow_circle":case"arrow_cross":r=t;break;case"double_arrow_point":case"double_arrow_circle":case"double_arrow_cross":e=t.replace("double_",""),r=e;break}return{arrowTypeStart:e,arrowTypeEnd:r}},"destructEdgeType"),XCe=o((t,e,r,n,i,a)=>{let s=r.get(t.id),l=n.get(t.id)??!1,u=WCe(e,t.id);u?(u.cssStyles=t.styles,u.cssCompiledStyles=o9(t.classes),u.cssClasses=t.classes.join(" ")):e.push({id:t.id,label:t.text,labelStyle:"",parentId:s,padding:i.flowchart?.padding||8,cssStyles:t.styles,cssCompiledStyles:o9(["default","node",...t.classes]),cssClasses:"default "+t.classes.join(" "),shape:YCe(t),dir:t.dir,domId:t.domId,isGroup:l,look:a,link:t.link,linkTarget:t.linkTarget,tooltip:GX(t.id)})},"addNodeFromVertex");o(o9,"getCompiledStyles");jCe=o(()=>{let t=de(),e=[],r=[],n=WX(),i=new Map,a=new Map;for(let u=n.length-1;u>=0;u--){let h=n[u];h.nodes.length>0&&a.set(h.id,!0);for(let f of h.nodes)i.set(f,h.id)}for(let u=n.length-1;u>=0;u--){let h=n[u];e.push({id:h.id,label:h.title,labelStyle:"",parentId:i.get(h.id),padding:8,cssCompiledStyles:o9(h.classes),cssClasses:h.classes.join(" "),shape:"rect",dir:h.dir,isGroup:!0,look:t.look})}$X().forEach(u=>{XCe(u,e,i,a,t,t.look||"classic")});let l=VX();return l.forEach((u,h)=>{let{arrowTypeStart:f,arrowTypeEnd:d}=qCe(u.type),p=[...l.defaultStyle??[]];u.style&&p.push(...u.style);let m={id:y5(u.start,u.end,{counter:h,prefix:"L"}),start:u.start,end:u.end,type:u.type??"normal",label:u.text,labelpos:"c",thickness:u.stroke,minlen:u.length,classes:u?.stroke==="invisible"?"":"edge-thickness-normal edge-pattern-solid flowchart-link",arrowTypeStart:u?.stroke==="invisible"?"none":f,arrowTypeEnd:u?.stroke==="invisible"?"none":d,arrowheadStyle:"fill: #333",labelStyle:p,style:p,pattern:u.stroke,look:t.look};r.push(m)}),{nodes:e,edges:r,other:{},config:t}},"getData"),A5={defaultConfig:o(()=>_4.flowchart,"defaultConfig"),setAccTitle:kr,getAccTitle:Ar,getAccDescription:Lr,getData:jCe,setAccDescription:_r,addVertex:xCe,lookUpDomId:S5,addLink:wCe,updateLinkInterpolate:TCe,updateLink:kCe,addClass:ECe,setDirection:CCe,setClass:u9,setTooltip:SCe,getTooltip:GX,setClickEvent:LCe,setLink:_Ce,bindFunctions:DCe,getDirection:h9,getVertices:$X,getEdges:VX,getClasses:RCe,clear:NCe,setGen:MCe,defaultStyle:ICe,addSubGraph:OCe,getDepthFirstPos:BCe,indexNodes:FCe,getSubGraphs:WX,destructLink:UCe,lex:HCe,exists:qX,makeUniq:XX,setDiagramTitle:nn,getDiagramTitle:Xr}});var KCe,jX,KX=R(()=>{"use strict";KCe=o(t=>{let e=new Set;for(let r of t)switch(r){case"x":e.add("right"),e.add("left");break;case"y":e.add("up"),e.add("down");break;default:e.add(r);break}return e},"expandAndDeduplicateDirections"),jX=o((t,e,r)=>{let n=KCe(t),i=2,a=e.height+2*r.padding,s=a/i,l=e.width+2*s+r.padding,u=r.padding/2;return n.has("right")&&n.has("left")&&n.has("up")&&n.has("down")?[{x:0,y:0},{x:s,y:0},{x:l/2,y:2*u},{x:l-s,y:0},{x:l,y:0},{x:l,y:-a/3},{x:l+2*u,y:-a/2},{x:l,y:-2*a/3},{x:l,y:-a},{x:l-s,y:-a},{x:l/2,y:-a-2*u},{x:s,y:-a},{x:0,y:-a},{x:0,y:-2*a/3},{x:-2*u,y:-a/2},{x:0,y:-a/3}]:n.has("right")&&n.has("left")&&n.has("up")?[{x:s,y:0},{x:l-s,y:0},{x:l,y:-a/2},{x:l-s,y:-a},{x:s,y:-a},{x:0,y:-a/2}]:n.has("right")&&n.has("left")&&n.has("down")?[{x:0,y:0},{x:s,y:-a},{x:l-s,y:-a},{x:l,y:0}]:n.has("right")&&n.has("up")&&n.has("down")?[{x:0,y:0},{x:l,y:-s},{x:l,y:-a+s},{x:0,y:-a}]:n.has("left")&&n.has("up")&&n.has("down")?[{x:l,y:0},{x:0,y:-s},{x:0,y:-a+s},{x:l,y:-a}]:n.has("right")&&n.has("left")?[{x:s,y:0},{x:s,y:-u},{x:l-s,y:-u},{x:l-s,y:0},{x:l,y:-a/2},{x:l-s,y:-a},{x:l-s,y:-a+u},{x:s,y:-a+u},{x:s,y:-a},{x:0,y:-a/2}]:n.has("up")&&n.has("down")?[{x:l/2,y:0},{x:0,y:-u},{x:s,y:-u},{x:s,y:-a+u},{x:0,y:-a+u},{x:l/2,y:-a},{x:l,y:-a+u},{x:l-s,y:-a+u},{x:l-s,y:-u},{x:l,y:-u}]:n.has("right")&&n.has("up")?[{x:0,y:0},{x:l,y:-s},{x:0,y:-a}]:n.has("right")&&n.has("down")?[{x:0,y:0},{x:l,y:0},{x:0,y:-a}]:n.has("left")&&n.has("up")?[{x:l,y:0},{x:0,y:-s},{x:l,y:-a}]:n.has("left")&&n.has("down")?[{x:l,y:0},{x:0,y:0},{x:l,y:-a}]:n.has("right")?[{x:s,y:-u},{x:s,y:-u},{x:l-s,y:-u},{x:l-s,y:0},{x:l,y:-a/2},{x:l-s,y:-a},{x:l-s,y:-a+u},{x:s,y:-a+u},{x:s,y:-a+u}]:n.has("left")?[{x:s,y:0},{x:s,y:-u},{x:l-s,y:-u},{x:l-s,y:-a+u},{x:s,y:-a+u},{x:s,y:-a},{x:0,y:-a/2}]:n.has("up")?[{x:s,y:-u},{x:s,y:-a+u},{x:0,y:-a+u},{x:l/2,y:-a},{x:l,y:-a+u},{x:l-s,y:-a+u},{x:l-s,y:-u}]:n.has("down")?[{x:l/2,y:0},{x:0,y:-u},{x:s,y:-u},{x:s,y:-a+u},{x:l-s,y:-a+u},{x:l-s,y:-u},{x:l,y:-u}]:[{x:0,y:0}]},"getArrowPoints")});function m9(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}function rj(t){Sd=t}function ro(t,e){if(e){if(nj.test(t))return t.replace(QCe,QX)}else if(ij.test(t))return t.replace(ZCe,QX);return t}function t7e(t){return t.replace(e7e,(e,r)=>(r=r.toLowerCase(),r==="colon"?":":r.charAt(0)==="#"?r.charAt(1)==="x"?String.fromCharCode(parseInt(r.substring(2),16)):String.fromCharCode(+r.substring(1)):""))}function ln(t,e){let r=typeof t=="string"?t:t.source;e=e||"";let n={replace:o((i,a)=>{let s=typeof a=="string"?a:a.source;return s=s.replace(r7e,"$1"),r=r.replace(i,s),n},"replace"),getRegex:o(()=>new RegExp(r,e),"getRegex")};return n}function ZX(t){try{t=encodeURI(t).replace(/%25/g,"%")}catch{return null}return t}function JX(t,e){let r=t.replace(/\|/g,(a,s,l)=>{let u=!1,h=s;for(;--h>=0&&l[h]==="\\";)u=!u;return u?"|":" |"}),n=r.split(/ \|/),i=0;if(n[0].trim()||n.shift(),n.length>0&&!n[n.length-1].trim()&&n.pop(),e)if(n.length>e)n.splice(e);else for(;n.length{let a=i.match(/^\s+/);if(a===null)return i;let[s]=a;return s.length>=n.length?i.slice(n.length):i}).join(` +`)}function jr(t,e){return Cd.parse(t,e)}var Sd,nj,QCe,ij,ZCe,JCe,QX,e7e,r7e,gv,hm,a7e,s7e,o7e,vv,l7e,aj,sj,g9,c7e,y9,u7e,h7e,D5,v9,f7e,oj,d7e,x9,tj,p7e,m7e,lj,g7e,cj,y7e,xv,v7e,x7e,b7e,w7e,T7e,k7e,E7e,C7e,S7e,L5,A7e,uj,hj,_7e,b9,L7e,d9,D7e,_5,mv,Su,fm,yv,Au,um,p9,Cd,mkt,gkt,ykt,vkt,xkt,bkt,wkt,fj=R(()=>{"use strict";o(m9,"_getDefaults");Sd=m9();o(rj,"changeDefaults");nj=/[&<>"']/,QCe=new RegExp(nj.source,"g"),ij=/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,ZCe=new RegExp(ij.source,"g"),JCe={"&":"&","<":"<",">":">",'"':""","'":"'"},QX=o(t=>JCe[t],"getEscapeReplacement");o(ro,"escape$1");e7e=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;o(t7e,"unescape");r7e=/(^|[^\[])\^/g;o(ln,"edit");o(ZX,"cleanUrl");gv={exec:o(()=>null,"exec")};o(JX,"splitCells");o(pv,"rtrim");o(n7e,"findClosingBracket");o(ej,"outputLink");o(i7e,"indentCodeCompensation");hm=class{static{o(this,"_Tokenizer")}options;rules;lexer;constructor(e){this.options=e||Sd}space(e){let r=this.rules.block.newline.exec(e);if(r&&r[0].length>0)return{type:"space",raw:r[0]}}code(e){let r=this.rules.block.code.exec(e);if(r){let n=r[0].replace(/^ {1,4}/gm,"");return{type:"code",raw:r[0],codeBlockStyle:"indented",text:this.options.pedantic?n:pv(n,` +`)}}}fences(e){let r=this.rules.block.fences.exec(e);if(r){let n=r[0],i=i7e(n,r[3]||"");return{type:"code",raw:n,lang:r[2]?r[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):r[2],text:i}}}heading(e){let r=this.rules.block.heading.exec(e);if(r){let n=r[2].trim();if(/#$/.test(n)){let i=pv(n,"#");(this.options.pedantic||!i||/ $/.test(i))&&(n=i.trim())}return{type:"heading",raw:r[0],depth:r[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(e){let r=this.rules.block.hr.exec(e);if(r)return{type:"hr",raw:pv(r[0],` +`)}}blockquote(e){let r=this.rules.block.blockquote.exec(e);if(r){let n=pv(r[0],` +`).split(` +`),i="",a="",s=[];for(;n.length>0;){let l=!1,u=[],h;for(h=0;h/.test(n[h]))u.push(n[h]),l=!0;else if(!l)u.push(n[h]);else break;n=n.slice(h);let f=u.join(` +`),d=f.replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,` + $1`).replace(/^ {0,3}>[ \t]?/gm,"");i=i?`${i} +${f}`:f,a=a?`${a} +${d}`:d;let p=this.lexer.state.top;if(this.lexer.state.top=!0,this.lexer.blockTokens(d,s,!0),this.lexer.state.top=p,n.length===0)break;let m=s[s.length-1];if(m?.type==="code")break;if(m?.type==="blockquote"){let g=m,y=g.raw+` +`+n.join(` +`),v=this.blockquote(y);s[s.length-1]=v,i=i.substring(0,i.length-g.raw.length)+v.raw,a=a.substring(0,a.length-g.text.length)+v.text;break}else if(m?.type==="list"){let g=m,y=g.raw+` +`+n.join(` +`),v=this.list(y);s[s.length-1]=v,i=i.substring(0,i.length-m.raw.length)+v.raw,a=a.substring(0,a.length-g.raw.length)+v.raw,n=y.substring(s[s.length-1].raw.length).split(` +`);continue}}return{type:"blockquote",raw:i,tokens:s,text:a}}}list(e){let r=this.rules.block.list.exec(e);if(r){let n=r[1].trim(),i=n.length>1,a={type:"list",raw:"",ordered:i,start:i?+n.slice(0,-1):"",loose:!1,items:[]};n=i?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=i?n:"[*+-]");let s=new RegExp(`^( {0,3}${n})((?:[ ][^\\n]*)?(?:\\n|$))`),l=!1;for(;e;){let u=!1,h="",f="";if(!(r=s.exec(e))||this.rules.block.hr.test(e))break;h=r[0],e=e.substring(h.length);let d=r[2].split(` +`,1)[0].replace(/^\t+/,x=>" ".repeat(3*x.length)),p=e.split(` +`,1)[0],m=!d.trim(),g=0;if(this.options.pedantic?(g=2,f=d.trimStart()):m?g=r[1].length+1:(g=r[2].search(/[^ ]/),g=g>4?1:g,f=d.slice(g),g+=r[1].length),m&&/^ *$/.test(p)&&(h+=p+` +`,e=e.substring(p.length+1),u=!0),!u){let x=new RegExp(`^ {0,${Math.min(3,g-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`),b=new RegExp(`^ {0,${Math.min(3,g-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),w=new RegExp(`^ {0,${Math.min(3,g-1)}}(?:\`\`\`|~~~)`),S=new RegExp(`^ {0,${Math.min(3,g-1)}}#`);for(;e;){let T=e.split(` +`,1)[0];if(p=T,this.options.pedantic&&(p=p.replace(/^ {1,4}(?=( {4})*[^ ])/g," ")),w.test(p)||S.test(p)||x.test(p)||b.test(e))break;if(p.search(/[^ ]/)>=g||!p.trim())f+=` +`+p.slice(g);else{if(m||d.search(/[^ ]/)>=4||w.test(d)||S.test(d)||b.test(d))break;f+=` +`+p}!m&&!p.trim()&&(m=!0),h+=T+` +`,e=e.substring(T.length+1),d=p.slice(g)}}a.loose||(l?a.loose=!0:/\n *\n *$/.test(h)&&(l=!0));let y=null,v;this.options.gfm&&(y=/^\[[ xX]\] /.exec(f),y&&(v=y[0]!=="[ ] ",f=f.replace(/^\[[ xX]\] +/,""))),a.items.push({type:"list_item",raw:h,task:!!y,checked:v,loose:!1,text:f,tokens:[]}),a.raw+=h}a.items[a.items.length-1].raw=a.items[a.items.length-1].raw.trimEnd(),a.items[a.items.length-1].text=a.items[a.items.length-1].text.trimEnd(),a.raw=a.raw.trimEnd();for(let u=0;ud.type==="space"),f=h.length>0&&h.some(d=>/\n.*\n/.test(d.raw));a.loose=f}if(a.loose)for(let u=0;u$/,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",a=r[3]?r[3].substring(1,r[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):r[3];return{type:"def",tag:n,raw:r[0],href:i,title:a}}}table(e){let r=this.rules.block.table.exec(e);if(!r||!/[:|]/.test(r[2]))return;let n=JX(r[1]),i=r[2].replace(/^\||\| *$/g,"").split("|"),a=r[3]&&r[3].trim()?r[3].replace(/\n[ \t]*$/,"").split(` +`):[],s={type:"table",raw:r[0],header:[],align:[],rows:[]};if(n.length===i.length){for(let l of i)/^ *-+: *$/.test(l)?s.align.push("right"):/^ *:-+: *$/.test(l)?s.align.push("center"):/^ *:-+ *$/.test(l)?s.align.push("left"):s.align.push(null);for(let l=0;l({text:u,tokens:this.lexer.inline(u),header:!1,align:s.align[h]})));return s}}lheading(e){let r=this.rules.block.lheading.exec(e);if(r)return{type:"heading",raw:r[0],depth:r[2].charAt(0)==="="?1:2,text:r[1],tokens:this.lexer.inline(r[1])}}paragraph(e){let r=this.rules.block.paragraph.exec(e);if(r){let n=r[1].charAt(r[1].length-1)===` +`?r[1].slice(0,-1):r[1];return{type:"paragraph",raw:r[0],text:n,tokens:this.lexer.inline(n)}}}text(e){let r=this.rules.block.text.exec(e);if(r)return{type:"text",raw:r[0],text:r[0],tokens:this.lexer.inline(r[0])}}escape(e){let r=this.rules.inline.escape.exec(e);if(r)return{type:"escape",raw:r[0],text:ro(r[1])}}tag(e){let r=this.rules.inline.tag.exec(e);if(r)return!this.lexer.state.inLink&&/^/i.test(r[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(r[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(r[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:r[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:r[0]}}link(e){let r=this.rules.inline.link.exec(e);if(r){let n=r[2].trim();if(!this.options.pedantic&&/^$/.test(n))return;let s=pv(n.slice(0,-1),"\\");if((n.length-s.length)%2===0)return}else{let s=n7e(r[2],"()");if(s>-1){let u=(r[0].indexOf("!")===0?5:4)+r[1].length+s;r[2]=r[2].substring(0,s),r[0]=r[0].substring(0,u).trim(),r[3]=""}}let i=r[2],a="";if(this.options.pedantic){let s=/^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(i);s&&(i=s[1],a=s[3])}else a=r[3]?r[3].slice(1,-1):"";return i=i.trim(),/^$/.test(n)?i=i.slice(1):i=i.slice(1,-1)),ej(r,{href:i&&i.replace(this.rules.inline.anyPunctuation,"$1"),title:a&&a.replace(this.rules.inline.anyPunctuation,"$1")},r[0],this.lexer)}}reflink(e,r){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){let i=(n[2]||n[1]).replace(/\s+/g," "),a=r[i.toLowerCase()];if(!a){let s=n[0].charAt(0);return{type:"text",raw:s,text:s}}return ej(n,a,n[0],this.lexer)}}emStrong(e,r,n=""){let i=this.rules.inline.emStrongLDelim.exec(e);if(!i||i[3]&&n.match(/[\p{L}\p{N}]/u))return;if(!(i[1]||i[2]||"")||!n||this.rules.inline.punctuation.exec(n)){let s=[...i[0]].length-1,l,u,h=s,f=0,d=i[0][0]==="*"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(d.lastIndex=0,r=r.slice(-1*e.length+s);(i=d.exec(r))!=null;){if(l=i[1]||i[2]||i[3]||i[4]||i[5]||i[6],!l)continue;if(u=[...l].length,i[3]||i[4]){h+=u;continue}else if((i[5]||i[6])&&s%3&&!((s+u)%3)){f+=u;continue}if(h-=u,h>0)continue;u=Math.min(u,u+h+f);let p=[...i[0]][0].length,m=e.slice(0,s+i.index+p+u);if(Math.min(s,u)%2){let y=m.slice(1,-1);return{type:"em",raw:m,text:y,tokens:this.lexer.inlineTokens(y)}}let g=m.slice(2,-2);return{type:"strong",raw:m,text:g,tokens:this.lexer.inlineTokens(g)}}}}codespan(e){let r=this.rules.inline.code.exec(e);if(r){let n=r[2].replace(/\n/g," "),i=/[^ ]/.test(n),a=/^ /.test(n)&&/ $/.test(n);return i&&a&&(n=n.substring(1,n.length-1)),n=ro(n,!0),{type:"codespan",raw:r[0],text:n}}}br(e){let r=this.rules.inline.br.exec(e);if(r)return{type:"br",raw:r[0]}}del(e){let r=this.rules.inline.del.exec(e);if(r)return{type:"del",raw:r[0],text:r[2],tokens:this.lexer.inlineTokens(r[2])}}autolink(e){let r=this.rules.inline.autolink.exec(e);if(r){let n,i;return r[2]==="@"?(n=ro(r[1]),i="mailto:"+n):(n=ro(r[1]),i=n),{type:"link",raw:r[0],text:n,href:i,tokens:[{type:"text",raw:n,text:n}]}}}url(e){let r;if(r=this.rules.inline.url.exec(e)){let n,i;if(r[2]==="@")n=ro(r[0]),i="mailto:"+n;else{let a;do a=r[0],r[0]=this.rules.inline._backpedal.exec(r[0])?.[0]??"";while(a!==r[0]);n=ro(r[0]),r[1]==="www."?i="http://"+r[0]:i=r[0]}return{type:"link",raw:r[0],text:n,href:i,tokens:[{type:"text",raw:n,text:n}]}}}inlineText(e){let r=this.rules.inline.text.exec(e);if(r){let n;return this.lexer.state.inRawBlock?n=r[0]:n=ro(r[0]),{type:"text",raw:r[0],text:n}}}},a7e=/^(?: *(?:\n|$))+/,s7e=/^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,o7e=/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,vv=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,l7e=/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,aj=/(?:[*+-]|\d{1,9}[.)])/,sj=ln(/^(?!bull |blockCode|fences|blockquote|heading|html)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html))+?)\n {0,3}(=+|-+) *(?:\n+|$)/).replace(/bull/g,aj).replace(/blockCode/g,/ {4}/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).getRegex(),g9=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,c7e=/^[^\n]+/,y9=/(?!\s*\])(?:\\.|[^\[\]\\])+/,u7e=ln(/^ {0,3}\[(label)\]: *(?:\n *)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/).replace("label",y9).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),h7e=ln(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,aj).getRegex(),D5="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",v9=/|$))/,f7e=ln("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n *)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$))","i").replace("comment",v9).replace("tag",D5).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),oj=ln(g9).replace("hr",vv).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",D5).getRegex(),d7e=ln(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",oj).getRegex(),x9={blockquote:d7e,code:s7e,def:u7e,fences:o7e,heading:l7e,hr:vv,html:f7e,lheading:sj,list:h7e,newline:a7e,paragraph:oj,table:gv,text:c7e},tj=ln("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",vv).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",D5).getRegex(),p7e={...x9,table:tj,paragraph:ln(g9).replace("hr",vv).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",tj).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",D5).getRegex()},m7e={...x9,html:ln(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment",v9).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:gv,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:ln(g9).replace("hr",vv).replace("heading",` *#{1,6} *[^ +]`).replace("lheading",sj).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},lj=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,g7e=/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,cj=/^( {2,}|\\)\n(?!\s*$)/,y7e=/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\]*?>/g,b7e=ln(/^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/,"u").replace(/punct/g,xv).getRegex(),w7e=ln("^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)[punct](\\*+)(?=[\\s]|$)|[^punct\\s](\\*+)(?!\\*)(?=[punct\\s]|$)|(?!\\*)[punct\\s](\\*+)(?=[^punct\\s])|[\\s](\\*+)(?!\\*)(?=[punct])|(?!\\*)[punct](\\*+)(?!\\*)(?=[punct])|[^punct\\s](\\*+)(?=[^punct\\s])","gu").replace(/punct/g,xv).getRegex(),T7e=ln("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)[punct](_+)(?=[\\s]|$)|[^punct\\s](_+)(?!_)(?=[punct\\s]|$)|(?!_)[punct\\s](_+)(?=[^punct\\s])|[\\s](_+)(?!_)(?=[punct])|(?!_)[punct](_+)(?!_)(?=[punct])","gu").replace(/punct/g,xv).getRegex(),k7e=ln(/\\([punct])/,"gu").replace(/punct/g,xv).getRegex(),E7e=ln(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),C7e=ln(v9).replace("(?:-->|$)","-->").getRegex(),S7e=ln("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",C7e).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),L5=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,A7e=ln(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/).replace("label",L5).replace("href",/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),uj=ln(/^!?\[(label)\]\[(ref)\]/).replace("label",L5).replace("ref",y9).getRegex(),hj=ln(/^!?\[(ref)\](?:\[\])?/).replace("ref",y9).getRegex(),_7e=ln("reflink|nolink(?!\\()","g").replace("reflink",uj).replace("nolink",hj).getRegex(),b9={_backpedal:gv,anyPunctuation:k7e,autolink:E7e,blockSkip:x7e,br:cj,code:g7e,del:gv,emStrongLDelim:b7e,emStrongRDelimAst:w7e,emStrongRDelimUnd:T7e,escape:lj,link:A7e,nolink:hj,punctuation:v7e,reflink:uj,reflinkSearch:_7e,tag:S7e,text:y7e,url:gv},L7e={...b9,link:ln(/^!?\[(label)\]\((.*?)\)/).replace("label",L5).getRegex(),reflink:ln(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",L5).getRegex()},d9={...b9,escape:ln(lj).replace("])","~|])").getRegex(),url:ln(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,"i").replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\u+" ".repeat(h.length));let i,a,s;for(;e;)if(!(this.options.extensions&&this.options.extensions.block&&this.options.extensions.block.some(l=>(i=l.call({lexer:this},e,r))?(e=e.substring(i.raw.length),r.push(i),!0):!1))){if(i=this.tokenizer.space(e)){e=e.substring(i.raw.length),i.raw.length===1&&r.length>0?r[r.length-1].raw+=` +`:r.push(i);continue}if(i=this.tokenizer.code(e)){e=e.substring(i.raw.length),a=r[r.length-1],a&&(a.type==="paragraph"||a.type==="text")?(a.raw+=` +`+i.raw,a.text+=` +`+i.text,this.inlineQueue[this.inlineQueue.length-1].src=a.text):r.push(i);continue}if(i=this.tokenizer.fences(e)){e=e.substring(i.raw.length),r.push(i);continue}if(i=this.tokenizer.heading(e)){e=e.substring(i.raw.length),r.push(i);continue}if(i=this.tokenizer.hr(e)){e=e.substring(i.raw.length),r.push(i);continue}if(i=this.tokenizer.blockquote(e)){e=e.substring(i.raw.length),r.push(i);continue}if(i=this.tokenizer.list(e)){e=e.substring(i.raw.length),r.push(i);continue}if(i=this.tokenizer.html(e)){e=e.substring(i.raw.length),r.push(i);continue}if(i=this.tokenizer.def(e)){e=e.substring(i.raw.length),a=r[r.length-1],a&&(a.type==="paragraph"||a.type==="text")?(a.raw+=` +`+i.raw,a.text+=` +`+i.raw,this.inlineQueue[this.inlineQueue.length-1].src=a.text):this.tokens.links[i.tag]||(this.tokens.links[i.tag]={href:i.href,title:i.title});continue}if(i=this.tokenizer.table(e)){e=e.substring(i.raw.length),r.push(i);continue}if(i=this.tokenizer.lheading(e)){e=e.substring(i.raw.length),r.push(i);continue}if(s=e,this.options.extensions&&this.options.extensions.startBlock){let l=1/0,u=e.slice(1),h;this.options.extensions.startBlock.forEach(f=>{h=f.call({lexer:this},u),typeof h=="number"&&h>=0&&(l=Math.min(l,h))}),l<1/0&&l>=0&&(s=e.substring(0,l+1))}if(this.state.top&&(i=this.tokenizer.paragraph(s))){a=r[r.length-1],n&&a?.type==="paragraph"?(a.raw+=` +`+i.raw,a.text+=` +`+i.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=a.text):r.push(i),n=s.length!==e.length,e=e.substring(i.raw.length);continue}if(i=this.tokenizer.text(e)){e=e.substring(i.raw.length),a=r[r.length-1],a&&a.type==="text"?(a.raw+=` +`+i.raw,a.text+=` +`+i.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=a.text):r.push(i);continue}if(e){let l="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(l);break}else throw new Error(l)}}return this.state.top=!0,r}inline(e,r=[]){return this.inlineQueue.push({src:e,tokens:r}),r}inlineTokens(e,r=[]){let n,i,a,s=e,l,u,h;if(this.tokens.links){let f=Object.keys(this.tokens.links);if(f.length>0)for(;(l=this.tokenizer.rules.inline.reflinkSearch.exec(s))!=null;)f.includes(l[0].slice(l[0].lastIndexOf("[")+1,-1))&&(s=s.slice(0,l.index)+"["+"a".repeat(l[0].length-2)+"]"+s.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(l=this.tokenizer.rules.inline.blockSkip.exec(s))!=null;)s=s.slice(0,l.index)+"["+"a".repeat(l[0].length-2)+"]"+s.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);for(;(l=this.tokenizer.rules.inline.anyPunctuation.exec(s))!=null;)s=s.slice(0,l.index)+"++"+s.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);for(;e;)if(u||(h=""),u=!1,!(this.options.extensions&&this.options.extensions.inline&&this.options.extensions.inline.some(f=>(n=f.call({lexer:this},e,r))?(e=e.substring(n.raw.length),r.push(n),!0):!1))){if(n=this.tokenizer.escape(e)){e=e.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.tag(e)){e=e.substring(n.raw.length),i=r[r.length-1],i&&n.type==="text"&&i.type==="text"?(i.raw+=n.raw,i.text+=n.text):r.push(n);continue}if(n=this.tokenizer.link(e)){e=e.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(n.raw.length),i=r[r.length-1],i&&n.type==="text"&&i.type==="text"?(i.raw+=n.raw,i.text+=n.text):r.push(n);continue}if(n=this.tokenizer.emStrong(e,s,h)){e=e.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.codespan(e)){e=e.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.br(e)){e=e.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.del(e)){e=e.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.autolink(e)){e=e.substring(n.raw.length),r.push(n);continue}if(!this.state.inLink&&(n=this.tokenizer.url(e))){e=e.substring(n.raw.length),r.push(n);continue}if(a=e,this.options.extensions&&this.options.extensions.startInline){let f=1/0,d=e.slice(1),p;this.options.extensions.startInline.forEach(m=>{p=m.call({lexer:this},d),typeof p=="number"&&p>=0&&(f=Math.min(f,p))}),f<1/0&&f>=0&&(a=e.substring(0,f+1))}if(n=this.tokenizer.inlineText(a)){e=e.substring(n.raw.length),n.raw.slice(-1)!=="_"&&(h=n.raw.slice(-1)),u=!0,i=r[r.length-1],i&&i.type==="text"?(i.raw+=n.raw,i.text+=n.text):r.push(n);continue}if(e){let f="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(f);break}else throw new Error(f)}}return r}},fm=class{static{o(this,"_Renderer")}options;parser;constructor(e){this.options=e||Sd}space(e){return""}code({text:e,lang:r,escaped:n}){let i=(r||"").match(/^\S*/)?.[0],a=e.replace(/\n$/,"")+` +`;return i?'
'+(n?a:ro(a,!0))+`
+`:"
"+(n?a:ro(a,!0))+`
+`}blockquote({tokens:e}){return`
+${this.parser.parse(e)}
+`}html({text:e}){return e}heading({tokens:e,depth:r}){return`${this.parser.parseInline(e)} +`}hr(e){return`
+`}list(e){let r=e.ordered,n=e.start,i="";for(let l=0;l +`+i+" +`}listitem(e){let r="";if(e.task){let n=this.checkbox({checked:!!e.checked});e.loose?e.tokens.length>0&&e.tokens[0].type==="paragraph"?(e.tokens[0].text=n+" "+e.tokens[0].text,e.tokens[0].tokens&&e.tokens[0].tokens.length>0&&e.tokens[0].tokens[0].type==="text"&&(e.tokens[0].tokens[0].text=n+" "+e.tokens[0].tokens[0].text)):e.tokens.unshift({type:"text",raw:n+" ",text:n+" "}):r+=n+" "}return r+=this.parser.parse(e.tokens,!!e.loose),`
  • ${r}
  • +`}checkbox({checked:e}){return"'}paragraph({tokens:e}){return`

    ${this.parser.parseInline(e)}

    +`}table(e){let r="",n="";for(let a=0;a${i}`),` + +`+r+` +`+i+`
    +`}tablerow({text:e}){return` +${e} +`}tablecell(e){let r=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+r+` +`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${e}`}br(e){return"
    "}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:r,tokens:n}){let i=this.parser.parseInline(n),a=ZX(e);if(a===null)return i;e=a;let s='
    ",s}image({href:e,title:r,text:n}){let i=ZX(e);if(i===null)return n;e=i;let a=`${n}{let l=a[s].flat(1/0);n=n.concat(this.walkTokens(l,r))}):a.tokens&&(n=n.concat(this.walkTokens(a.tokens,r)))}}return n}use(...e){let r=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(n=>{let i={...n};if(i.async=this.defaults.async||i.async||!1,n.extensions&&(n.extensions.forEach(a=>{if(!a.name)throw new Error("extension name required");if("renderer"in a){let s=r.renderers[a.name];s?r.renderers[a.name]=function(...l){let u=a.renderer.apply(this,l);return u===!1&&(u=s.apply(this,l)),u}:r.renderers[a.name]=a.renderer}if("tokenizer"in a){if(!a.level||a.level!=="block"&&a.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");let s=r[a.level];s?s.unshift(a.tokenizer):r[a.level]=[a.tokenizer],a.start&&(a.level==="block"?r.startBlock?r.startBlock.push(a.start):r.startBlock=[a.start]:a.level==="inline"&&(r.startInline?r.startInline.push(a.start):r.startInline=[a.start]))}"childTokens"in a&&a.childTokens&&(r.childTokens[a.name]=a.childTokens)}),i.extensions=r),n.renderer){let a=this.defaults.renderer||new fm(this.defaults);for(let s in n.renderer){if(!(s in a))throw new Error(`renderer '${s}' does not exist`);if(["options","parser"].includes(s))continue;let l=s,u=n.renderer[l];n.useNewRenderer||(u=this.#t(u,l,a));let h=a[l];a[l]=(...f)=>{let d=u.apply(a,f);return d===!1&&(d=h.apply(a,f)),d||""}}i.renderer=a}if(n.tokenizer){let a=this.defaults.tokenizer||new hm(this.defaults);for(let s in n.tokenizer){if(!(s in a))throw new Error(`tokenizer '${s}' does not exist`);if(["options","rules","lexer"].includes(s))continue;let l=s,u=n.tokenizer[l],h=a[l];a[l]=(...f)=>{let d=u.apply(a,f);return d===!1&&(d=h.apply(a,f)),d}}i.tokenizer=a}if(n.hooks){let a=this.defaults.hooks||new um;for(let s in n.hooks){if(!(s in a))throw new Error(`hook '${s}' does not exist`);if(s==="options")continue;let l=s,u=n.hooks[l],h=a[l];um.passThroughHooks.has(s)?a[l]=f=>{if(this.defaults.async)return Promise.resolve(u.call(a,f)).then(p=>h.call(a,p));let d=u.call(a,f);return h.call(a,d)}:a[l]=(...f)=>{let d=u.apply(a,f);return d===!1&&(d=h.apply(a,f)),d}}i.hooks=a}if(n.walkTokens){let a=this.defaults.walkTokens,s=n.walkTokens;i.walkTokens=function(l){let u=[];return u.push(s.call(this,l)),a&&(u=u.concat(a.call(this,l))),u}}this.defaults={...this.defaults,...i}}),this}#t(e,r,n){switch(r){case"heading":return function(i){return!i.type||i.type!==r?e.apply(this,arguments):e.call(this,n.parser.parseInline(i.tokens),i.depth,t7e(n.parser.parseInline(i.tokens,n.parser.textRenderer)))};case"code":return function(i){return!i.type||i.type!==r?e.apply(this,arguments):e.call(this,i.text,i.lang,!!i.escaped)};case"table":return function(i){if(!i.type||i.type!==r)return e.apply(this,arguments);let a="",s="";for(let u=0;u0&&f.tokens[0].type==="paragraph"?(f.tokens[0].text=g+" "+f.tokens[0].text,f.tokens[0].tokens&&f.tokens[0].tokens.length>0&&f.tokens[0].tokens[0].type==="text"&&(f.tokens[0].tokens[0].text=g+" "+f.tokens[0].tokens[0].text)):f.tokens.unshift({type:"text",text:g+" "}):m+=g+" "}m+=this.parser.parse(f.tokens,l),u+=this.listitem({type:"list_item",raw:m,text:m,task:p,checked:!!d,loose:l,tokens:f.tokens})}return e.call(this,u,a,s)};case"html":return function(i){return!i.type||i.type!==r?e.apply(this,arguments):e.call(this,i.text,i.block)};case"paragraph":return function(i){return!i.type||i.type!==r?e.apply(this,arguments):e.call(this,this.parser.parseInline(i.tokens))};case"escape":return function(i){return!i.type||i.type!==r?e.apply(this,arguments):e.call(this,i.text)};case"link":return function(i){return!i.type||i.type!==r?e.apply(this,arguments):e.call(this,i.href,i.title,this.parser.parseInline(i.tokens))};case"image":return function(i){return!i.type||i.type!==r?e.apply(this,arguments):e.call(this,i.href,i.title,i.text)};case"strong":return function(i){return!i.type||i.type!==r?e.apply(this,arguments):e.call(this,this.parser.parseInline(i.tokens))};case"em":return function(i){return!i.type||i.type!==r?e.apply(this,arguments):e.call(this,this.parser.parseInline(i.tokens))};case"codespan":return function(i){return!i.type||i.type!==r?e.apply(this,arguments):e.call(this,i.text)};case"del":return function(i){return!i.type||i.type!==r?e.apply(this,arguments):e.call(this,this.parser.parseInline(i.tokens))};case"text":return function(i){return!i.type||i.type!==r?e.apply(this,arguments):e.call(this,i.text)}}return e}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,r){return Su.lex(e,r??this.defaults)}parser(e,r){return Au.parse(e,r??this.defaults)}#e(e,r){return(n,i)=>{let a={...i},s={...this.defaults,...a};this.defaults.async===!0&&a.async===!1&&(s.silent||console.warn("marked(): The async option was set to true by an extension. The async: false option sent to parse will be ignored."),s.async=!0);let l=this.#r(!!s.silent,!!s.async);if(typeof n>"u"||n===null)return l(new Error("marked(): input parameter is undefined or null"));if(typeof n!="string")return l(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(n)+", string expected"));if(s.hooks&&(s.hooks.options=s),s.async)return Promise.resolve(s.hooks?s.hooks.preprocess(n):n).then(u=>e(u,s)).then(u=>s.hooks?s.hooks.processAllTokens(u):u).then(u=>s.walkTokens?Promise.all(this.walkTokens(u,s.walkTokens)).then(()=>u):u).then(u=>r(u,s)).then(u=>s.hooks?s.hooks.postprocess(u):u).catch(l);try{s.hooks&&(n=s.hooks.preprocess(n));let u=e(n,s);s.hooks&&(u=s.hooks.processAllTokens(u)),s.walkTokens&&this.walkTokens(u,s.walkTokens);let h=r(u,s);return s.hooks&&(h=s.hooks.postprocess(h)),h}catch(u){return l(u)}}}#r(e,r){return n=>{if(n.message+=` +Please report this to https://github.com/markedjs/marked.`,e){let i="

    An error occurred:

    "+ro(n.message+"",!0)+"
    ";return r?Promise.resolve(i):i}if(r)return Promise.reject(n);throw n}}},Cd=new p9;o(jr,"marked");jr.options=jr.setOptions=function(t){return Cd.setOptions(t),jr.defaults=Cd.defaults,rj(jr.defaults),jr};jr.getDefaults=m9;jr.defaults=Sd;jr.use=function(...t){return Cd.use(...t),jr.defaults=Cd.defaults,rj(jr.defaults),jr};jr.walkTokens=function(t,e){return Cd.walkTokens(t,e)};jr.parseInline=Cd.parseInline;jr.Parser=Au;jr.parser=Au.parse;jr.Renderer=fm;jr.TextRenderer=yv;jr.Lexer=Su;jr.lexer=Su.lex;jr.Tokenizer=hm;jr.Hooks=um;jr.parse=jr;mkt=jr.options,gkt=jr.setOptions,ykt=jr.use,vkt=jr.walkTokens,xkt=jr.parseInline,bkt=Au.parse,wkt=Su.lex});function R7e(t,{markdownAutoWrap:e}){let n=t.replace(//g,` +`).replace(/\n{2,}/g,` +`),i=Gb(n);return e===!1?i.replace(/ /g," "):i}function dj(t,e={}){let r=R7e(t,e),n=jr.lexer(r),i=[[]],a=0;function s(l,u="normal"){l.type==="text"?l.text.split(` +`).forEach((f,d)=>{d!==0&&(a++,i.push([])),f.split(" ").forEach(p=>{p&&i[a].push({content:p,type:u})})}):l.type==="strong"||l.type==="em"?l.tokens.forEach(h=>{s(h,l.type)}):l.type==="html"&&i[a].push({content:l.text,type:"normal"})}return o(s,"processNode"),n.forEach(l=>{l.type==="paragraph"?l.tokens?.forEach(u=>{s(u)}):l.type==="html"&&i[a].push({content:l.text,type:"normal"})}),i}function pj(t,{markdownAutoWrap:e}={}){let r=jr.lexer(t);function n(i){return i.type==="text"?e===!1?i.text.replace(/\n */g,"
    ").replace(/ /g," "):i.text.replace(/\n */g,"
    "):i.type==="strong"?`${i.tokens?.map(n).join("")}`:i.type==="em"?`${i.tokens?.map(n).join("")}`:i.type==="paragraph"?`

    ${i.tokens?.map(n).join("")}

    `:i.type==="space"?"":i.type==="html"?`${i.text}`:`Unsupported markdown: ${i.type}`}return o(n,"output"),r.map(n).join("")}var mj=R(()=>{"use strict";fj();zC();o(R7e,"preprocessMarkdown");o(dj,"markdownToLines");o(pj,"markdownToHTML")});function N7e(t){return Intl.Segmenter?[...new Intl.Segmenter().segment(t)].map(e=>e.segment):[...t]}function M7e(t,e){let r=N7e(e.content);return gj(t,[],r,e.type)}function gj(t,e,r,n){if(r.length===0)return[{content:e.join(""),type:n},{content:"",type:n}];let[i,...a]=r,s=[...e,i];return t([{content:s.join(""),type:n}])?gj(t,s,a,n):(e.length===0&&i&&(e.push(i),r.shift()),[{content:e.join(""),type:n},{content:r.join(""),type:n}])}function yj(t,e){if(t.some(({content:r})=>r.includes(` +`)))throw new Error("splitLineToFitWidth does not support newlines in the line");return w9(t,e)}function w9(t,e,r=[],n=[]){if(t.length===0)return n.length>0&&r.push(n),r.length>0?r:[];let i="";t[0].content===" "&&(i=" ",t.shift());let a=t.shift()??{content:" ",type:"normal"},s=[...n];if(i!==""&&s.push({content:i,type:"normal"}),s.push(a),e(s))return w9(t,e,r,s);if(n.length>0)r.push(n),t.unshift(a);else if(a.content){let[l,u]=M7e(e,a);r.push([l]),u.content&&t.unshift(u)}return w9(t,e,r)}var vj=R(()=>{"use strict";o(N7e,"splitTextToChars");o(M7e,"splitWordToFitWidth");o(gj,"splitWordToFitWidthRecursion");o(yj,"splitLineToFitWidth");o(w9,"splitLineToFitWidthRecursion")});function xj(t,e){e&&t.attr("style",e)}async function I7e(t,e,r,n,i=!1){let a=t.append("foreignObject");a.attr("width",`${10*r}px`),a.attr("height",`${10*r}px`);let s=a.append("xhtml:div"),l=e.label;e.label&&Ni(e.label)&&(l=await yh(e.label.replace(We.lineBreakRegex,` +`),de()));let u=e.isNode?"nodeLabel":"edgeLabel",h=s.append("span");h.html(l),xj(h,e.labelStyle),h.attr("class",`${u} ${n}`),xj(s,e.labelStyle),s.style("display","table-cell"),s.style("white-space","nowrap"),s.style("line-height","1.5"),s.style("max-width",r+"px"),s.style("text-align","center"),s.attr("xmlns","http://www.w3.org/1999/xhtml"),i&&s.attr("class","labelBkg");let f=s.node().getBoundingClientRect();return f.width===r&&(s.style("display","table"),s.style("white-space","break-spaces"),s.style("width",r+"px"),f=s.node().getBoundingClientRect()),a.node()}function T9(t,e,r){return t.append("tspan").attr("class","text-outer-tspan").attr("x",0).attr("y",e*r-.1+"em").attr("dy",r+"em")}function O7e(t,e,r){let n=t.append("text"),i=T9(n,1,e);k9(i,r);let a=i.node().getComputedTextLength();return n.remove(),a}function bj(t,e,r){let n=t.append("text"),i=T9(n,1,e);k9(i,[{content:r,type:"normal"}]);let a=i.node()?.getBoundingClientRect();return a&&n.remove(),a}function P7e(t,e,r,n=!1){let a=e.append("g"),s=a.insert("rect").attr("class","background").attr("style","stroke: none"),l=a.append("text").attr("y","-10.1"),u=0;for(let h of r){let f=o(p=>O7e(a,1.1,p)<=t,"checkWidth"),d=f(h)?[h]:yj(h,f);for(let p of d){let m=T9(l,u,1.1);k9(m,p),u++}}if(n){let h=l.node().getBBox(),f=2;return s.attr("x",-f).attr("y",-f).attr("width",h.width+2*f).attr("height",h.height+2*f),a.node()}else return l.node()}function k9(t,e){t.text(""),e.forEach((r,n)=>{let i=t.append("tspan").attr("font-style",r.type==="em"?"italic":"normal").attr("class","text-inner-tspan").attr("font-weight",r.type==="strong"?"bold":"normal");n===0?i.text(r.content):i.text(" "+r.content)})}function E9(t){return t.replace(/fa[bklrs]?:fa-[\w-]+/g,e=>``)}var ta,Al=R(()=>{"use strict";_t();rr();Zt();ut();mj();xr();vj();o(xj,"applyStyle");o(I7e,"addHtmlSpan");o(T9,"createTspan");o(O7e,"computeWidthOfText");o(bj,"computeDimensionOfText");o(P7e,"createFormattedText");o(k9,"updateTextContentAndStyles");o(E9,"replaceIconSubstring");ta=o(async(t,e="",{style:r="",isTitle:n=!1,classes:i="",useHtmlLabels:a=!0,isNode:s=!0,width:l=200,addSvgBackground:u=!1}={},h)=>{if(V.debug("XYZ createText",e,r,n,i,a,s,"addSvgBackground: ",u),a){let f=pj(e,h),d=E9(to(f)),p=e.replace(/\\\\/g,"\\"),m={isNode:s,label:Ni(e)?p:d,labelStyle:r.replace("fill:","color:")};return await I7e(t,m,l,i,u)}else{let f=e.replace(//g,"
    "),d=dj(f.replace("
    ","
    "),h),p=P7e(l,t,d,e?u:!1);if(s){/stroke:/.exec(r)&&(r=r.replace("stroke:","lineColor:"));let m=r.replace(/stroke:[^;]+;?/g,"").replace(/stroke-width:[^;]+;?/g,"").replace(/fill:[^;]+;?/g,"").replace(/color:/g,"fill:");$e(p).attr("style",m)}else{let m=r.replace(/stroke:[^;]+;?/g,"").replace(/stroke-width:[^;]+;?/g,"").replace(/fill:[^;]+;?/g,"").replace(/background:/g,"fill:");$e(p).select("rect").attr("style",m.replace(/background:/g,"fill:"));let g=r.replace(/stroke:[^;]+;?/g,"").replace(/stroke-width:[^;]+;?/g,"").replace(/fill:[^;]+;?/g,"").replace(/color:/g,"fill:");$e(p).select("text").attr("style",g)}return p}},"createText")});function wj(t,e){e&&t.attr("style",e)}function B7e(t){let e=$e(document.createElementNS("http://www.w3.org/2000/svg","foreignObject")),r=e.append("xhtml:div"),n=t.label,i=t.isNode?"nodeLabel":"edgeLabel",a=r.append("span");return a.html(n),wj(a,t.labelStyle),a.attr("class",i),wj(r,t.labelStyle),r.style("display","inline-block"),r.style("white-space","nowrap"),r.attr("xmlns","http://www.w3.org/1999/xhtml"),e.node()}var F7e,ra,bv=R(()=>{"use strict";Zt();ut();_t();rr();xr();Al();o(wj,"applyStyle");o(B7e,"addHtmlLabel");F7e=o((t,e,r,n)=>{let i=t||"";if(typeof i=="object"&&(i=i[0]),yr(de().flowchart.htmlLabels)){i=i.replace(/\\n|\n/g,"
    "),V.debug("vertexText"+i);let a={isNode:n,label:E9(to(i)),labelStyle:e.replace("fill:","color:")};return B7e(a)}else{let a=document.createElementNS("http://www.w3.org/2000/svg","text");a.setAttribute("style",e.replace("color:","fill:"));let s=[];typeof i=="string"?s=i.split(/\\n|\n|/gi):Array.isArray(i)?s=i:s=[];for(let l of s){let u=document.createElementNS("http://www.w3.org/2000/svg","tspan");u.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),u.setAttribute("dy","1em"),u.setAttribute("x","0"),r?u.setAttribute("class","title-row"):u.setAttribute("class","row"),u.textContent=l.trim(),a.appendChild(u)}return a}},"createLabel"),ra=F7e});function z7e(t,e){return t.intersect(e)}var Tj,kj=R(()=>{"use strict";o(z7e,"intersectNode");Tj=z7e});function G7e(t,e,r,n){var i=t.x,a=t.y,s=i-n.x,l=a-n.y,u=Math.sqrt(e*e*l*l+r*r*s*s),h=Math.abs(e*r*s/u);n.x{"use strict";o(G7e,"intersectEllipse");R5=G7e});function $7e(t,e,r){return R5(t,e,e,r)}var Ej,Cj=R(()=>{"use strict";C9();o($7e,"intersectCircle");Ej=$7e});function V7e(t,e,r,n){var i,a,s,l,u,h,f,d,p,m,g,y,v,x,b;if(i=e.y-t.y,s=t.x-e.x,u=e.x*t.y-t.x*e.y,p=i*r.x+s*r.y+u,m=i*n.x+s*n.y+u,!(p!==0&&m!==0&&Sj(p,m))&&(a=n.y-r.y,l=r.x-n.x,h=n.x*r.y-r.x*n.y,f=a*t.x+l*t.y+h,d=a*e.x+l*e.y+h,!(f!==0&&d!==0&&Sj(f,d))&&(g=i*l-a*s,g!==0)))return y=Math.abs(g/2),v=s*h-l*u,x=v<0?(v-y)/g:(v+y)/g,v=a*u-i*h,b=v<0?(v-y)/g:(v+y)/g,{x,y:b}}function Sj(t,e){return t*e>0}var Aj,_j=R(()=>{"use strict";o(V7e,"intersectLine");o(Sj,"sameSign");Aj=V7e});function U7e(t,e,r){var n=t.x,i=t.y,a=[],s=Number.POSITIVE_INFINITY,l=Number.POSITIVE_INFINITY;typeof e.forEach=="function"?e.forEach(function(g){s=Math.min(s,g.x),l=Math.min(l,g.y)}):(s=Math.min(s,e.x),l=Math.min(l,e.y));for(var u=n-t.width/2-s,h=i-t.height/2-l,f=0;f1&&a.sort(function(g,y){var v=g.x-r.x,x=g.y-r.y,b=Math.sqrt(v*v+x*x),w=y.x-r.x,S=y.y-r.y,T=Math.sqrt(w*w+S*S);return b{"use strict";_j();Lj=U7e;o(U7e,"intersectPolygon")});var H7e,Ad,S9=R(()=>{"use strict";H7e=o((t,e)=>{var r=t.x,n=t.y,i=e.x-r,a=e.y-n,s=t.width/2,l=t.height/2,u,h;return Math.abs(a)*s>Math.abs(i)*l?(a<0&&(l=-l),u=a===0?0:l*i/a,h=l):(i<0&&(s=-s),u=s,h=i===0?0:s*a/i),{x:r+u,y:n+h}},"intersectRect"),Ad=H7e});var Tn,A9=R(()=>{"use strict";kj();Cj();C9();Dj();S9();Tn={node:Tj,circle:Ej,ellipse:R5,polygon:Lj,rect:Ad}});function _l(t,e,r,n){return t.insert("polygon",":first-child").attr("points",n.map(function(i){return i.x+","+i.y}).join(" ")).attr("class","label-container").attr("transform","translate("+-e/2+","+r/2+")")}var Ti,kn,N5=R(()=>{"use strict";bv();Al();_t();Zt();rr();xr();Ti=o(async(t,e,r,n)=>{let i=de(),a,s=e.useHtmlLabels||yr(i.flowchart.htmlLabels);r?a=r:a="node default";let l=t.insert("g").attr("class",a).attr("id",e.domId||e.id),u=l.insert("g").attr("class","label").attr("style",e.labelStyle),h;e.labelText===void 0?h="":h=typeof e.labelText=="string"?e.labelText:e.labelText[0];let f=u.node(),d;e.labelType==="markdown"?d=ta(u,qr(to(h),i),{useHtmlLabels:s,width:e.width||i.flowchart.wrappingWidth,classes:"markdown-node-label"},i):d=f.appendChild(ra(qr(to(h),i),e.labelStyle,!1,n));let p=d.getBBox(),m=e.padding/2;if(yr(i.flowchart.htmlLabels)){let g=d.children[0],y=$e(d),v=g.getElementsByTagName("img");if(v){let x=h.replace(/]*>/g,"").trim()==="";await Promise.all([...v].map(b=>new Promise(w=>{function S(){if(b.style.display="flex",b.style.flexDirection="column",x){let T=i.fontSize?i.fontSize:window.getComputedStyle(document.body).fontSize,_=parseInt(T,10)*5+"px";b.style.minWidth=_,b.style.maxWidth=_}else b.style.width="100%";w(b)}o(S,"setupImage"),setTimeout(()=>{b.complete&&S()}),b.addEventListener("error",S),b.addEventListener("load",S)})))}p=g.getBoundingClientRect(),y.attr("width",p.width),y.attr("height",p.height)}return s?u.attr("transform","translate("+-p.width/2+", "+-p.height/2+")"):u.attr("transform","translate(0, "+-p.height/2+")"),e.centerLabel&&u.attr("transform","translate("+-p.width/2+", "+-p.height/2+")"),u.insert("rect",":first-child"),{shapeSvg:l,bbox:p,halfPadding:m,label:u}},"labelHelper"),kn=o((t,e)=>{let r=e.node().getBBox();t.width=r.width,t.height=r.height},"updateNodeBounds");o(_l,"insertPolygonShape")});var Y7e,Rj,Nj=R(()=>{"use strict";N5();ut();_t();A9();Y7e=o(async(t,e)=>{e.useHtmlLabels||de().flowchart.htmlLabels||(e.centerLabel=!0);let{shapeSvg:n,bbox:i,halfPadding:a}=await Ti(t,e,"node "+e.classes,!0);V.info("Classes = ",e.classes);let s=n.insert("rect",":first-child");return s.attr("rx",e.rx).attr("ry",e.ry).attr("x",-i.width/2-a).attr("y",-i.height/2-a).attr("width",i.width+e.padding).attr("height",i.height+e.padding),kn(e,s),e.intersect=function(l){return Tn.rect(e,l)},n},"note"),Rj=Y7e});function _9(t,e,r,n){let i=[],a=o(l=>{i.push(l,0)},"addBorder"),s=o(l=>{i.push(0,l)},"skipBorder");e.includes("t")?(V.debug("add top border"),a(r)):s(r),e.includes("r")?(V.debug("add right border"),a(n)):s(n),e.includes("b")?(V.debug("add bottom border"),a(r)):s(r),e.includes("l")?(V.debug("add left border"),a(n)):s(n),t.attr("stroke-dasharray",i.join(" "))}var Mj,no,Ij,W7e,q7e,X7e,j7e,K7e,Q7e,Z7e,J7e,eSe,tSe,rSe,nSe,iSe,aSe,sSe,oSe,lSe,cSe,uSe,Oj,hSe,fSe,Pj,dm,pm,Bj,Fj,wv,M5=R(()=>{"use strict";Zt();_t();rr();ut();KX();bv();A9();Nj();N5();Mj=o(t=>t?" "+t:"","formatClass"),no=o((t,e)=>`${e||"node default"}${Mj(t.classes)} ${Mj(t.class)}`,"getClassesFromNode"),Ij=o(async(t,e)=>{let{shapeSvg:r,bbox:n}=await Ti(t,e,no(e,void 0),!0),i=n.width+e.padding,a=n.height+e.padding,s=i+a,l=[{x:s/2,y:0},{x:s,y:-s/2},{x:s/2,y:-s},{x:0,y:-s/2}];V.info("Question main (Circle)");let u=_l(r,s,s,l);return u.attr("style",e.style),kn(e,u),e.intersect=function(h){return V.warn("Intersect called"),Tn.polygon(e,l,h)},r},"question"),W7e=o((t,e)=>{let r=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),n=28,i=[{x:0,y:n/2},{x:n/2,y:0},{x:0,y:-n/2},{x:-n/2,y:0}];return r.insert("polygon",":first-child").attr("points",i.map(function(s){return s.x+","+s.y}).join(" ")).attr("class","state-start").attr("r",7).attr("width",28).attr("height",28),e.width=28,e.height=28,e.intersect=function(s){return Tn.circle(e,14,s)},r},"choice"),q7e=o(async(t,e)=>{let{shapeSvg:r,bbox:n}=await Ti(t,e,no(e,void 0),!0),i=4,a=n.height+e.padding,s=a/i,l=n.width+2*s+e.padding,u=[{x:s,y:0},{x:l-s,y:0},{x:l,y:-a/2},{x:l-s,y:-a},{x:s,y:-a},{x:0,y:-a/2}],h=_l(r,l,a,u);return h.attr("style",e.style),kn(e,h),e.intersect=function(f){return Tn.polygon(e,u,f)},r},"hexagon"),X7e=o(async(t,e)=>{let{shapeSvg:r,bbox:n}=await Ti(t,e,void 0,!0),i=2,a=n.height+2*e.padding,s=a/i,l=n.width+2*s+e.padding,u=jX(e.directions,n,e),h=_l(r,l,a,u);return h.attr("style",e.style),kn(e,h),e.intersect=function(f){return Tn.polygon(e,u,f)},r},"block_arrow"),j7e=o(async(t,e)=>{let{shapeSvg:r,bbox:n}=await Ti(t,e,no(e,void 0),!0),i=n.width+e.padding,a=n.height+e.padding,s=[{x:-a/2,y:0},{x:i,y:0},{x:i,y:-a},{x:-a/2,y:-a},{x:0,y:-a/2}];return _l(r,i,a,s).attr("style",e.style),e.width=i+a,e.height=a,e.intersect=function(u){return Tn.polygon(e,s,u)},r},"rect_left_inv_arrow"),K7e=o(async(t,e)=>{let{shapeSvg:r,bbox:n}=await Ti(t,e,no(e),!0),i=n.width+e.padding,a=n.height+e.padding,s=[{x:-2*a/6,y:0},{x:i-a/6,y:0},{x:i+2*a/6,y:-a},{x:a/6,y:-a}],l=_l(r,i,a,s);return l.attr("style",e.style),kn(e,l),e.intersect=function(u){return Tn.polygon(e,s,u)},r},"lean_right"),Q7e=o(async(t,e)=>{let{shapeSvg:r,bbox:n}=await Ti(t,e,no(e,void 0),!0),i=n.width+e.padding,a=n.height+e.padding,s=[{x:2*a/6,y:0},{x:i+a/6,y:0},{x:i-2*a/6,y:-a},{x:-a/6,y:-a}],l=_l(r,i,a,s);return l.attr("style",e.style),kn(e,l),e.intersect=function(u){return Tn.polygon(e,s,u)},r},"lean_left"),Z7e=o(async(t,e)=>{let{shapeSvg:r,bbox:n}=await Ti(t,e,no(e,void 0),!0),i=n.width+e.padding,a=n.height+e.padding,s=[{x:-2*a/6,y:0},{x:i+2*a/6,y:0},{x:i-a/6,y:-a},{x:a/6,y:-a}],l=_l(r,i,a,s);return l.attr("style",e.style),kn(e,l),e.intersect=function(u){return Tn.polygon(e,s,u)},r},"trapezoid"),J7e=o(async(t,e)=>{let{shapeSvg:r,bbox:n}=await Ti(t,e,no(e,void 0),!0),i=n.width+e.padding,a=n.height+e.padding,s=[{x:a/6,y:0},{x:i-a/6,y:0},{x:i+2*a/6,y:-a},{x:-2*a/6,y:-a}],l=_l(r,i,a,s);return l.attr("style",e.style),kn(e,l),e.intersect=function(u){return Tn.polygon(e,s,u)},r},"inv_trapezoid"),eSe=o(async(t,e)=>{let{shapeSvg:r,bbox:n}=await Ti(t,e,no(e,void 0),!0),i=n.width+e.padding,a=n.height+e.padding,s=[{x:0,y:0},{x:i+a/2,y:0},{x:i,y:-a/2},{x:i+a/2,y:-a},{x:0,y:-a}],l=_l(r,i,a,s);return l.attr("style",e.style),kn(e,l),e.intersect=function(u){return Tn.polygon(e,s,u)},r},"rect_right_inv_arrow"),tSe=o(async(t,e)=>{let{shapeSvg:r,bbox:n}=await Ti(t,e,no(e,void 0),!0),i=n.width+e.padding,a=i/2,s=a/(2.5+i/50),l=n.height+s+e.padding,u="M 0,"+s+" a "+a+","+s+" 0,0,0 "+i+" 0 a "+a+","+s+" 0,0,0 "+-i+" 0 l 0,"+l+" a "+a+","+s+" 0,0,0 "+i+" 0 l 0,"+-l,h=r.attr("label-offset-y",s).insert("path",":first-child").attr("style",e.style).attr("d",u).attr("transform","translate("+-i/2+","+-(l/2+s)+")");return kn(e,h),e.intersect=function(f){let d=Tn.rect(e,f),p=d.x-e.x;if(a!=0&&(Math.abs(p)e.height/2-s)){let m=s*s*(1-p*p/(a*a));m!=0&&(m=Math.sqrt(m)),m=s-m,f.y-e.y>0&&(m=-m),d.y+=m}return d},r},"cylinder"),rSe=o(async(t,e)=>{let{shapeSvg:r,bbox:n,halfPadding:i}=await Ti(t,e,"node "+e.classes+" "+e.class,!0),a=r.insert("rect",":first-child"),s=e.positioned?e.width:n.width+e.padding,l=e.positioned?e.height:n.height+e.padding,u=e.positioned?-s/2:-n.width/2-i,h=e.positioned?-l/2:-n.height/2-i;if(a.attr("class","basic label-container").attr("style",e.style).attr("rx",e.rx).attr("ry",e.ry).attr("x",u).attr("y",h).attr("width",s).attr("height",l),e.props){let f=new Set(Object.keys(e.props));e.props.borders&&(_9(a,e.props.borders,s,l),f.delete("borders")),f.forEach(d=>{V.warn(`Unknown node property ${d}`)})}return kn(e,a),e.intersect=function(f){return Tn.rect(e,f)},r},"rect"),nSe=o(async(t,e)=>{let{shapeSvg:r,bbox:n,halfPadding:i}=await Ti(t,e,"node "+e.classes,!0),a=r.insert("rect",":first-child"),s=e.positioned?e.width:n.width+e.padding,l=e.positioned?e.height:n.height+e.padding,u=e.positioned?-s/2:-n.width/2-i,h=e.positioned?-l/2:-n.height/2-i;if(a.attr("class","basic cluster composite label-container").attr("style",e.style).attr("rx",e.rx).attr("ry",e.ry).attr("x",u).attr("y",h).attr("width",s).attr("height",l),e.props){let f=new Set(Object.keys(e.props));e.props.borders&&(_9(a,e.props.borders,s,l),f.delete("borders")),f.forEach(d=>{V.warn(`Unknown node property ${d}`)})}return kn(e,a),e.intersect=function(f){return Tn.rect(e,f)},r},"composite"),iSe=o(async(t,e)=>{let{shapeSvg:r}=await Ti(t,e,"label",!0);V.trace("Classes = ",e.class);let n=r.insert("rect",":first-child"),i=0,a=0;if(n.attr("width",i).attr("height",a),r.attr("class","label edgeLabel"),e.props){let s=new Set(Object.keys(e.props));e.props.borders&&(_9(n,e.props.borders,i,a),s.delete("borders")),s.forEach(l=>{V.warn(`Unknown node property ${l}`)})}return kn(e,n),e.intersect=function(s){return Tn.rect(e,s)},r},"labelRect");o(_9,"applyNodePropertyBorders");aSe=o((t,e)=>{let r;e.classes?r="node "+e.classes:r="node default";let n=t.insert("g").attr("class",r).attr("id",e.domId||e.id),i=n.insert("rect",":first-child"),a=n.insert("line"),s=n.insert("g").attr("class","label"),l=e.labelText.flat?e.labelText.flat():e.labelText,u="";typeof l=="object"?u=l[0]:u=l,V.info("Label text abc79",u,l,typeof l=="object");let h=s.node().appendChild(ra(u,e.labelStyle,!0,!0)),f={width:0,height:0};if(yr(de().flowchart.htmlLabels)){let y=h.children[0],v=$e(h);f=y.getBoundingClientRect(),v.attr("width",f.width),v.attr("height",f.height)}V.info("Text 2",l);let d=l.slice(1,l.length),p=h.getBBox(),m=s.node().appendChild(ra(d.join?d.join("
    "):d,e.labelStyle,!0,!0));if(yr(de().flowchart.htmlLabels)){let y=m.children[0],v=$e(m);f=y.getBoundingClientRect(),v.attr("width",f.width),v.attr("height",f.height)}let g=e.padding/2;return $e(m).attr("transform","translate( "+(f.width>p.width?0:(p.width-f.width)/2)+", "+(p.height+g+5)+")"),$e(h).attr("transform","translate( "+(f.width{let{shapeSvg:r,bbox:n}=await Ti(t,e,no(e,void 0),!0),i=n.height+e.padding,a=n.width+i/4+e.padding,s=r.insert("rect",":first-child").attr("style",e.style).attr("rx",i/2).attr("ry",i/2).attr("x",-a/2).attr("y",-i/2).attr("width",a).attr("height",i);return kn(e,s),e.intersect=function(l){return Tn.rect(e,l)},r},"stadium"),oSe=o(async(t,e)=>{let{shapeSvg:r,bbox:n,halfPadding:i}=await Ti(t,e,no(e,void 0),!0),a=r.insert("circle",":first-child");return a.attr("style",e.style).attr("rx",e.rx).attr("ry",e.ry).attr("r",n.width/2+i).attr("width",n.width+e.padding).attr("height",n.height+e.padding),V.info("Circle main"),kn(e,a),e.intersect=function(s){return V.info("Circle intersect",e,n.width/2+i,s),Tn.circle(e,n.width/2+i,s)},r},"circle"),lSe=o(async(t,e)=>{let{shapeSvg:r,bbox:n,halfPadding:i}=await Ti(t,e,no(e,void 0),!0),a=5,s=r.insert("g",":first-child"),l=s.insert("circle"),u=s.insert("circle");return s.attr("class",e.class),l.attr("style",e.style).attr("rx",e.rx).attr("ry",e.ry).attr("r",n.width/2+i+a).attr("width",n.width+e.padding+a*2).attr("height",n.height+e.padding+a*2),u.attr("style",e.style).attr("rx",e.rx).attr("ry",e.ry).attr("r",n.width/2+i).attr("width",n.width+e.padding).attr("height",n.height+e.padding),V.info("DoubleCircle main"),kn(e,l),e.intersect=function(h){return V.info("DoubleCircle intersect",e,n.width/2+i+a,h),Tn.circle(e,n.width/2+i+a,h)},r},"doublecircle"),cSe=o(async(t,e)=>{let{shapeSvg:r,bbox:n}=await Ti(t,e,no(e,void 0),!0),i=n.width+e.padding,a=n.height+e.padding,s=[{x:0,y:0},{x:i,y:0},{x:i,y:-a},{x:0,y:-a},{x:0,y:0},{x:-8,y:0},{x:i+8,y:0},{x:i+8,y:-a},{x:-8,y:-a},{x:-8,y:0}],l=_l(r,i,a,s);return l.attr("style",e.style),kn(e,l),e.intersect=function(u){return Tn.polygon(e,s,u)},r},"subroutine"),uSe=o((t,e)=>{let r=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),n=r.insert("circle",":first-child");return n.attr("class","state-start").attr("r",7).attr("width",14).attr("height",14),kn(e,n),e.intersect=function(i){return Tn.circle(e,7,i)},r},"start"),Oj=o((t,e,r)=>{let n=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),i=70,a=10;r==="LR"&&(i=10,a=70);let s=n.append("rect").attr("x",-1*i/2).attr("y",-1*a/2).attr("width",i).attr("height",a).attr("class","fork-join");return kn(e,s),e.height=e.height+e.padding/2,e.width=e.width+e.padding/2,e.intersect=function(l){return Tn.rect(e,l)},n},"forkJoin"),hSe=o((t,e)=>{let r=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),n=r.insert("circle",":first-child"),i=r.insert("circle",":first-child");return i.attr("class","state-start").attr("r",7).attr("width",14).attr("height",14),n.attr("class","state-end").attr("r",5).attr("width",10).attr("height",10),kn(e,i),e.intersect=function(a){return Tn.circle(e,7,a)},r},"end"),fSe=o((t,e)=>{let r=e.padding/2,n=4,i=8,a;e.classes?a="node "+e.classes:a="node default";let s=t.insert("g").attr("class",a).attr("id",e.domId||e.id),l=s.insert("rect",":first-child"),u=s.insert("line"),h=s.insert("line"),f=0,d=n,p=s.insert("g").attr("class","label"),m=0,g=e.classData.annotations?.[0],y=e.classData.annotations[0]?"\xAB"+e.classData.annotations[0]+"\xBB":"",v=p.node().appendChild(ra(y,e.labelStyle,!0,!0)),x=v.getBBox();if(yr(de().flowchart.htmlLabels)){let A=v.children[0],L=$e(v);x=A.getBoundingClientRect(),L.attr("width",x.width),L.attr("height",x.height)}e.classData.annotations[0]&&(d+=x.height+n,f+=x.width);let b=e.classData.label;e.classData.type!==void 0&&e.classData.type!==""&&(de().flowchart.htmlLabels?b+="<"+e.classData.type+">":b+="<"+e.classData.type+">");let w=p.node().appendChild(ra(b,e.labelStyle,!0,!0));$e(w).attr("class","classTitle");let S=w.getBBox();if(yr(de().flowchart.htmlLabels)){let A=w.children[0],L=$e(w);S=A.getBoundingClientRect(),L.attr("width",S.width),L.attr("height",S.height)}d+=S.height+n,S.width>f&&(f=S.width);let T=[];e.classData.members.forEach(A=>{let L=A.getDisplayDetails(),M=L.displayText;de().flowchart.htmlLabels&&(M=M.replace(//g,">"));let N=p.node().appendChild(ra(M,L.cssStyle?L.cssStyle:e.labelStyle,!0,!0)),k=N.getBBox();if(yr(de().flowchart.htmlLabels)){let I=N.children[0],C=$e(N);k=I.getBoundingClientRect(),C.attr("width",k.width),C.attr("height",k.height)}k.width>f&&(f=k.width),d+=k.height+n,T.push(N)}),d+=i;let E=[];if(e.classData.methods.forEach(A=>{let L=A.getDisplayDetails(),M=L.displayText;de().flowchart.htmlLabels&&(M=M.replace(//g,">"));let N=p.node().appendChild(ra(M,L.cssStyle?L.cssStyle:e.labelStyle,!0,!0)),k=N.getBBox();if(yr(de().flowchart.htmlLabels)){let I=N.children[0],C=$e(N);k=I.getBoundingClientRect(),C.attr("width",k.width),C.attr("height",k.height)}k.width>f&&(f=k.width),d+=k.height+n,E.push(N)}),d+=i,g){let A=(f-x.width)/2;$e(v).attr("transform","translate( "+(-1*f/2+A)+", "+-1*d/2+")"),m=x.height+n}let _=(f-S.width)/2;return $e(w).attr("transform","translate( "+(-1*f/2+_)+", "+(-1*d/2+m)+")"),m+=S.height+n,u.attr("class","divider").attr("x1",-f/2-r).attr("x2",f/2+r).attr("y1",-d/2-r+i+m).attr("y2",-d/2-r+i+m),m+=i,T.forEach(A=>{$e(A).attr("transform","translate( "+-f/2+", "+(-1*d/2+m+i/2)+")");let L=A?.getBBox();m+=(L?.height??0)+n}),m+=i,h.attr("class","divider").attr("x1",-f/2-r).attr("x2",f/2+r).attr("y1",-d/2-r+i+m).attr("y2",-d/2-r+i+m),m+=i,E.forEach(A=>{$e(A).attr("transform","translate( "+-f/2+", "+(-1*d/2+m)+")");let L=A?.getBBox();m+=(L?.height??0)+n}),l.attr("style",e.style).attr("class","outer title-state").attr("x",-f/2-r).attr("y",-(d/2)-r).attr("width",f+e.padding).attr("height",d+e.padding),kn(e,l),e.intersect=function(A){return Tn.rect(e,A)},s},"class_box"),Pj={rhombus:Ij,composite:nSe,question:Ij,rect:rSe,labelRect:iSe,rectWithTitle:aSe,choice:W7e,circle:oSe,doublecircle:lSe,stadium:sSe,hexagon:q7e,block_arrow:X7e,rect_left_inv_arrow:j7e,lean_right:K7e,lean_left:Q7e,trapezoid:Z7e,inv_trapezoid:J7e,rect_right_inv_arrow:eSe,cylinder:tSe,start:uSe,end:hSe,note:Rj,subroutine:cSe,fork:Oj,join:Oj,class_box:fSe},dm={},pm=o(async(t,e,r)=>{let n,i;if(e.link){let a;de().securityLevel==="sandbox"?a="_top":e.linkTarget&&(a=e.linkTarget||"_blank"),n=t.insert("svg:a").attr("xlink:href",e.link).attr("target",a),i=await Pj[e.shape](n,e,r)}else i=await Pj[e.shape](t,e,r),n=i;return e.tooltip&&i.attr("title",e.tooltip),e.class&&i.attr("class","node default "+e.class),dm[e.id]=n,e.haveCallback&&dm[e.id].attr("class",dm[e.id].attr("class")+" clickable"),n},"insertNode"),Bj=o((t,e)=>{dm[e.id]=t},"setNodeElem"),Fj=o(()=>{dm={}},"clear"),wv=o(t=>{let e=dm[t.id];V.trace("Transforming node",t.diff,t,"translate("+(t.x-t.width/2-5)+", "+t.width/2+")");let r=8,n=t.diff||0;return t.clusterNode?e.attr("transform","translate("+(t.x+n-t.width/2)+", "+(t.y-t.height/2-r)+")"):e.attr("transform","translate("+t.x+", "+t.y+")"),n},"positionNode")});var I5,L9=R(()=>{"use strict";Zt();M5();I5=o((t,e)=>{let r;return e==="sandbox"&&(r=$e("#i"+t)),(e==="sandbox"?$e(r.nodes()[0].contentDocument.body):$e("body")).select(`[id="${t}"]`)},"getDiagramElement")});var io,_d=R(()=>{"use strict";io=o(({flowchart:t})=>{let e=t?.subGraphTitleMargin?.top??0,r=t?.subGraphTitleMargin?.bottom??0,n=e+r;return{subGraphTitleTopMargin:e,subGraphTitleBottomMargin:r,subGraphTitleTotalMargin:n}},"getSubGraphTitleMargins")});function D9(t,e,r){if(t&&t.length){let[n,i]=e,a=Math.PI/180*r,s=Math.cos(a),l=Math.sin(a);for(let u of t){let[h,f]=u;u[0]=(h-n)*s-(f-i)*l+n,u[1]=(h-n)*l+(f-i)*s+i}}}function dSe(t,e){return t[0]===e[0]&&t[1]===e[1]}function pSe(t,e,r,n=1){let i=r,a=Math.max(e,.1),s=t[0]&&t[0][0]&&typeof t[0][0]=="number"?[t]:t,l=[0,0];if(i)for(let h of s)D9(h,l,i);let u=function(h,f,d){let p=[];for(let b of h){let w=[...b];dSe(w[0],w[w.length-1])||w.push([w[0][0],w[0][1]]),w.length>2&&p.push(w)}let m=[];f=Math.max(f,.1);let g=[];for(let b of p)for(let w=0;wb.yminw.ymin?1:b.xw.x?1:b.ymax===w.ymax?0:(b.ymax-w.ymax)/Math.abs(b.ymax-w.ymax)),!g.length)return m;let y=[],v=g[0].ymin,x=0;for(;y.length||g.length;){if(g.length){let b=-1;for(let w=0;wv);w++)b=w;g.splice(0,b+1).forEach(w=>{y.push({s:v,edge:w})})}if(y=y.filter(b=>!(b.edge.ymax<=v)),y.sort((b,w)=>b.edge.x===w.edge.x?0:(b.edge.x-w.edge.x)/Math.abs(b.edge.x-w.edge.x)),(d!==1||x%f==0)&&y.length>1)for(let b=0;b=y.length)break;let S=y[b].edge,T=y[w].edge;m.push([[Math.round(S.x),v],[Math.round(T.x),v]])}v+=d,y.forEach(b=>{b.edge.x=b.edge.x+d*b.edge.islope}),x++}return m}(s,a,n);if(i){for(let h of s)D9(h,l,-i);(function(h,f,d){let p=[];h.forEach(m=>p.push(...m)),D9(p,f,d)})(u,l,-i)}return u}function Cv(t,e){var r;let n=e.hachureAngle+90,i=e.hachureGap;i<0&&(i=4*e.strokeWidth),i=Math.round(Math.max(i,.1));let a=1;return e.roughness>=1&&(((r=e.randomizer)===null||r===void 0?void 0:r.next())||Math.random())>.7&&(a=i),pSe(t,i,n,a||1)}function U5(t){let e=t[0],r=t[1];return Math.sqrt(Math.pow(e[0]-r[0],2)+Math.pow(e[1]-r[1],2))}function N9(t,e){return t.type===e}function W9(t){let e=[],r=function(s){let l=new Array;for(;s!=="";)if(s.match(/^([ \t\r\n,]+)/))s=s.substr(RegExp.$1.length);else if(s.match(/^([aAcChHlLmMqQsStTvVzZ])/))l[l.length]={type:mSe,text:RegExp.$1},s=s.substr(RegExp.$1.length);else{if(!s.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/))return[];l[l.length]={type:R9,text:`${parseFloat(RegExp.$1)}`},s=s.substr(RegExp.$1.length)}return l[l.length]={type:zj,text:""},l}(t),n="BOD",i=0,a=r[i];for(;!N9(a,zj);){let s=0,l=[];if(n==="BOD"){if(a.text!=="M"&&a.text!=="m")return W9("M0,0"+t);i++,s=O5[a.text],n=a.text}else N9(a,R9)?s=O5[n]:(i++,s=O5[a.text],n=a.text);if(!(i+sf%2?h+r:h+e);a.push({key:"C",data:u}),e=u[4],r=u[5];break}case"Q":a.push({key:"Q",data:[...l]}),e=l[2],r=l[3];break;case"q":{let u=l.map((h,f)=>f%2?h+r:h+e);a.push({key:"Q",data:u}),e=u[2],r=u[3];break}case"A":a.push({key:"A",data:[...l]}),e=l[5],r=l[6];break;case"a":e+=l[5],r+=l[6],a.push({key:"A",data:[l[0],l[1],l[2],l[3],l[4],e,r]});break;case"H":a.push({key:"H",data:[...l]}),e=l[0];break;case"h":e+=l[0],a.push({key:"H",data:[e]});break;case"V":a.push({key:"V",data:[...l]}),r=l[0];break;case"v":r+=l[0],a.push({key:"V",data:[r]});break;case"S":a.push({key:"S",data:[...l]}),e=l[2],r=l[3];break;case"s":{let u=l.map((h,f)=>f%2?h+r:h+e);a.push({key:"S",data:u}),e=u[2],r=u[3];break}case"T":a.push({key:"T",data:[...l]}),e=l[0],r=l[1];break;case"t":e+=l[0],r+=l[1],a.push({key:"T",data:[e,r]});break;case"Z":case"z":a.push({key:"Z",data:[]}),e=n,r=i}return a}function Xj(t){let e=[],r="",n=0,i=0,a=0,s=0,l=0,u=0;for(let{key:h,data:f}of t){switch(h){case"M":e.push({key:"M",data:[...f]}),[n,i]=f,[a,s]=f;break;case"C":e.push({key:"C",data:[...f]}),n=f[4],i=f[5],l=f[2],u=f[3];break;case"L":e.push({key:"L",data:[...f]}),[n,i]=f;break;case"H":n=f[0],e.push({key:"L",data:[n,i]});break;case"V":i=f[0],e.push({key:"L",data:[n,i]});break;case"S":{let d=0,p=0;r==="C"||r==="S"?(d=n+(n-l),p=i+(i-u)):(d=n,p=i),e.push({key:"C",data:[d,p,...f]}),l=f[0],u=f[1],n=f[2],i=f[3];break}case"T":{let[d,p]=f,m=0,g=0;r==="Q"||r==="T"?(m=n+(n-l),g=i+(i-u)):(m=n,g=i);let y=n+2*(m-n)/3,v=i+2*(g-i)/3,x=d+2*(m-d)/3,b=p+2*(g-p)/3;e.push({key:"C",data:[y,v,x,b,d,p]}),l=m,u=g,n=d,i=p;break}case"Q":{let[d,p,m,g]=f,y=n+2*(d-n)/3,v=i+2*(p-i)/3,x=m+2*(d-m)/3,b=g+2*(p-g)/3;e.push({key:"C",data:[y,v,x,b,m,g]}),l=d,u=p,n=m,i=g;break}case"A":{let d=Math.abs(f[0]),p=Math.abs(f[1]),m=f[2],g=f[3],y=f[4],v=f[5],x=f[6];d===0||p===0?(e.push({key:"C",data:[n,i,v,x,v,x]}),n=v,i=x):(n!==v||i!==x)&&(jj(n,i,v,x,d,p,m,g,y).forEach(function(b){e.push({key:"C",data:b})}),n=v,i=x);break}case"Z":e.push({key:"Z",data:[]}),n=a,i=s}r=h}return e}function Tv(t,e,r){return[t*Math.cos(r)-e*Math.sin(r),t*Math.sin(r)+e*Math.cos(r)]}function jj(t,e,r,n,i,a,s,l,u,h){let f=(d=s,Math.PI*d/180);var d;let p=[],m=0,g=0,y=0,v=0;if(h)[m,g,y,v]=h;else{[t,e]=Tv(t,e,-f),[r,n]=Tv(r,n,-f);let I=(t-r)/2,C=(e-n)/2,O=I*I/(i*i)+C*C/(a*a);O>1&&(O=Math.sqrt(O),i*=O,a*=O);let D=i*i,P=a*a,F=D*P-D*C*C-P*I*I,B=D*C*C+P*I*I,$=(l===u?-1:1)*Math.sqrt(Math.abs(F/B));y=$*i*C/a+(t+r)/2,v=$*-a*I/i+(e+n)/2,m=Math.asin(parseFloat(((e-v)/a).toFixed(9))),g=Math.asin(parseFloat(((n-v)/a).toFixed(9))),tg&&(m-=2*Math.PI),!u&&g>m&&(g-=2*Math.PI)}let x=g-m;if(Math.abs(x)>120*Math.PI/180){let I=g,C=r,O=n;g=u&&g>m?m+120*Math.PI/180*1:m+120*Math.PI/180*-1,p=jj(r=y+i*Math.cos(g),n=v+a*Math.sin(g),C,O,i,a,s,0,u,[g,I,y,v])}x=g-m;let b=Math.cos(m),w=Math.sin(m),S=Math.cos(g),T=Math.sin(g),E=Math.tan(x/4),_=4/3*i*E,A=4/3*a*E,L=[t,e],M=[t+_*w,e-A*b],N=[r+_*T,n-A*S],k=[r,n];if(M[0]=2*L[0]-M[0],M[1]=2*L[1]-M[1],h)return[M,N,k].concat(p);{p=[M,N,k].concat(p);let I=[];for(let C=0;C2){let i=[];for(let a=0;a2*Math.PI&&(m=0,g=2*Math.PI);let y=2*Math.PI/u.curveStepCount,v=Math.min(y/2,(g-m)/2),x=Yj(v,h,f,d,p,m,g,1,u);if(!u.disableMultiStroke){let b=Yj(v,h,f,d,p,m,g,1.5,u);x.push(...b)}return s&&(l?x.push(...Vh(h,f,h+d*Math.cos(m),f+p*Math.sin(m),u),...Vh(h,f,h+d*Math.cos(g),f+p*Math.sin(g),u)):x.push({op:"lineTo",data:[h,f]},{op:"lineTo",data:[h+d*Math.cos(m),f+p*Math.sin(m)]})),{type:"path",ops:x}}function Vj(t,e){let r=Xj(qj(W9(t))),n=[],i=[0,0],a=[0,0];for(let{key:s,data:l}of r)switch(s){case"M":a=[l[0],l[1]],i=[l[0],l[1]];break;case"L":n.push(...Vh(a[0],a[1],l[0],l[1],e)),a=[l[0],l[1]];break;case"C":{let[u,h,f,d,p,m]=l;n.push(...vSe(u,h,f,d,p,m,a,e)),a=[p,m];break}case"Z":n.push(...Vh(a[0],a[1],i[0],i[1],e)),a=[i[0],i[1]]}return{type:"path",ops:n}}function M9(t,e){let r=[];for(let n of t)if(n.length){let i=e.maxRandomnessOffset||0,a=n.length;if(a>2){r.push({op:"move",data:[n[0][0]+Yt(i,e),n[0][1]+Yt(i,e)]});for(let s=1;s500?.4:-.0016668*u+1.233334;let f=i.maxRandomnessOffset||0;f*f*100>l&&(f=u/10);let d=f/2,p=.2+.2*Zj(i),m=i.bowing*i.maxRandomnessOffset*(n-e)/200,g=i.bowing*i.maxRandomnessOffset*(t-r)/200;m=Yt(m,i,h),g=Yt(g,i,h);let y=[],v=o(()=>Yt(d,i,h),"M"),x=o(()=>Yt(f,i,h),"k"),b=i.preserveVertices;return a&&(s?y.push({op:"move",data:[t+(b?0:v()),e+(b?0:v())]}):y.push({op:"move",data:[t+(b?0:Yt(f,i,h)),e+(b?0:Yt(f,i,h))]})),s?y.push({op:"bcurveTo",data:[m+t+(r-t)*p+v(),g+e+(n-e)*p+v(),m+t+2*(r-t)*p+v(),g+e+2*(n-e)*p+v(),r+(b?0:v()),n+(b?0:v())]}):y.push({op:"bcurveTo",data:[m+t+(r-t)*p+x(),g+e+(n-e)*p+x(),m+t+2*(r-t)*p+x(),g+e+2*(n-e)*p+x(),r+(b?0:x()),n+(b?0:x())]}),y}function P5(t,e,r){if(!t.length)return[];let n=[];n.push([t[0][0]+Yt(e,r),t[0][1]+Yt(e,r)]),n.push([t[0][0]+Yt(e,r),t[0][1]+Yt(e,r)]);for(let i=1;i3){let a=[],s=1-r.curveTightness;i.push({op:"move",data:[t[1][0],t[1][1]]});for(let l=1;l+21&&i.push(l)):i.push(l),i.push(t[e+3])}else{let u=t[e+0],h=t[e+1],f=t[e+2],d=t[e+3],p=Ld(u,h,.5),m=Ld(h,f,.5),g=Ld(f,d,.5),y=Ld(p,m,.5),v=Ld(m,g,.5),x=Ld(y,v,.5);U9([u,p,y,x],0,r,i),U9([x,v,g,d],0,r,i)}var a,s;return i}function bSe(t,e){return V5(t,0,t.length,e)}function V5(t,e,r,n,i){let a=i||[],s=t[e],l=t[r-1],u=0,h=1;for(let f=e+1;fu&&(u=d,h=f)}return Math.sqrt(u)>n?(V5(t,e,h+1,n,a),V5(t,h,r,n,a)):(a.length||a.push(s),a.push(l)),a}function I9(t,e=.15,r){let n=[],i=(t.length-1)/3;for(let a=0;a0?V5(n,0,n.length,r):n}var Ev,O9,P9,B9,F9,z9,Cs,G9,mSe,R9,zj,O5,gSe,ao,gm,H9,B5,Y9,Jt,ti=R(()=>{"use strict";o(D9,"t");o(dSe,"e");o(pSe,"s");o(Cv,"n");Ev=class{static{o(this,"o")}constructor(e){this.helper=e}fillPolygons(e,r){return this._fillPolygons(e,r)}_fillPolygons(e,r){let n=Cv(e,r);return{type:"fillSketch",ops:this.renderLines(n,r)}}renderLines(e,r){let n=[];for(let i of e)n.push(...this.helper.doubleLineOps(i[0][0],i[0][1],i[1][0],i[1][1],r));return n}};o(U5,"a");O9=class extends Ev{static{o(this,"h")}fillPolygons(e,r){let n=r.hachureGap;n<0&&(n=4*r.strokeWidth),n=Math.max(n,.1);let i=Cv(e,Object.assign({},r,{hachureGap:n})),a=Math.PI/180*r.hachureAngle,s=[],l=.5*n*Math.cos(a),u=.5*n*Math.sin(a);for(let[h,f]of i)U5([h,f])&&s.push([[h[0]-l,h[1]+u],[...f]],[[h[0]+l,h[1]-u],[...f]]);return{type:"fillSketch",ops:this.renderLines(s,r)}}},P9=class extends Ev{static{o(this,"r")}fillPolygons(e,r){let n=this._fillPolygons(e,r),i=Object.assign({},r,{hachureAngle:r.hachureAngle+90}),a=this._fillPolygons(e,i);return n.ops=n.ops.concat(a.ops),n}},B9=class{static{o(this,"i")}constructor(e){this.helper=e}fillPolygons(e,r){let n=Cv(e,r=Object.assign({},r,{hachureAngle:0}));return this.dotsOnLines(n,r)}dotsOnLines(e,r){let n=[],i=r.hachureGap;i<0&&(i=4*r.strokeWidth),i=Math.max(i,.1);let a=r.fillWeight;a<0&&(a=r.strokeWidth/2);let s=i/4;for(let l of e){let u=U5(l),h=u/i,f=Math.ceil(h)-1,d=u-f*i,p=(l[0][0]+l[1][0])/2-i/4,m=Math.min(l[0][1],l[1][1]);for(let g=0;g{let l=U5(s),u=Math.floor(l/(n+i)),h=(l+i-u*(n+i))/2,f=s[0],d=s[1];f[0]>d[0]&&(f=s[1],d=s[0]);let p=Math.atan((d[1]-f[1])/(d[0]-f[0]));for(let m=0;m{let s=U5(a),l=Math.round(s/(2*r)),u=a[0],h=a[1];u[0]>h[0]&&(u=a[1],h=a[0]);let f=Math.atan((h[1]-u[1])/(h[0]-u[0]));for(let d=0;d2*Math.PI&&(_=0,A=2*Math.PI);let L=(A-_)/b.curveStepCount,M=[];for(let N=_;N<=A;N+=L)M.push([w+T*Math.cos(N),S+E*Math.sin(N)]);return M.push([w+T*Math.cos(A),S+E*Math.sin(A)]),M.push([w,S]),mm([M],b)}(e,r,n,i,a,s,h));return h.stroke!==ao&&f.push(d),this._d("arc",f,h)}curve(e,r){let n=this._o(r),i=[],a=Gj(e,n);if(n.fill&&n.fill!==ao)if(n.fillStyle==="solid"){let s=Gj(e,Object.assign(Object.assign({},n),{disableMultiStroke:!0,roughness:n.roughness?n.roughness+n.fillShapeRoughnessGain:0}));i.push({type:"fillPath",ops:this._mergedShape(s.ops)})}else{let s=[],l=e;if(l.length){let u=typeof l[0][0]=="number"?[l]:l;for(let h of u)h.length<3?s.push(...h):h.length===3?s.push(...I9(Wj([h[0],h[0],h[1],h[2]]),10,(1+n.roughness)/2)):s.push(...I9(Wj(h),10,(1+n.roughness)/2))}s.length&&i.push(mm([s],n))}return n.stroke!==ao&&i.push(a),this._d("curve",i,n)}polygon(e,r){let n=this._o(r),i=[],a=F5(e,!0,n);return n.fill&&(n.fillStyle==="solid"?i.push(M9([e],n)):i.push(mm([e],n))),n.stroke!==ao&&i.push(a),this._d("polygon",i,n)}path(e,r){let n=this._o(r),i=[];if(!e)return this._d("path",i,n);e=(e||"").replace(/\n/g," ").replace(/(-\s)/g,"-").replace("/(ss)/g"," ");let a=n.fill&&n.fill!=="transparent"&&n.fill!==ao,s=n.stroke!==ao,l=!!(n.simplification&&n.simplification<1),u=function(f,d,p){let m=Xj(qj(W9(f))),g=[],y=[],v=[0,0],x=[],b=o(()=>{x.length>=4&&y.push(...I9(x,d)),x=[]},"i"),w=o(()=>{b(),y.length&&(g.push(y),y=[])},"c");for(let{key:T,data:E}of m)switch(T){case"M":w(),v=[E[0],E[1]],y.push(v);break;case"L":b(),y.push([E[0],E[1]]);break;case"C":if(!x.length){let _=y.length?y[y.length-1]:v;x.push([_[0],_[1]])}x.push([E[0],E[1]]),x.push([E[2],E[3]]),x.push([E[4],E[5]]);break;case"Z":b(),y.push([v[0],v[1]])}if(w(),!p)return g;let S=[];for(let T of g){let E=bSe(T,p);E.length&&S.push(E)}return S}(e,1,l?4-4*(n.simplification||1):(1+n.roughness)/2),h=Vj(e,n);if(a)if(n.fillStyle==="solid")if(u.length===1){let f=Vj(e,Object.assign(Object.assign({},n),{disableMultiStroke:!0,roughness:n.roughness?n.roughness+n.fillShapeRoughnessGain:0}));i.push({type:"fillPath",ops:this._mergedShape(f.ops)})}else i.push(M9(u,n));else i.push(mm(u,n));return s&&(l?u.forEach(f=>{i.push(F5(f,!1,n))}):i.push(h)),this._d("path",i,n)}opsToPath(e,r){let n="";for(let i of e.ops){let a=typeof r=="number"&&r>=0?i.data.map(s=>+s.toFixed(r)):i.data;switch(i.op){case"move":n+=`M${a[0]} ${a[1]} `;break;case"bcurveTo":n+=`C${a[0]} ${a[1]}, ${a[2]} ${a[3]}, ${a[4]} ${a[5]} `;break;case"lineTo":n+=`L${a[0]} ${a[1]} `}}return n.trim()}toPaths(e){let r=e.sets||[],n=e.options||this.defaultOptions,i=[];for(let a of r){let s=null;switch(a.type){case"path":s={d:this.opsToPath(a),stroke:n.stroke,strokeWidth:n.strokeWidth,fill:ao};break;case"fillPath":s={d:this.opsToPath(a),stroke:ao,strokeWidth:0,fill:n.fill||ao};break;case"fillSketch":s=this.fillSketch(a,n)}s&&i.push(s)}return i}fillSketch(e,r){let n=r.fillWeight;return n<0&&(n=r.strokeWidth/2),{d:this.opsToPath(e),stroke:r.fill||ao,strokeWidth:n,fill:ao}}_mergedShape(e){return e.filter((r,n)=>n===0||r.op!=="move")}},H9=class{static{o(this,"st")}constructor(e,r){this.canvas=e,this.ctx=this.canvas.getContext("2d"),this.gen=new gm(r)}draw(e){let r=e.sets||[],n=e.options||this.getDefaultOptions(),i=this.ctx,a=e.options.fixedDecimalPlaceDigits;for(let s of r)switch(s.type){case"path":i.save(),i.strokeStyle=n.stroke==="none"?"transparent":n.stroke,i.lineWidth=n.strokeWidth,n.strokeLineDash&&i.setLineDash(n.strokeLineDash),n.strokeLineDashOffset&&(i.lineDashOffset=n.strokeLineDashOffset),this._drawToContext(i,s,a),i.restore();break;case"fillPath":{i.save(),i.fillStyle=n.fill||"";let l=e.shape==="curve"||e.shape==="polygon"||e.shape==="path"?"evenodd":"nonzero";this._drawToContext(i,s,a,l),i.restore();break}case"fillSketch":this.fillSketch(i,s,n)}}fillSketch(e,r,n){let i=n.fillWeight;i<0&&(i=n.strokeWidth/2),e.save(),n.fillLineDash&&e.setLineDash(n.fillLineDash),n.fillLineDashOffset&&(e.lineDashOffset=n.fillLineDashOffset),e.strokeStyle=n.fill||"",e.lineWidth=i,this._drawToContext(e,r,n.fixedDecimalPlaceDigits),e.restore()}_drawToContext(e,r,n,i="nonzero"){e.beginPath();for(let a of r.ops){let s=typeof n=="number"&&n>=0?a.data.map(l=>+l.toFixed(n)):a.data;switch(a.op){case"move":e.moveTo(s[0],s[1]);break;case"bcurveTo":e.bezierCurveTo(s[0],s[1],s[2],s[3],s[4],s[5]);break;case"lineTo":e.lineTo(s[0],s[1])}}r.type==="fillPath"?e.fill(i):e.stroke()}get generator(){return this.gen}getDefaultOptions(){return this.gen.defaultOptions}line(e,r,n,i,a){let s=this.gen.line(e,r,n,i,a);return this.draw(s),s}rectangle(e,r,n,i,a){let s=this.gen.rectangle(e,r,n,i,a);return this.draw(s),s}ellipse(e,r,n,i,a){let s=this.gen.ellipse(e,r,n,i,a);return this.draw(s),s}circle(e,r,n,i){let a=this.gen.circle(e,r,n,i);return this.draw(a),a}linearPath(e,r){let n=this.gen.linearPath(e,r);return this.draw(n),n}polygon(e,r){let n=this.gen.polygon(e,r);return this.draw(n),n}arc(e,r,n,i,a,s,l=!1,u){let h=this.gen.arc(e,r,n,i,a,s,l,u);return this.draw(h),h}curve(e,r){let n=this.gen.curve(e,r);return this.draw(n),n}path(e,r){let n=this.gen.path(e,r);return this.draw(n),n}},B5="http://www.w3.org/2000/svg",Y9=class{static{o(this,"ot")}constructor(e,r){this.svg=e,this.gen=new gm(r)}draw(e){let r=e.sets||[],n=e.options||this.getDefaultOptions(),i=this.svg.ownerDocument||window.document,a=i.createElementNS(B5,"g"),s=e.options.fixedDecimalPlaceDigits;for(let l of r){let u=null;switch(l.type){case"path":u=i.createElementNS(B5,"path"),u.setAttribute("d",this.opsToPath(l,s)),u.setAttribute("stroke",n.stroke),u.setAttribute("stroke-width",n.strokeWidth+""),u.setAttribute("fill","none"),n.strokeLineDash&&u.setAttribute("stroke-dasharray",n.strokeLineDash.join(" ").trim()),n.strokeLineDashOffset&&u.setAttribute("stroke-dashoffset",`${n.strokeLineDashOffset}`);break;case"fillPath":u=i.createElementNS(B5,"path"),u.setAttribute("d",this.opsToPath(l,s)),u.setAttribute("stroke","none"),u.setAttribute("stroke-width","0"),u.setAttribute("fill",n.fill||""),e.shape!=="curve"&&e.shape!=="polygon"||u.setAttribute("fill-rule","evenodd");break;case"fillSketch":u=this.fillSketch(i,l,n)}u&&a.appendChild(u)}return a}fillSketch(e,r,n){let i=n.fillWeight;i<0&&(i=n.strokeWidth/2);let a=e.createElementNS(B5,"path");return a.setAttribute("d",this.opsToPath(r,n.fixedDecimalPlaceDigits)),a.setAttribute("stroke",n.fill||""),a.setAttribute("stroke-width",i+""),a.setAttribute("fill","none"),n.fillLineDash&&a.setAttribute("stroke-dasharray",n.fillLineDash.join(" ").trim()),n.fillLineDashOffset&&a.setAttribute("stroke-dashoffset",`${n.fillLineDashOffset}`),a}get generator(){return this.gen}getDefaultOptions(){return this.gen.defaultOptions}opsToPath(e,r){return this.gen.opsToPath(e,r)}line(e,r,n,i,a){let s=this.gen.line(e,r,n,i,a);return this.draw(s)}rectangle(e,r,n,i,a){let s=this.gen.rectangle(e,r,n,i,a);return this.draw(s)}ellipse(e,r,n,i,a){let s=this.gen.ellipse(e,r,n,i,a);return this.draw(s)}circle(e,r,n,i){let a=this.gen.circle(e,r,n,i);return this.draw(a)}linearPath(e,r){let n=this.gen.linearPath(e,r);return this.draw(n)}polygon(e,r){let n=this.gen.polygon(e,r);return this.draw(n)}arc(e,r,n,i,a,s,l=!1,u){let h=this.gen.arc(e,r,n,i,a,s,l,u);return this.draw(h)}curve(e,r){let n=this.gen.curve(e,r);return this.draw(n)}path(e,r){let n=this.gen.path(e,r);return this.draw(n)}},Jt={canvas:o((t,e)=>new H9(t,e),"canvas"),svg:o((t,e)=>new Y9(t,e),"svg"),generator:o(t=>new gm(t),"generator"),newSeed:o(()=>gm.newSeed(),"newSeed")}});var wSe,Dd,q9=R(()=>{"use strict";wSe=o((t,e)=>{var r=t.x,n=t.y,i=e.x-r,a=e.y-n,s=t.width/2,l=t.height/2,u,h;return Math.abs(a)*s>Math.abs(i)*l?(a<0&&(l=-l),u=a===0?0:l*i/a,h=l):(i<0&&(s=-s),u=s,h=i===0?0:s*a/i),{x:r+u,y:n+h}},"intersectRect"),Dd=wSe});function TSe(t,e){e&&t.attr("style",e)}async function kSe(t){let e=$e(document.createElementNS("http://www.w3.org/2000/svg","foreignObject")),r=e.append("xhtml:div"),n=t.label;t.label&&Ni(t.label)&&(n=await yh(t.label.replace(We.lineBreakRegex,` +`),de()));let i=t.isNode?"nodeLabel":"edgeLabel";return r.html('"+n+""),TSe(r,t.labelStyle),r.style("display","inline-block"),r.style("padding-right","1px"),r.style("white-space","nowrap"),r.attr("xmlns","http://www.w3.org/1999/xhtml"),e.node()}var ESe,gc,H5=R(()=>{"use strict";Zt();ut();_t();rr();xr();o(TSe,"applyStyle");o(kSe,"addHtmlLabel");ESe=o(async(t,e,r,n)=>{let i=t||"";if(typeof i=="object"&&(i=i[0]),yr(de().flowchart.htmlLabels)){i=i.replace(/\\n|\n/g,"
    "),V.info("vertexText"+i);let a={isNode:n,label:to(i).replace(/fa[blrs]?:fa-[\w-]+/g,l=>``),labelStyle:e&&e.replace("fill:","color:")};return await kSe(a)}else{let a=document.createElementNS("http://www.w3.org/2000/svg","text");a.setAttribute("style",e.replace("color:","fill:"));let s=[];typeof i=="string"?s=i.split(/\\n|\n|/gi):Array.isArray(i)?s=i:s=[];for(let l of s){let u=document.createElementNS("http://www.w3.org/2000/svg","tspan");u.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),u.setAttribute("dy","1em"),u.setAttribute("x","0"),r?u.setAttribute("class","title-row"):u.setAttribute("class","row"),u.textContent=l.trim(),a.appendChild(u)}return a}},"createLabel"),gc=ESe});var _u,Sv=R(()=>{"use strict";_u=o((t,e,r,n,i)=>["M",t+i,e,"H",t+r-i,"A",i,i,0,0,1,t+r,e+i,"V",e+n-i,"A",i,i,0,0,1,t+r-i,e+n,"H",t+i,"A",i,i,0,0,1,t,e+n-i,"V",e+i,"A",i,i,0,0,1,t+i,e,"Z"].join(" "),"createRoundedRectPathD")});var Lu,Jj,CSe,Br,Fr,ki=R(()=>{"use strict";_t();Lu=o(t=>{let{handDrawnSeed:e}=de();return{fill:t,hachureAngle:120,hachureGap:4,fillWeight:2,roughness:.7,stroke:t,seed:e}},"solidStateFill"),Jj=o(t=>{let e=CSe([...t.cssCompiledStyles||[],...t.cssStyles||[]]);return{stylesMap:e,stylesArray:[...e]}},"compileStyles"),CSe=o(t=>{let e=new Map;return t.forEach(r=>{let[n,i]=r.split(":");e.set(n.trim(),i?.trim())}),e},"styles2Map"),Br=o(t=>{let{stylesArray:e}=Jj(t),r=[],n=[],i=[],a=[];return e.forEach(s=>{let l=s[0];l==="color"||l==="font-size"||l==="font-family"||l==="font-weight"||l==="font-style"||l==="text-decoration"||l==="text-align"||l==="text-transform"||l==="line-height"||l==="letter-spacing"||l==="word-spacing"||l==="text-shadow"||l==="text-overflow"||l==="white-space"||l==="word-wrap"||l==="word-break"||l==="overflow-wrap"||l==="hyphens"?r.push(s.join(":")+" !important"):(n.push(s.join(":")+" !important"),l.includes("stroke")&&i.push(s.join(":")+" !important"),l==="fill"&&a.push(s.join(":")+" !important"))}),{labelStyles:r.join(";"),nodeStyles:n.join(";"),stylesArray:e,borderStyles:i,backgroundStyles:a}},"styles2String"),Fr=o((t,e)=>{let{themeVariables:r,handDrawnSeed:n}=de(),{nodeBorder:i,mainBkg:a}=r,{stylesMap:s}=Jj(t);return Object.assign({roughness:.7,fill:s.get("fill")||a,fillStyle:"hachure",fillWeight:4,stroke:s.get("stroke")||i,seed:n,strokeWidth:1.3},e)},"userNodeOverrides")});var eK,SSe,ASe,_Se,LSe,DSe,tK,Y5,rK,X9=R(()=>{"use strict";_t();rr();ut();_d();Zt();ti();Al();q9();H5();Sv();ki();eK=o(async(t,e)=>{V.info("Creating subgraph rect for ",e.id,e);let r=de(),{themeVariables:n,handDrawnSeed:i}=r,{clusterBkg:a,clusterBorder:s}=n,{labelStyles:l,nodeStyles:u,borderStyles:h,backgroundStyles:f}=Br(e),d=t.insert("g").attr("class","cluster "+e.cssClasses).attr("id",e.id).attr("data-look",e.look),p=yr(r.flowchart.htmlLabels),m=d.insert("g").attr("class","cluster-label "),g=await ta(m,e.label,{style:e.labelStyle,useHtmlLabels:p,isNode:!0}),y=g.getBBox();if(yr(r.flowchart.htmlLabels)){let _=g.children[0],A=$e(g);y=_.getBoundingClientRect(),A.attr("width",y.width),A.attr("height",y.height)}let v=e.width<=y.width+e.padding?y.width+e.padding:e.width;e.width<=y.width+e.padding?e.diff=(v-e.width)/2-e.padding:e.diff=-e.padding;let x=e.height,b=e.x-v/2,w=e.y-x/2;V.trace("Data ",e,JSON.stringify(e));let S;if(e.look==="handDrawn"){let _=Jt.svg(d),A=Fr(e,{roughness:.7,fill:a,stroke:s,fillWeight:3,seed:i}),L=_.path(_u(b,w,v,x,0),A);S=d.insert(()=>(V.debug("Rough node insert CXC",L),L),":first-child"),S.select("path:nth-child(2)").attr("style",h.join(";")),S.select("path").attr("style",f.join(";").replace("fill","stroke"))}else S=d.insert("rect",":first-child"),S.attr("style",u).attr("rx",e.rx).attr("ry",e.ry).attr("x",b).attr("y",w).attr("width",v).attr("height",x);let{subGraphTitleTopMargin:T}=io(r);if(m.attr("transform",`translate(${e.x-y.width/2}, ${e.y-e.height/2+T})`),l){let _=m.select("span");_&&_.attr("style",l)}let E=S.node().getBBox();return e.offsetX=0,e.width=E.width,e.height=E.height,e.offsetY=y.height-e.padding/2,e.intersect=function(_){return Dd(e,_)},{cluster:d,labelBBox:y}},"rect"),SSe=o((t,e)=>{let r=t.insert("g").attr("class","note-cluster").attr("id",e.id),n=r.insert("rect",":first-child"),i=0*e.padding,a=i/2;n.attr("rx",e.rx).attr("ry",e.ry).attr("x",e.x-e.width/2-a).attr("y",e.y-e.height/2-a).attr("width",e.width+i).attr("height",e.height+i).attr("fill","none");let s=n.node().getBBox();return e.width=s.width,e.height=s.height,e.intersect=function(l){return Dd(e,l)},{cluster:r,labelBBox:{width:0,height:0}}},"noteGroup"),ASe=o(async(t,e)=>{let r=de(),{themeVariables:n,handDrawnSeed:i}=r,{altBackground:a,compositeBackground:s,compositeTitleBackground:l,nodeBorder:u}=n,h=t.insert("g").attr("class",e.cssClasses).attr("id",e.id).attr("data-id",e.id).attr("data-look",e.look),f=h.insert("g",":first-child"),d=h.insert("g").attr("class","cluster-label"),p=h.append("rect"),m=d.node().appendChild(await gc(e.label,e.labelStyle,void 0,!0)),g=m.getBBox();if(yr(r.flowchart.htmlLabels)){let L=m.children[0],M=$e(m);g=L.getBoundingClientRect(),M.attr("width",g.width),M.attr("height",g.height)}let y=0*e.padding,v=y/2,x=(e.width<=g.width+e.padding?g.width+e.padding:e.width)+y;e.width<=g.width+e.padding?e.diff=(x-e.width)/2-e.padding:e.diff=-e.padding;let b=e.height+y,w=e.height+y-g.height-6,S=e.x-x/2,T=e.y-b/2;e.width=x;let E=e.y-e.height/2-v+g.height+2,_;if(e.look==="handDrawn"){let L=e.cssClasses.includes("statediagram-cluster-alt"),M=Jt.svg(h),N=e.rx||e.ry?M.path(_u(S,T,x,b,10),{roughness:.7,fill:l,fillStyle:"solid",stroke:u,seed:i}):M.rectangle(S,T,x,b,{seed:i});_=h.insert(()=>N,":first-child");let k=M.rectangle(S,E,x,w,{fill:L?a:s,fillStyle:L?"hachure":"solid",stroke:u,seed:i});_=h.insert(()=>N,":first-child"),p=h.insert(()=>k)}else _=f.insert("rect",":first-child"),_.attr("class","outer").attr("x",S).attr("y",T).attr("width",x).attr("height",b).attr("data-look",e.look),p.attr("class","inner").attr("x",S).attr("y",E).attr("width",x).attr("height",w);d.attr("transform",`translate(${e.x-g.width/2}, ${T+1-(yr(r.flowchart.htmlLabels)?0:3)})`);let A=_.node().getBBox();return e.height=A.height,e.offsetX=0,e.offsetY=g.height-e.padding/2,e.labelBBox=g,e.intersect=function(L){return Dd(e,L)},{cluster:h,labelBBox:g}},"roundedWithTitle"),_Se=o((t,e)=>{let r=de(),{themeVariables:n,handDrawnSeed:i}=r,{nodeBorder:a}=n,s=t.insert("g").attr("class",e.cssClasses).attr("id",e.id).attr("data-look",e.look),l=s.insert("g",":first-child"),u=0*e.padding,h=e.width+u;e.diff=-e.padding;let f=e.height+u,d=e.x-h/2,p=e.y-f/2;e.width=h;let m;if(e.look==="handDrawn"){let v=Jt.svg(s).rectangle(d,p,h,f,{fill:"lightgrey",roughness:.5,strokeLineDash:[5],stroke:a,seed:i});m=s.insert(()=>v,":first-child")}else m=l.insert("rect",":first-child"),m.attr("class","divider").attr("x",d).attr("y",p).attr("width",h).attr("height",f).attr("data-look",e.look);let g=m.node().getBBox();return e.height=g.height,e.offsetX=0,e.offsetY=0,e.intersect=function(y){return Dd(e,y)},{cluster:s,labelBBox:{}}},"divider"),LSe=eK,DSe={rect:eK,squareRect:LSe,roundedWithTitle:ASe,noteGroup:SSe,divider:_Se},tK=new Map,Y5=o(async(t,e)=>{let r=e.shape||"rect",n=await DSe[r](t,e);return tK.set(e.id,n),n},"insertCluster"),rK=o(()=>{tK=new Map},"clear")});function W5(t,e){if(t===void 0||e===void 0)return{angle:0,deltaX:0,deltaY:0};t=q5(t),e=q5(e);let[r,n]=[t.x,t.y],[i,a]=[e.x,e.y],s=i-r,l=a-n;return{angle:Math.atan(l/s),deltaX:s,deltaY:l}}var Uh,q5,X5,j9=R(()=>{"use strict";Uh={aggregation:18,extension:18,composition:18,dependency:6,lollipop:13.5,arrow_point:4};o(W5,"calculateDeltaAndAngle");q5=o(t=>Array.isArray(t)?{x:t[0],y:t[1]}:t,"pointTransformer"),X5=o(t=>({x:o(function(e,r,n){let i=0;if(r===0&&Object.hasOwn(Uh,t.arrowTypeStart)){let{angle:a,deltaX:s}=W5(n[0],n[1]);i=Uh[t.arrowTypeStart]*Math.cos(a)*(s>=0?1:-1)}else if(r===n.length-1&&Object.hasOwn(Uh,t.arrowTypeEnd)){let{angle:a,deltaX:s}=W5(n[n.length-1],n[n.length-2]);i=Uh[t.arrowTypeEnd]*Math.cos(a)*(s>=0?1:-1)}return q5(e).x+i},"x"),y:o(function(e,r,n){let i=0;if(r===0&&Object.hasOwn(Uh,t.arrowTypeStart)){let{angle:a,deltaY:s}=W5(n[0],n[1]);i=Uh[t.arrowTypeStart]*Math.abs(Math.sin(a))*(s>=0?1:-1)}else if(r===n.length-1&&Object.hasOwn(Uh,t.arrowTypeEnd)){let{angle:a,deltaY:s}=W5(n[n.length-1],n[n.length-2]);i=Uh[t.arrowTypeEnd]*Math.abs(Math.sin(a))*(s>=0?1:-1)}return q5(e).y+i},"y")}),"getLineFunctionsWithOffset")});var iK,RSe,nK,aK=R(()=>{"use strict";ut();iK=o((t,e,r,n,i)=>{e.arrowTypeStart&&nK(t,"start",e.arrowTypeStart,r,n,i),e.arrowTypeEnd&&nK(t,"end",e.arrowTypeEnd,r,n,i)},"addEdgeMarkers"),RSe={arrow_cross:"cross",arrow_point:"point",arrow_barb:"barb",arrow_circle:"circle",aggregation:"aggregation",extension:"extension",composition:"composition",dependency:"dependency",lollipop:"lollipop"},nK=o((t,e,r,n,i,a)=>{let s=RSe[r];if(!s){V.warn(`Unknown arrow type: ${r}`);return}let l=e==="start"?"Start":"End";t.attr(`marker-${e}`,`url(${n}#${i}_${a}-${s}${l})`)},"addEdgeMarker")});function j5(t,e){de().flowchart.htmlLabels&&t&&(t.style.width=e.length*9+"px",t.style.height="12px")}function ISe(t){let e=[],r=[];for(let n=1;n5&&Math.abs(a.y-i.y)>5||i.y===a.y&&a.x===s.x&&Math.abs(a.x-i.x)>5&&Math.abs(a.y-s.y)>5)&&(e.push(a),r.push(n))}return{cornerPoints:e,cornerPointPositions:r}}var K5,da,lK,Av,Q5,Z5,NSe,MSe,sK,oK,OSe,J5,K9=R(()=>{"use strict";_t();rr();ut();Al();xr();j9();_d();Zt();ti();H5();aK();K5=new Map,da=new Map,lK=o(()=>{K5.clear(),da.clear()},"clear"),Av=o(t=>t?t.reduce((r,n)=>r+";"+n,""):"","getLabelStyles"),Q5=o(async(t,e)=>{let r=yr(de().flowchart.htmlLabels),n=await ta(t,e.label,{style:Av(e.labelStyle),useHtmlLabels:r,addSvgBackground:!0,isNode:!1});V.info("abc82",e,e.labelType);let i=t.insert("g").attr("class","edgeLabel"),a=i.insert("g").attr("class","label");a.node().appendChild(n);let s=n.getBBox();if(r){let u=n.children[0],h=$e(n);s=u.getBoundingClientRect(),h.attr("width",s.width),h.attr("height",s.height)}a.attr("transform","translate("+-s.width/2+", "+-s.height/2+")"),K5.set(e.id,i),e.width=s.width,e.height=s.height;let l;if(e.startLabelLeft){let u=await gc(e.startLabelLeft,Av(e.labelStyle)),h=t.insert("g").attr("class","edgeTerminals"),f=h.insert("g").attr("class","inner");l=f.node().appendChild(u);let d=u.getBBox();f.attr("transform","translate("+-d.width/2+", "+-d.height/2+")"),da.get(e.id)||da.set(e.id,{}),da.get(e.id).startLeft=h,j5(l,e.startLabelLeft)}if(e.startLabelRight){let u=await gc(e.startLabelRight,Av(e.labelStyle)),h=t.insert("g").attr("class","edgeTerminals"),f=h.insert("g").attr("class","inner");l=h.node().appendChild(u),f.node().appendChild(u);let d=u.getBBox();f.attr("transform","translate("+-d.width/2+", "+-d.height/2+")"),da.get(e.id)||da.set(e.id,{}),da.get(e.id).startRight=h,j5(l,e.startLabelRight)}if(e.endLabelLeft){let u=await gc(e.endLabelLeft,Av(e.labelStyle)),h=t.insert("g").attr("class","edgeTerminals"),f=h.insert("g").attr("class","inner");l=f.node().appendChild(u);let d=u.getBBox();f.attr("transform","translate("+-d.width/2+", "+-d.height/2+")"),h.node().appendChild(u),da.get(e.id)||da.set(e.id,{}),da.get(e.id).endLeft=h,j5(l,e.endLabelLeft)}if(e.endLabelRight){let u=await gc(e.endLabelRight,Av(e.labelStyle)),h=t.insert("g").attr("class","edgeTerminals"),f=h.insert("g").attr("class","inner");l=f.node().appendChild(u);let d=u.getBBox();f.attr("transform","translate("+-d.width/2+", "+-d.height/2+")"),h.node().appendChild(u),da.get(e.id)||da.set(e.id,{}),da.get(e.id).endRight=h,j5(l,e.endLabelRight)}return n},"insertEdgeLabel");o(j5,"setTerminalWidth");Z5=o((t,e)=>{V.debug("Moving label abc88 ",t.id,t.label,K5.get(t.id),e);let r=e.updatedPath?e.updatedPath:e.originalPath,n=de(),{subGraphTitleTotalMargin:i}=io(n);if(t.label){let a=K5.get(t.id),s=t.x,l=t.y;if(r){let u=Lt.calcLabelPosition(r);V.debug("Moving label "+t.label+" from (",s,",",l,") to (",u.x,",",u.y,") abc88"),e.updatedPath&&(s=u.x,l=u.y)}a.attr("transform",`translate(${s}, ${l+i/2})`)}if(t.startLabelLeft){let a=da.get(t.id).startLeft,s=t.x,l=t.y;if(r){let u=Lt.calcTerminalLabelPosition(t.arrowTypeStart?10:0,"start_left",r);s=u.x,l=u.y}a.attr("transform",`translate(${s}, ${l})`)}if(t.startLabelRight){let a=da.get(t.id).startRight,s=t.x,l=t.y;if(r){let u=Lt.calcTerminalLabelPosition(t.arrowTypeStart?10:0,"start_right",r);s=u.x,l=u.y}a.attr("transform",`translate(${s}, ${l})`)}if(t.endLabelLeft){let a=da.get(t.id).endLeft,s=t.x,l=t.y;if(r){let u=Lt.calcTerminalLabelPosition(t.arrowTypeEnd?10:0,"end_left",r);s=u.x,l=u.y}a.attr("transform",`translate(${s}, ${l})`)}if(t.endLabelRight){let a=da.get(t.id).endRight,s=t.x,l=t.y;if(r){let u=Lt.calcTerminalLabelPosition(t.arrowTypeEnd?10:0,"end_right",r);s=u.x,l=u.y}a.attr("transform",`translate(${s}, ${l})`)}},"positionEdgeLabel"),NSe=o((t,e)=>{let r=t.x,n=t.y,i=Math.abs(e.x-r),a=Math.abs(e.y-n),s=t.width/2,l=t.height/2;return i>=s||a>=l},"outsideNode"),MSe=o((t,e,r)=>{V.debug(`intersection calc abc89: + outsidePoint: ${JSON.stringify(e)} + insidePoint : ${JSON.stringify(r)} + node : x:${t.x} y:${t.y} w:${t.width} h:${t.height}`);let n=t.x,i=t.y,a=Math.abs(n-r.x),s=t.width/2,l=r.xMath.abs(n-e.x)*u){let d=r.y{V.warn("abc88 cutPathAtIntersect",t,e);let r=[],n=t[0],i=!1;return t.forEach(a=>{if(V.info("abc88 checking point",a,e),!NSe(e,a)&&!i){let s=MSe(e,n,a);V.debug("abc88 inside",a,n,s),V.debug("abc88 intersection",s,e);let l=!1;r.forEach(u=>{l=l||u.x===s.x&&u.y===s.y}),r.some(u=>u.x===s.x&&u.y===s.y)?V.warn("abc88 no intersect",s,r):r.push(s),i=!0}else V.warn("abc88 outside",a,n),n=a,i||r.push(a)}),V.debug("returning points",r),r},"cutPathAtIntersect");o(ISe,"extractCornerPoints");oK=o(function(t,e,r){let n=e.x-t.x,i=e.y-t.y,a=Math.sqrt(n*n+i*i),s=r/a;return{x:e.x-s*n,y:e.y-s*i}},"findAdjacentPoint"),OSe=o(function(t){let{cornerPointPositions:e}=ISe(t),r=[];for(let n=0;n10&&Math.abs(a.y-i.y)>=10){V.debug("Corner point fixing",Math.abs(a.x-i.x),Math.abs(a.y-i.y));let m=5;s.x===l.x?p={x:h<0?l.x-m+d:l.x+m-d,y:f<0?l.y-d:l.y+d}:p={x:h<0?l.x-d:l.x+d,y:f<0?l.y-m+d:l.y+m-d}}else V.debug("Corner point skipping fixing",Math.abs(a.x-i.x),Math.abs(a.y-i.y));r.push(p,u)}else r.push(t[n]);return r},"fixCorners"),J5=o(function(t,e,r,n,i,a,s){let{handDrawnSeed:l}=de(),u=e.points,h=!1,f=i;var d=a;d.intersect&&f.intersect&&(u=u.slice(1,e.points.length-1),u.unshift(f.intersect(u[0])),V.debug("Last point APA12",e.start,"-->",e.end,u[u.length-1],d,d.intersect(u[u.length-1])),u.push(d.intersect(u[u.length-1]))),e.toCluster&&(V.info("to cluster abc88",r.get(e.toCluster)),u=sK(e.points,r.get(e.toCluster).node),h=!0),e.fromCluster&&(V.debug("from cluster abc88",r.get(e.fromCluster),JSON.stringify(u,null,2)),u=sK(u.reverse(),r.get(e.fromCluster).node).reverse(),h=!0);let p=u.filter(A=>!Number.isNaN(A.y));p=OSe(p);let m=p[p.length-1];if(p.length>1){m=p[p.length-1];let A=p[p.length-2],L=(m.x-A.x)/2,M=(m.y-A.y)/2,N={x:A.x+L,y:A.y+M};p.splice(-1,0,N)}let g=vs;e.curve&&(g=e.curve);let{x:y,y:v}=X5(e),x=ha().x(y).y(v).curve(g),b;switch(e.thickness){case"normal":b="edge-thickness-normal";break;case"thick":b="edge-thickness-thick";break;case"invisible":b="edge-thickness-invisible";break;default:b="edge-thickness-normal"}switch(e.pattern){case"solid":b+=" edge-pattern-solid";break;case"dotted":b+=" edge-pattern-dotted";break;case"dashed":b+=" edge-pattern-dashed";break;default:b+=" edge-pattern-solid"}let w,S=x(p),T=Array.isArray(e.style)?e.style:[e.style];if(e.look==="handDrawn"){let A=Jt.svg(t);Object.assign([],p);let L=A.path(S,{roughness:.3,seed:l});b+=" transition",w=$e(L).select("path").attr("id",e.id).attr("class"," "+b+(e.classes?" "+e.classes:"")).attr("style",T?T.reduce((N,k)=>N+";"+k,""):"");let M=w.attr("d");w.attr("d",M),t.node().appendChild(w.node())}else w=t.append("path").attr("d",S).attr("id",e.id).attr("class"," "+b+(e.classes?" "+e.classes:"")).attr("style",T?T.reduce((A,L)=>A+";"+L,""):"");let E="";(de().flowchart.arrowMarkerAbsolute||de().state.arrowMarkerAbsolute)&&(E=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search,E=E.replace(/\(/g,"\\(").replace(/\)/g,"\\)")),V.info("arrowTypeStart",e.arrowTypeStart),V.info("arrowTypeEnd",e.arrowTypeEnd),iK(w,e,E,s,n);let _={};return h&&(_.updatedPath=u),_.originalPath=e.points,_},"insertEdge")});var PSe,BSe,FSe,zSe,GSe,$Se,VSe,USe,HSe,YSe,WSe,ew,Q9=R(()=>{"use strict";ut();PSe=o((t,e,r,n)=>{e.forEach(i=>{WSe[i](t,r,n)})},"insertMarkers"),BSe=o((t,e,r)=>{V.trace("Making markers for ",r),t.append("defs").append("marker").attr("id",r+"_"+e+"-extensionStart").attr("class","marker extension "+e).attr("refX",18).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 1,7 L18,13 V 1 Z"),t.append("defs").append("marker").attr("id",r+"_"+e+"-extensionEnd").attr("class","marker extension "+e).attr("refX",1).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 1,1 V 13 L18,7 Z")},"extension"),FSe=o((t,e,r)=>{t.append("defs").append("marker").attr("id",r+"_"+e+"-compositionStart").attr("class","marker composition "+e).attr("refX",18).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",r+"_"+e+"-compositionEnd").attr("class","marker composition "+e).attr("refX",1).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z")},"composition"),zSe=o((t,e,r)=>{t.append("defs").append("marker").attr("id",r+"_"+e+"-aggregationStart").attr("class","marker aggregation "+e).attr("refX",18).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",r+"_"+e+"-aggregationEnd").attr("class","marker aggregation "+e).attr("refX",1).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z")},"aggregation"),GSe=o((t,e,r)=>{t.append("defs").append("marker").attr("id",r+"_"+e+"-dependencyStart").attr("class","marker dependency "+e).attr("refX",6).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 5,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",r+"_"+e+"-dependencyEnd").attr("class","marker dependency "+e).attr("refX",13).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},"dependency"),$Se=o((t,e,r)=>{t.append("defs").append("marker").attr("id",r+"_"+e+"-lollipopStart").attr("class","marker lollipop "+e).attr("refX",13).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("circle").attr("stroke","black").attr("fill","transparent").attr("cx",7).attr("cy",7).attr("r",6),t.append("defs").append("marker").attr("id",r+"_"+e+"-lollipopEnd").attr("class","marker lollipop "+e).attr("refX",1).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("circle").attr("stroke","black").attr("fill","transparent").attr("cx",7).attr("cy",7).attr("r",6)},"lollipop"),VSe=o((t,e,r)=>{t.append("marker").attr("id",r+"_"+e+"-pointEnd").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",5).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",8).attr("markerHeight",8).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),t.append("marker").attr("id",r+"_"+e+"-pointStart").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",4.5).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",8).attr("markerHeight",8).attr("orient","auto").append("path").attr("d","M 0 5 L 10 10 L 10 0 z").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0")},"point"),USe=o((t,e,r)=>{t.append("marker").attr("id",r+"_"+e+"-circleEnd").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",11).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),t.append("marker").attr("id",r+"_"+e+"-circleStart").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",-1).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0")},"circle"),HSe=o((t,e,r)=>{t.append("marker").attr("id",r+"_"+e+"-crossEnd").attr("class","marker cross "+e).attr("viewBox","0 0 11 11").attr("refX",12).attr("refY",5.2).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("path").attr("d","M 1,1 l 9,9 M 10,1 l -9,9").attr("class","arrowMarkerPath").style("stroke-width",2).style("stroke-dasharray","1,0"),t.append("marker").attr("id",r+"_"+e+"-crossStart").attr("class","marker cross "+e).attr("viewBox","0 0 11 11").attr("refX",-1).attr("refY",5.2).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("path").attr("d","M 1,1 l 9,9 M 10,1 l -9,9").attr("class","arrowMarkerPath").style("stroke-width",2).style("stroke-dasharray","1,0")},"cross"),YSe=o((t,e,r)=>{t.append("defs").append("marker").attr("id",r+"_"+e+"-barbEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",14).attr("markerUnits","userSpaceOnUse").attr("orient","auto").append("path").attr("d","M 19,7 L9,13 L14,7 L9,1 Z")},"barb"),WSe={extension:BSe,composition:FSe,aggregation:zSe,dependency:GSe,lollipop:$Se,point:VSe,circle:USe,cross:HSe,barb:YSe},ew=PSe});var zr,ar,En,ri=R(()=>{"use strict";Al();_t();Zt();rr();xr();zr=o(async(t,e,r)=>{let n,i=e.useHtmlLabels||yr(de().flowchart.htmlLabels);r?n=r:n="node default";let a=t.insert("g").attr("class",n).attr("id",e.domId||e.id),s=a.insert("g").attr("class","label").attr("style",e.labelStyle),l;e.label===void 0?l="":l=typeof e.label=="string"?e.label:e.label[0];let u;u=await ta(s,qr(to(l),de()),{useHtmlLabels:i,width:e.width||de().flowchart.wrappingWidth,cssClasses:"markdown-node-label",style:e.labelStyle});let h=u.getBBox(),f=e.padding/2;if(yr(de().flowchart.htmlLabels)){let d=u.children[0],p=$e(u),m=d.getElementsByTagName("img");if(m){let g=l.replace(/]*>/g,"").trim()==="";await Promise.all([...m].map(y=>new Promise(v=>{function x(){if(y.style.display="flex",y.style.flexDirection="column",g){let b=de().fontSize?de().fontSize:window.getComputedStyle(document.body).fontSize,S=parseInt(b,10)*5+"px";y.style.minWidth=S,y.style.maxWidth=S}else y.style.width="100%";v(y)}o(x,"setupImage"),setTimeout(()=>{y.complete&&x()}),y.addEventListener("error",x),y.addEventListener("load",x)})))}h=d.getBoundingClientRect(),p.attr("width",h.width),p.attr("height",h.height)}return i?s.attr("transform","translate("+-h.width/2+", "+-h.height/2+")"):s.attr("transform","translate(0, "+-h.height/2+")"),e.centerLabel&&s.attr("transform","translate("+-h.width/2+", "+-h.height/2+")"),s.insert("rect",":first-child"),{shapeSvg:a,bbox:h,halfPadding:f,label:s}},"labelHelper"),ar=o((t,e)=>{let r=e.node().getBBox();t.width=r.width,t.height=r.height},"updateNodeBounds"),En=o((t,e)=>(t.look==="handDrawn"?"rough-node":"node")+" "+t.cssClasses+" "+(e||""),"getNodeClasses")});function qSe(t,e){return t.intersect(e)}var cK,uK=R(()=>{"use strict";o(qSe,"intersectNode");cK=qSe});function XSe(t,e,r,n){var i=t.x,a=t.y,s=i-n.x,l=a-n.y,u=Math.sqrt(e*e*l*l+r*r*s*s),h=Math.abs(e*r*s/u);n.x{"use strict";o(XSe,"intersectEllipse");tw=XSe});function jSe(t,e,r){return tw(t,e,e,r)}var hK,fK=R(()=>{"use strict";Z9();o(jSe,"intersectCircle");hK=jSe});function KSe(t,e,r,n){var i,a,s,l,u,h,f,d,p,m,g,y,v,x,b;if(i=e.y-t.y,s=t.x-e.x,u=e.x*t.y-t.x*e.y,p=i*r.x+s*r.y+u,m=i*n.x+s*n.y+u,!(p!==0&&m!==0&&dK(p,m))&&(a=n.y-r.y,l=r.x-n.x,h=n.x*r.y-r.x*n.y,f=a*t.x+l*t.y+h,d=a*e.x+l*e.y+h,!(f!==0&&d!==0&&dK(f,d))&&(g=i*l-a*s,g!==0)))return y=Math.abs(g/2),v=s*h-l*u,x=v<0?(v-y)/g:(v+y)/g,v=a*u-i*h,b=v<0?(v-y)/g:(v+y)/g,{x,y:b}}function dK(t,e){return t*e>0}var pK,mK=R(()=>{"use strict";o(KSe,"intersectLine");o(dK,"sameSign");pK=KSe});function QSe(t,e,r){let n=t.x,i=t.y,a=[],s=Number.POSITIVE_INFINITY,l=Number.POSITIVE_INFINITY;typeof e.forEach=="function"?e.forEach(function(f){s=Math.min(s,f.x),l=Math.min(l,f.y)}):(s=Math.min(s,e.x),l=Math.min(l,e.y));let u=n-t.width/2-s,h=i-t.height/2-l;for(let f=0;f1&&a.sort(function(f,d){let p=f.x-r.x,m=f.y-r.y,g=Math.sqrt(p*p+m*m),y=d.x-r.x,v=d.y-r.y,x=Math.sqrt(y*y+v*v);return g{"use strict";mK();o(QSe,"intersectPolygon");gK=QSe});var sr,hi=R(()=>{"use strict";uK();fK();Z9();yK();q9();sr={node:cK,circle:hK,ellipse:tw,polygon:gK,rect:Dd}});var Rd,_v=R(()=>{"use strict";ri();hi();Sv();ki();ti();Rd=o(async(t,e,r)=>{let{labelStyles:n,nodeStyles:i}=Br(e);e.labelStyle=n;let{shapeSvg:a,bbox:s}=await zr(t,e,En(e)),l=Math.max(s.width+r.labelPaddingX*2,e?.width||0),u=Math.max(s.height+r.labelPaddingY*2,e?.height||0),h=-l/2,f=-u/2,d,{rx:p,ry:m}=e,{cssStyles:g}=e;if(r?.rx&&r.ry&&(p=r.rx,m=r.ry),e.look==="handDrawn"){let y=Jt.svg(a),v=Fr(e,{}),x=p||m?y.path(_u(h,f,l,u,p||0),v):y.rectangle(h,f,l,u,v);d=a.insert(()=>x,":first-child"),d.attr("class","basic label-container").attr("style",g)}else d=a.insert("rect",":first-child"),d.attr("class","basic label-container").attr("style",i).attr("rx",p).attr("data-id","abc").attr("data-et","node").attr("ry",m).attr("x",h).attr("y",f).attr("width",l).attr("height",u);return ar(e,d),e.intersect=function(y){return sr.rect(e,y)},a},"drawRect")});var vK,xK=R(()=>{"use strict";_v();vK=o(async(t,e)=>Rd(t,e,{rx:5,ry:5,classes:"flowchart-node"}),"state")});var bK,wK=R(()=>{"use strict";_v();bK=o(async(t,e)=>{let r={rx:5,ry:5,classes:"",labelPaddingX:(e?.padding||0)*1,labelPaddingY:(e?.padding||0)*1};return Rd(t,e,r)},"roundedRect")});var TK,kK=R(()=>{"use strict";_v();TK=o(async(t,e)=>{let r={rx:0,ry:0,classes:"",labelPaddingX:(e?.padding||0)*2,labelPaddingY:(e?.padding||0)*1};return Rd(t,e,r)},"squareRect")});var EK,CK=R(()=>{"use strict";ri();hi();ti();ki();_t();EK=o((t,e)=>{let{themeVariables:r}=de(),{lineColor:n}=r,i=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),a;if(e.look==="handDrawn"){let l=Jt.svg(i).circle(0,0,14,Lu(n));a=i.insert(()=>l)}else a=i.insert("circle",":first-child");return a.attr("class","state-start").attr("r",7).attr("width",14).attr("height",14),ar(e,a),e.intersect=function(s){return sr.circle(e,7,s)},i},"stateStart")});var SK,AK=R(()=>{"use strict";ri();hi();ti();ki();_t();SK=o((t,e)=>{let{themeVariables:r}=de(),{lineColor:n}=r,i=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),a,s;if(e.look==="handDrawn"){let l=Jt.svg(i),u=l.circle(0,0,14,{...Lu(n),roughness:.5}),h=l.circle(0,0,5,{...Lu(n),fillStyle:"solid"});a=i.insert(()=>u),s=i.insert(()=>h)}else s=i.insert("circle",":first-child"),a=i.insert("circle",":first-child"),a.attr("class","state-start").attr("r",7).attr("width",14).attr("height",14),s.attr("class","state-end").attr("r",5).attr("width",10).attr("height",10);return ar(e,a),e.intersect=function(l){return sr.circle(e,7,l)},i},"stateEnd")});var J9,_K=R(()=>{"use strict";ri();hi();ti();ki();_t();J9=o((t,e,r)=>{let{themeVariables:n}=de(),{lineColor:i}=n,a=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),s=70,l=10;r==="LR"&&(s=10,l=70);let u=-1*s/2,h=-1*l/2,f;if(e.look==="handDrawn"){let y=Jt.svg(a).rectangle(u,h,s,l,Lu(i));f=a.insert(()=>y)}else f=a.append("rect").attr("x",u).attr("y",h).attr("width",s).attr("height",l).attr("class","fork-join");ar(e,f);let d=0,p=0,m=10;return e.height&&(d=e.height),e.width&&(p=e.width),e.padding&&(m=e.padding),e.height=d+m/2,e.width=p+m/2,e.intersect=function(g){return sr.rect(e,g)},a},"forkJoin")});var LK,DK=R(()=>{"use strict";hi();ti();ki();_t();LK=o((t,e)=>{let{labelStyles:r,nodeStyles:n}=Br(e);e.labelStyle=r;let{themeVariables:i}=de(),{lineColor:a}=i,s=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),l=28,u=[{x:0,y:l/2},{x:l/2,y:0},{x:0,y:-l/2},{x:-l/2,y:0}],h;if(e.look==="handDrawn"){let f=Jt.svg(s),d=u.map(function(m){return[m.x,m.y]}),p=f.polygon(d,Lu(a));h=s.insert(()=>p)}else h=s.insert("polygon",":first-child").attr("points",u.map(function(f){return f.x+","+f.y}).join(" "));return h.attr("class","state-start").attr("r",7).attr("width",28).attr("height",28).attr("style",n),e.width=28,e.height=28,e.intersect=function(f){return sr.circle(e,14,f)},s},"choice")});var RK,NK=R(()=>{"use strict";ut();ri();hi();_t();ti();RK=o(async(t,e)=>{let{themeVariables:r,handDrawnSeed:n}=de(),{noteBorderColor:i,noteBkgColor:a}=r;e.useHtmlLabels||(e.centerLabel=!0);let{shapeSvg:l,bbox:u}=await zr(t,e,"node "+e.cssClasses);V.info("Classes = ",e.cssClasses);let{cssStyles:h}=e,f,d=u.width+e.padding,p=u.height+e.padding,m=-d/2,g=-p/2;if(e.look==="handDrawn"){let v=Jt.svg(l).rectangle(m,g,d,p,{roughness:.7,fill:a,fillWeight:3,seed:n,stroke:i});f=l.insert(()=>v,":first-child"),f.attr("class","basic label-container").attr("style",h)}else f=l.insert("rect",":first-child"),f.attr("rx",e.rx).attr("ry",e.ry).attr("x",m).attr("y",g).attr("width",d).attr("height",p);return ar(e,f),e.intersect=function(y){return sr.rect(e,y)},l},"note")});var MK,IK=R(()=>{"use strict";ri();hi();ki();ti();Sv();MK=o(async(t,e)=>{let{labelStyles:r,nodeStyles:n}=Br(e);e.labelStyle=r;let{shapeSvg:i,bbox:a}=await zr(t,e,En(e)),s=a.height+e.padding,l=a.width+s/4+e.padding,u,{cssStyles:h}=e;if(e.look==="handDrawn"){let f=Jt.svg(i),d=Fr(e,{}),p=_u(-l/2,-s/2,l,s,s/2),m=f.path(p,d);u=i.insert(()=>m,":first-child"),u.attr("class","basic label-container").attr("style",h)}else u=i.insert("rect",":first-child"),u.attr("class","basic label-container").attr("style",n).attr("rx",s/2).attr("ry",s/2).attr("x",-l/2).attr("y",-s/2).attr("width",l).attr("height",s);return ar(e,u),e.intersect=function(f){return sr.rect(e,f)},i},"stadium")});var OK,PK=R(()=>{"use strict";Zt();rr();ri();H5();hi();ki();ti();_t();Sv();ut();OK=o(async(t,e)=>{let{labelStyles:r,nodeStyles:n}=Br(e);e.labelStyle=r;let i;e.cssClasses?i="node "+e.cssClasses:i="node default";let a=t.insert("g").attr("class",i).attr("id",e.domId||e.id),s=a.insert("g"),l=a.insert("g").attr("class","label").attr("style",n),u=e.description,h=e.label,f=l.node().appendChild(await gc(h,e.labelStyle,!0,!0)),d={width:0,height:0};if(yr(de()?.flowchart?.htmlLabels)){let A=f.children[0],L=$e(f);d=A.getBoundingClientRect(),L.attr("width",d.width),L.attr("height",d.height)}V.info("Text 2",u);let p=u||[],m=f.getBBox(),g=l.node().appendChild(await gc(p.join?p.join("
    "):p,e.labelStyle,!0,!0)),y=g.children[0],v=$e(g);d=y.getBoundingClientRect(),v.attr("width",d.width),v.attr("height",d.height);let x=(e.padding||0)/2;$e(g).attr("transform","translate( "+(d.width>m.width?0:(m.width-d.width)/2)+", "+(m.height+x+5)+")"),$e(f).attr("transform","translate( "+(d.width(V.debug("Rough node insert CXC",M),N),":first-child"),E=a.insert(()=>(V.debug("Rough node insert CXC",M),M),":first-child")}else E=s.insert("rect",":first-child"),_=s.insert("line"),E.attr("class","outer title-state").attr("style",n).attr("x",-d.width/2-x).attr("y",-d.height/2-x).attr("width",d.width+(e.padding||0)).attr("height",d.height+(e.padding||0)),_.attr("class","divider").attr("x1",-d.width/2-x).attr("x2",d.width/2+x).attr("y1",-d.height/2-x+m.height+x).attr("y2",-d.height/2-x+m.height+x);return ar(e,E),e.intersect=function(A){return sr.rect(e,A)},a},"rectWithTitle")});function Ma(t,e,r,n){return t.insert("polygon",":first-child").attr("points",n.map(function(i){return i.x+","+i.y}).join(" ")).attr("class","label-container").attr("transform","translate("+-e/2+","+r/2+")")}var Du=R(()=>{"use strict";o(Ma,"insertPolygonShape")});var BK,FK=R(()=>{"use strict";ri();hi();ki();ti();Du();BK=o(async(t,e)=>{let{labelStyles:r,nodeStyles:n}=Br(e);e.labelStyle=r;let{shapeSvg:i,bbox:a}=await zr(t,e,En(e)),s=(e?.padding||0)/2,l=a.width+e.padding,u=a.height+e.padding,h=-a.width/2-s,f=-a.height/2-s,d=[{x:0,y:0},{x:l,y:0},{x:l,y:-u},{x:0,y:-u},{x:0,y:0},{x:-8,y:0},{x:l+8,y:0},{x:l+8,y:-u},{x:-8,y:-u},{x:-8,y:0}];if(e.look==="handDrawn"){let p=Jt.svg(i),m=Fr(e,{}),g=p.rectangle(h-8,f,l+16,u,m),y=p.line(h,f,h,f+u,m),v=p.line(h+l,f,h+l,f+u,m);i.insert(()=>y,":first-child"),i.insert(()=>v,":first-child");let x=i.insert(()=>g,":first-child"),{cssStyles:b}=e;x.attr("class","basic label-container").attr("style",b),ar(e,x)}else{let p=Ma(i,l,u,d);n&&p.attr("style",n),ar(e,p)}return e.intersect=function(p){return sr.polygon(e,d,p)},i},"subroutine")});var ZSe,JSe,eAe,zK,GK=R(()=>{"use strict";ri();hi();ki();ti();ZSe=o((t,e,r,n,i,a)=>[`M${t},${e+a}`,`a${i},${a} 0,0,0 ${r},0`,`a${i},${a} 0,0,0 ${-r},0`,`l0,${n}`,`a${i},${a} 0,0,0 ${r},0`,`l0,${-n}`].join(" "),"createCylinderPathD"),JSe=o((t,e,r,n,i,a)=>[`M${t},${e+a}`,`M${t+r},${e+a}`,`a${i},${a} 0,0,0 ${-r},0`,`l0,${n}`,`a${i},${a} 0,0,0 ${r},0`,`l0,${-n}`].join(" "),"createOuterCylinderPathD"),eAe=o((t,e,r,n,i,a)=>[`M${t-r/2},${-n/2}`,`a${i},${a} 0,0,0 ${r},0`].join(" "),"createInnerCylinderPathD"),zK=o(async(t,e)=>{let{labelStyles:r,nodeStyles:n}=Br(e);e.labelStyle=r;let{shapeSvg:i,bbox:a}=await zr(t,e,En(e)),s=a.width+e.padding,l=s/2,u=l/(2.5+s/50),h=a.height+u+e.padding,f,{cssStyles:d}=e;if(e.look==="handDrawn"){let p=Jt.svg(i),m=JSe(0,0,s,h,l,u),g=eAe(0,u,s,h,l,u),y=p.path(m,Fr(e,{})),v=p.path(g,Fr(e,{fill:"none"}));f=i.insert(()=>v,":first-child"),f=i.insert(()=>y,":first-child"),f.attr("class","basic label-container"),d&&f.attr("style",d)}else{let p=ZSe(0,0,s,h,l,u);f=i.insert("path",":first-child").attr("d",p).attr("class","basic label-container").attr("style",d).attr("style",n)}return f.attr("label-offset-y",u),f.attr("transform",`translate(${-s/2}, ${-(h/2+u)})`),ar(e,f),e.intersect=function(p){let m=sr.rect(e,p),g=m.x-(e.x??0);if(l!=0&&(Math.abs(g)<(e.width??0)/2||Math.abs(g)==(e.width??0)/2&&Math.abs(m.y-(e.y??0))>(e.height??0)/2-u)){let y=u*u*(1-g*g/(l*l));y>0&&(y=Math.sqrt(y)),y=u-y,p.y-(e.y??0)>0&&(y=-y),m.y+=y}return m},i},"cylinder")});var $K,VK=R(()=>{"use strict";ut();ri();hi();ki();ti();$K=o(async(t,e)=>{let{labelStyles:r,nodeStyles:n}=Br(e);e.labelStyle=r;let{shapeSvg:i,bbox:a,halfPadding:s}=await zr(t,e,En(e)),l=a.width/2+s,u,{cssStyles:h}=e;if(e.look==="handDrawn"){let f=Jt.svg(i),d=Fr(e,{}),p=f.circle(0,0,l*2,d);u=i.insert(()=>p,":first-child"),u.attr("class","basic label-container").attr("style",h)}else u=i.insert("circle",":first-child").attr("class","basic label-container").attr("style",n).attr("r",l).attr("cx",0).attr("cy",0);return ar(e,u),e.intersect=function(f){return V.info("Circle intersect",e,l,f),sr.circle(e,l,f)},i},"circle")});var UK,HK=R(()=>{"use strict";ut();ri();hi();ki();ti();UK=o(async(t,e)=>{let{labelStyles:r,nodeStyles:n}=Br(e);e.labelStyle=r;let{shapeSvg:i,bbox:a,halfPadding:s}=await zr(t,e,En(e)),u=a.width/2+s+5,h=a.width/2+s,f,{cssStyles:d}=e;if(e.look==="handDrawn"){let p=Jt.svg(i),m=Fr(e,{roughness:.2,strokeWidth:2.5}),g=Fr(e,{roughness:.2,strokeWidth:1.5}),y=p.circle(0,0,u*2,m),v=p.circle(0,0,h*2,g);f=i.insert("g",":first-child"),f.attr("class",e.cssClasses).attr("style",d),f.node()?.appendChild(y),f.node()?.appendChild(v)}else{f=i.insert("g",":first-child");let p=f.insert("circle",":first-child"),m=f.insert("circle");f.attr("class","basic label-container").attr("style",n),p.attr("class","outer-circle").attr("style",n).attr("r",u).attr("cx",0).attr("cy",0),m.attr("class","inner-circle").attr("style",n).attr("r",h).attr("cx",0).attr("cy",0)}return ar(e,f),e.intersect=function(p){return V.info("DoubleCircle intersect",e,u,p),sr.circle(e,u,p)},i},"doublecircle")});var tAe,YK,WK=R(()=>{"use strict";ri();hi();ki();ti();Du();tAe=o((t,e,r,n)=>[`M${t-n/2},${e}`,`L${t+r},${e}`,`L${t+r},${e-n}`,`L${t-n/2},${e-n}`,`L${t},${e-n/2}`,"Z"].join(" "),"createPolygonPathD"),YK=o(async(t,e)=>{let{labelStyles:r,nodeStyles:n}=Br(e);e.labelStyle=r;let{shapeSvg:i,bbox:a}=await zr(t,e,En(e)),s=a.width+e.padding,l=a.height+e.padding,u=[{x:-l/2,y:0},{x:s,y:0},{x:s,y:-l},{x:-l/2,y:-l},{x:0,y:-l/2}],h,{cssStyles:f}=e;if(e.look==="handDrawn"){let d=Jt.svg(i),p=Fr(e,{}),m=tAe(0,0,s,l),g=d.path(m,p);h=i.insert(()=>g,":first-child").attr("transform",`translate(${-s/2}, ${l/2})`),f&&h.attr("style",f)}else h=Ma(i,s,l,u);return n&&h.attr("style",n),e.width=s+l,e.height=l,ar(e,h),e.intersect=function(d){return sr.polygon(e,u,d)},i},"rect_left_inv_arrow")});var rAe,qK,XK=R(()=>{"use strict";ut();ri();hi();ki();ti();Du();rAe=o((t,e,r)=>[`M${t+r/2},${e}`,`L${t+r},${e-r/2}`,`L${t+r/2},${e-r}`,`L${t},${e-r/2}`,"Z"].join(" "),"createDecisionBoxPathD"),qK=o(async(t,e)=>{let{labelStyles:r,nodeStyles:n}=Br(e);e.labelStyle=r;let{shapeSvg:i,bbox:a}=await zr(t,e,En(e)),s=a.width+e.padding,l=a.height+e.padding,u=s+l,h=[{x:u/2,y:0},{x:u,y:-u/2},{x:u/2,y:-u},{x:0,y:-u/2}],f,{cssStyles:d}=e;if(e.look==="handDrawn"){let p=Jt.svg(i),m=Fr(e,{}),g=rAe(0,0,u),y=p.path(g,m);f=i.insert(()=>y,":first-child").attr("transform",`translate(${-u/2}, ${u/2})`),d&&f.attr("style",d)}else f=Ma(i,u,u,h);return n&&f.attr("style",n),ar(e,f),e.intersect=function(p){return V.debug(`APA12 Intersect called SPLIT +point:`,p,` +node: +`,e,` +res:`,sr.polygon(e,h,p)),sr.polygon(e,h,p)},i},"question")});var nAe,jK,KK=R(()=>{"use strict";ri();hi();ki();ti();Du();nAe=o((t,e,r,n,i)=>[`M${t+i},${e}`,`L${t+r-i},${e}`,`L${t+r},${e-n/2}`,`L${t+r-i},${e-n}`,`L${t+i},${e-n}`,`L${t},${e-n/2}`,"Z"].join(" "),"createHexagonPathD"),jK=o(async(t,e)=>{let{labelStyles:r,nodeStyles:n}=Br(e);e.labelStyle=r;let{shapeSvg:i,bbox:a}=await zr(t,e,En(e)),s=4,l=a.height+e.padding,u=l/s,h=a.width+2*u+e.padding,f=[{x:u,y:0},{x:h-u,y:0},{x:h,y:-l/2},{x:h-u,y:-l},{x:u,y:-l},{x:0,y:-l/2}],d,{cssStyles:p}=e;if(e.look==="handDrawn"){let m=Jt.svg(i),g=Fr(e,{}),y=nAe(0,0,h,l,u),v=m.path(y,g);d=i.insert(()=>v,":first-child").attr("transform",`translate(${-h/2}, ${l/2})`),p&&d.attr("style",p)}else d=Ma(i,h,l,f);return n&&d.attr("style",n),e.width=h,e.height=l,ar(e,d),e.intersect=function(m){return sr.polygon(e,f,m)},i},"hexagon")});var iAe,QK,ZK=R(()=>{"use strict";ri();hi();ki();ti();Du();iAe=o((t,e,r,n)=>[`M${t-2*n/6},${e}`,`L${t+r-n/6},${e}`,`L${t+r+2*n/6},${e-n}`,`L${t+n/6},${e-n}`,"Z"].join(" "),"createLeanRightPathD"),QK=o(async(t,e)=>{let{labelStyles:r,nodeStyles:n}=Br(e);e.labelStyle=r;let{shapeSvg:i,bbox:a}=await zr(t,e,En(e)),s=a.width+e.padding,l=a.height+e.padding,u=[{x:-2*l/6,y:0},{x:s-l/6,y:0},{x:s+2*l/6,y:-l},{x:l/6,y:-l}],h,{cssStyles:f}=e;if(e.look==="handDrawn"){let d=Jt.svg(i),p=Fr(e,{}),m=iAe(0,0,s,l),g=d.path(m,p);h=i.insert(()=>g,":first-child").attr("transform",`translate(${-s/2}, ${l/2})`),f&&h.attr("style",f)}else h=Ma(i,s,l,u);return n&&h.attr("style",n),e.width=s,e.height=l,ar(e,h),e.intersect=function(d){return sr.polygon(e,u,d)},i},"lean_right")});var aAe,JK,eQ=R(()=>{"use strict";ri();hi();ki();ti();Du();aAe=o((t,e,r,n)=>[`M${t+2*n/6},${e}`,`L${t+r+n/6},${e}`,`L${t+r-2*n/6},${e-n}`,`L${t-n/6},${e-n}`,"Z"].join(" "),"createLeanLeftPathD"),JK=o(async(t,e)=>{let{labelStyles:r,nodeStyles:n}=Br(e);e.labelStyle=r;let{shapeSvg:i,bbox:a}=await zr(t,e,En(e)),s=a.width+e.padding,l=a.height+e.padding,u=[{x:2*l/6,y:0},{x:s+l/6,y:0},{x:s-2*l/6,y:-l},{x:-l/6,y:-l}],h,{cssStyles:f}=e;if(e.look==="handDrawn"){let d=Jt.svg(i),p=Fr(e,{}),m=aAe(0,0,s,l),g=d.path(m,p);h=i.insert(()=>g,":first-child").attr("transform",`translate(${-s/2}, ${l/2})`),f&&h.attr("style",f)}else h=Ma(i,s,l,u);return n&&h.attr("style",n),e.width=s,e.height=l,ar(e,h),e.intersect=function(d){return sr.polygon(e,u,d)},i},"lean_left")});var sAe,tQ,rQ=R(()=>{"use strict";ri();hi();ki();ti();Du();sAe=o((t,e,r,n)=>[`M${t-2*n/6},${e}`,`L${t+r+2*n/6},${e}`,`L${t+r-n/6},${e-n}`,`L${t+n/6},${e-n}`,"Z"].join(" "),"createTrapezoidPathD"),tQ=o(async(t,e)=>{let{labelStyles:r,nodeStyles:n}=Br(e);e.labelStyle=r;let{shapeSvg:i,bbox:a}=await zr(t,e,En(e)),s=a.width+e.padding,l=a.height+e.padding,u=[{x:-2*l/6,y:0},{x:s+2*l/6,y:0},{x:s-l/6,y:-l},{x:l/6,y:-l}],h,{cssStyles:f}=e;if(e.look==="handDrawn"){let d=Jt.svg(i),p=Fr(e,{}),m=sAe(0,0,s,l),g=d.path(m,p);h=i.insert(()=>g,":first-child").attr("transform",`translate(${-s/2}, ${l/2})`),f&&h.attr("style",f)}else h=Ma(i,s,l,u);return n&&h.attr("style",n),e.width=s,e.height=l,ar(e,h),e.intersect=function(d){return sr.polygon(e,u,d)},i},"trapezoid")});var oAe,nQ,iQ=R(()=>{"use strict";ri();hi();ki();ti();Du();oAe=o((t,e,r,n)=>[`M${t+n/6},${e}`,`L${t+r-n/6},${e}`,`L${t+r+2*n/6},${e-n}`,`L${t-2*n/6},${e-n}`,"Z"].join(" "),"createInvertedTrapezoidPathD"),nQ=o(async(t,e)=>{let{labelStyles:r,nodeStyles:n}=Br(e);e.labelStyle=r;let{shapeSvg:i,bbox:a}=await zr(t,e,En(e)),s=a.width+e.padding,l=a.height+e.padding,u=[{x:l/6,y:0},{x:s-l/6,y:0},{x:s+2*l/6,y:-l},{x:-2*l/6,y:-l}],h,{cssStyles:f}=e;if(e.look==="handDrawn"){let d=Jt.svg(i),p=Fr(e,{}),m=oAe(0,0,s,l),g=d.path(m,p);h=i.insert(()=>g,":first-child").attr("transform",`translate(${-s/2}, ${l/2})`),f&&h.attr("style",f)}else h=Ma(i,s,l,u);return n&&h.attr("style",n),e.width=s,e.height=l,ar(e,h),e.intersect=function(d){return sr.polygon(e,u,d)},i},"inv_trapezoid")});var aQ,sQ=R(()=>{"use strict";_v();ri();hi();aQ=o(async(t,e)=>{let{shapeSvg:r}=await zr(t,e,"label"),n=r.insert("rect",":first-child");return n.attr("width",.1).attr("height",.1),r.attr("class","label edgeLabel"),ar(e,n),e.intersect=function(s){return sr.rect(e,s)},r},"labelRect")});var oQ,ym,rw,lQ,cQ,eL,tL=R(()=>{"use strict";ut();xK();wK();kK();CK();AK();_K();DK();NK();IK();PK();_t();FK();GK();VK();HK();WK();XK();KK();ZK();eQ();rQ();iQ();sQ();oQ={state:vK,stateStart:EK,stateEnd:SK,fork:J9,join:J9,choice:LK,note:RK,roundedRect:bK,rectWithTitle:OK,squareRect:TK,stadium:MK,subroutine:BK,cylinder:zK,circle:$K,doublecircle:UK,odd:YK,diamond:qK,hexagon:jK,lean_right:QK,lean_left:JK,trapezoid:tQ,inv_trapezoid:nQ,labelRect:aQ},ym=new Map,rw=o(async(t,e,r)=>{let n,i;if(e.shape==="rect"&&(e.rx&&e.ry?e.shape="roundedRect":e.shape="squareRect"),e.link){let a;de().securityLevel==="sandbox"?a="_top":e.linkTarget&&(a=e.linkTarget||"_blank"),n=t.insert("svg:a").attr("xlink:href",e.link).attr("target",a),i=await oQ[e.shape](n,e,r)}else i=await oQ[e.shape](t,e,r),n=i;return e.tooltip&&i.attr("title",e.tooltip),ym.set(e.id,n),e.haveCallback&&ym.get(e.id).attr("class",ym.get(e.id).attr("class")+" clickable"),n},"insertNode"),lQ=o((t,e)=>{ym.set(e.id,t)},"setNodeElem"),cQ=o(()=>{ym.clear()},"clear"),eL=o(t=>{let e=ym.get(t.id);V.trace("Transforming node",t.diff,t,"translate("+(t.x-t.width/2-5)+", "+t.width/2+")");let r=8,n=t.diff||0;return t.clusterNode?e.attr("transform","translate("+(t.x+n-t.width/2)+", "+(t.y-t.height/2-r)+")"):e.attr("transform","translate("+t.x+", "+t.y+")"),n},"positionNode")});var uQ,hQ=R(()=>{"use strict";qs();rr();ut();X9();K9();Q9();tL();ri();xr();uQ={common:We,getConfig:Or,insertCluster:Y5,insertEdge:J5,insertEdgeLabel:Q5,insertMarkers:ew,insertNode:rw,interpolateToCurve:om,labelHelper:zr,log:V,positionEdgeLabel:Z5}});function cAe(t){return typeof t=="symbol"||Wn(t)&&fa(t)==lAe}var lAe,so,Nd=R(()=>{"use strict";wu();Mo();lAe="[object Symbol]";o(cAe,"isSymbol");so=cAe});function uAe(t,e){for(var r=-1,n=t==null?0:t.length,i=Array(n);++r{"use strict";o(uAe,"arrayMap");Ss=uAe});function pQ(t){if(typeof t=="string")return t;if(wt(t))return Ss(t,pQ)+"";if(so(t))return dQ?dQ.call(t):"";var e=t+"";return e=="0"&&1/t==-hAe?"-0":e}var hAe,fQ,dQ,mQ,gQ=R(()=>{"use strict";vd();Md();Bn();Nd();hAe=1/0,fQ=Ji?Ji.prototype:void 0,dQ=fQ?fQ.toString:void 0;o(pQ,"baseToString");mQ=pQ});function dAe(t){for(var e=t.length;e--&&fAe.test(t.charAt(e)););return e}var fAe,yQ,vQ=R(()=>{"use strict";fAe=/\s/;o(dAe,"trimmedEndIndex");yQ=dAe});function mAe(t){return t&&t.slice(0,yQ(t)+1).replace(pAe,"")}var pAe,xQ,bQ=R(()=>{"use strict";vQ();pAe=/^\s+/;o(mAe,"baseTrim");xQ=mAe});function bAe(t){if(typeof t=="number")return t;if(so(t))return wQ;if(pn(t)){var e=typeof t.valueOf=="function"?t.valueOf():t;t=pn(e)?e+"":e}if(typeof t!="string")return t===0?t:+t;t=xQ(t);var r=yAe.test(t);return r||vAe.test(t)?xAe(t.slice(2),r?2:8):gAe.test(t)?wQ:+t}var wQ,gAe,yAe,vAe,xAe,TQ,kQ=R(()=>{"use strict";bQ();Js();Nd();wQ=NaN,gAe=/^[-+]0x[0-9a-f]+$/i,yAe=/^0b[01]+$/i,vAe=/^0o[0-7]+$/i,xAe=parseInt;o(bAe,"toNumber");TQ=bAe});function TAe(t){if(!t)return t===0?t:0;if(t=TQ(t),t===EQ||t===-EQ){var e=t<0?-1:1;return e*wAe}return t===t?t:0}var EQ,wAe,vm,rL=R(()=>{"use strict";kQ();EQ=1/0,wAe=17976931348623157e292;o(TAe,"toFinite");vm=TAe});function kAe(t){var e=vm(t),r=e%1;return e===e?r?e-r:e:0}var yc,xm=R(()=>{"use strict";rL();o(kAe,"toInteger");yc=kAe});var EAe,nw,CQ=R(()=>{"use strict";Nh();Ro();EAe=xs(Jn,"WeakMap"),nw=EAe});function CAe(){}var qn,nL=R(()=>{"use strict";o(CAe,"noop");qn=CAe});function SAe(t,e){for(var r=-1,n=t==null?0:t.length;++r{"use strict";o(SAe,"arrayEach");iw=SAe});function AAe(t,e,r,n){for(var i=t.length,a=r+(n?1:-1);n?a--:++a{"use strict";o(AAe,"baseFindIndex");aw=AAe});function _Ae(t){return t!==t}var SQ,AQ=R(()=>{"use strict";o(_Ae,"baseIsNaN");SQ=_Ae});function LAe(t,e,r){for(var n=r-1,i=t.length;++n{"use strict";o(LAe,"strictIndexOf");_Q=LAe});function DAe(t,e,r){return e===e?_Q(t,e,r):aw(t,SQ,r)}var bm,sw=R(()=>{"use strict";aL();AQ();LQ();o(DAe,"baseIndexOf");bm=DAe});function RAe(t,e){var r=t==null?0:t.length;return!!r&&bm(t,e,0)>-1}var ow,sL=R(()=>{"use strict";sw();o(RAe,"arrayIncludes");ow=RAe});var NAe,DQ,RQ=R(()=>{"use strict";F_();NAe=s5(Object.keys,Object),DQ=NAe});function OAe(t){if(!fc(t))return DQ(t);var e=[];for(var r in Object(t))IAe.call(t,r)&&r!="constructor"&&e.push(r);return e}var MAe,IAe,wm,lw=R(()=>{"use strict";tm();RQ();MAe=Object.prototype,IAe=MAe.hasOwnProperty;o(OAe,"baseKeys");wm=OAe});function PAe(t){return ei(t)?h5(t):wm(t)}var Dr,vc=R(()=>{"use strict";U_();lw();Io();o(PAe,"keys");Dr=PAe});var BAe,FAe,zAe,pa,NQ=R(()=>{"use strict";am();kd();q_();Io();tm();vc();BAe=Object.prototype,FAe=BAe.hasOwnProperty,zAe=p5(function(t,e){if(fc(e)||ei(e)){Bo(e,Dr(e),t);return}for(var r in e)FAe.call(e,r)&&dc(t,r,e[r])}),pa=zAe});function VAe(t,e){if(wt(t))return!1;var r=typeof t;return r=="number"||r=="symbol"||r=="boolean"||t==null||so(t)?!0:$Ae.test(t)||!GAe.test(t)||e!=null&&t in Object(e)}var GAe,$Ae,Tm,cw=R(()=>{"use strict";Bn();Nd();GAe=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,$Ae=/^\w*$/;o(VAe,"isKey");Tm=VAe});function HAe(t){var e=qp(t,function(n){return r.size===UAe&&r.clear(),n}),r=e.cache;return e}var UAe,MQ,IQ=R(()=>{"use strict";R_();UAe=500;o(HAe,"memoizeCapped");MQ=HAe});var YAe,WAe,qAe,OQ,PQ=R(()=>{"use strict";IQ();YAe=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,WAe=/\\(\\)?/g,qAe=MQ(function(t){var e=[];return t.charCodeAt(0)===46&&e.push(""),t.replace(YAe,function(r,n,i,a){e.push(i?a.replace(WAe,"$1"):n||r)}),e}),OQ=qAe});function XAe(t){return t==null?"":mQ(t)}var uw,oL=R(()=>{"use strict";gQ();o(XAe,"toString");uw=XAe});function jAe(t,e){return wt(t)?t:Tm(t,e)?[t]:OQ(uw(t))}var Hh,Lv=R(()=>{"use strict";Bn();cw();PQ();oL();o(jAe,"castPath");Hh=jAe});function QAe(t){if(typeof t=="string"||so(t))return t;var e=t+"";return e=="0"&&1/t==-KAe?"-0":e}var KAe,xc,km=R(()=>{"use strict";Nd();KAe=1/0;o(QAe,"toKey");xc=QAe});function ZAe(t,e){e=Hh(e,t);for(var r=0,n=e.length;t!=null&&r{"use strict";Lv();km();o(ZAe,"baseGet");Yh=ZAe});function JAe(t,e,r){var n=t==null?void 0:Yh(t,e);return n===void 0?r:n}var BQ,FQ=R(()=>{"use strict";Dv();o(JAe,"get");BQ=JAe});function e8e(t,e){for(var r=-1,n=e.length,i=t.length;++r{"use strict";o(e8e,"arrayPush");Em=e8e});function t8e(t){return wt(t)||kl(t)||!!(zQ&&t&&t[zQ])}var zQ,GQ,$Q=R(()=>{"use strict";vd();rm();Bn();zQ=Ji?Ji.isConcatSpreadable:void 0;o(t8e,"isFlattenable");GQ=t8e});function VQ(t,e,r,n,i){var a=-1,s=t.length;for(r||(r=GQ),i||(i=[]);++a0&&r(l)?e>1?VQ(l,e-1,r,n,i):Em(i,l):n||(i[i.length]=l)}return i}var bc,Cm=R(()=>{"use strict";hw();$Q();o(VQ,"baseFlatten");bc=VQ});function r8e(t){var e=t==null?0:t.length;return e?bc(t,1):[]}var Gr,fw=R(()=>{"use strict";Cm();o(r8e,"flatten");Gr=r8e});function n8e(t){return d5(f5(t,void 0,Gr),t+"")}var UQ,HQ=R(()=>{"use strict";fw();H_();W_();o(n8e,"flatRest");UQ=n8e});function i8e(t,e,r){var n=-1,i=t.length;e<0&&(e=-e>i?0:i+e),r=r>i?i:r,r<0&&(r+=i),i=e>r?0:r-e>>>0,e>>>=0;for(var a=Array(i);++n{"use strict";o(i8e,"baseSlice");dw=i8e});function d8e(t){return f8e.test(t)}var a8e,s8e,o8e,l8e,c8e,u8e,h8e,f8e,YQ,WQ=R(()=>{"use strict";a8e="\\ud800-\\udfff",s8e="\\u0300-\\u036f",o8e="\\ufe20-\\ufe2f",l8e="\\u20d0-\\u20ff",c8e=s8e+o8e+l8e,u8e="\\ufe0e\\ufe0f",h8e="\\u200d",f8e=RegExp("["+h8e+a8e+c8e+u8e+"]");o(d8e,"hasUnicode");YQ=d8e});function p8e(t,e,r,n){var i=-1,a=t==null?0:t.length;for(n&&a&&(r=t[++i]);++i{"use strict";o(p8e,"arrayReduce");qQ=p8e});function m8e(t,e){return t&&Bo(e,Dr(e),t)}var jQ,KQ=R(()=>{"use strict";kd();vc();o(m8e,"baseAssign");jQ=m8e});function g8e(t,e){return t&&Bo(e,bs(e),t)}var QQ,ZQ=R(()=>{"use strict";kd();zh();o(g8e,"baseAssignIn");QQ=g8e});function y8e(t,e){for(var r=-1,n=t==null?0:t.length,i=0,a=[];++r{"use strict";o(y8e,"arrayFilter");Sm=y8e});function v8e(){return[]}var mw,cL=R(()=>{"use strict";o(v8e,"stubArray");mw=v8e});var x8e,b8e,JQ,w8e,Am,gw=R(()=>{"use strict";pw();cL();x8e=Object.prototype,b8e=x8e.propertyIsEnumerable,JQ=Object.getOwnPropertySymbols,w8e=JQ?function(t){return t==null?[]:(t=Object(t),Sm(JQ(t),function(e){return b8e.call(t,e)}))}:mw,Am=w8e});function T8e(t,e){return Bo(t,Am(t),e)}var eZ,tZ=R(()=>{"use strict";kd();gw();o(T8e,"copySymbols");eZ=T8e});var k8e,E8e,yw,uL=R(()=>{"use strict";hw();o5();gw();cL();k8e=Object.getOwnPropertySymbols,E8e=k8e?function(t){for(var e=[];t;)Em(e,Am(t)),t=em(t);return e}:mw,yw=E8e});function C8e(t,e){return Bo(t,yw(t),e)}var rZ,nZ=R(()=>{"use strict";kd();uL();o(C8e,"copySymbolsIn");rZ=C8e});function S8e(t,e,r){var n=e(t);return wt(t)?n:Em(n,r(t))}var vw,hL=R(()=>{"use strict";hw();Bn();o(S8e,"baseGetAllKeys");vw=S8e});function A8e(t){return vw(t,Dr,Am)}var Rv,fL=R(()=>{"use strict";hL();gw();vc();o(A8e,"getAllKeys");Rv=A8e});function _8e(t){return vw(t,bs,yw)}var xw,dL=R(()=>{"use strict";hL();uL();zh();o(_8e,"getAllKeysIn");xw=_8e});var L8e,bw,iZ=R(()=>{"use strict";Nh();Ro();L8e=xs(Jn,"DataView"),bw=L8e});var D8e,ww,aZ=R(()=>{"use strict";Nh();Ro();D8e=xs(Jn,"Promise"),ww=D8e});var R8e,Wh,pL=R(()=>{"use strict";Nh();Ro();R8e=xs(Jn,"Set"),Wh=R8e});var sZ,N8e,oZ,lZ,cZ,uZ,M8e,I8e,O8e,P8e,B8e,Id,oo,Od=R(()=>{"use strict";iZ();J3();aZ();pL();CQ();wu();__();sZ="[object Map]",N8e="[object Object]",oZ="[object Promise]",lZ="[object Set]",cZ="[object WeakMap]",uZ="[object DataView]",M8e=Tu(bw),I8e=Tu(Oh),O8e=Tu(ww),P8e=Tu(Wh),B8e=Tu(nw),Id=fa;(bw&&Id(new bw(new ArrayBuffer(1)))!=uZ||Oh&&Id(new Oh)!=sZ||ww&&Id(ww.resolve())!=oZ||Wh&&Id(new Wh)!=lZ||nw&&Id(new nw)!=cZ)&&(Id=o(function(t){var e=fa(t),r=e==N8e?t.constructor:void 0,n=r?Tu(r):"";if(n)switch(n){case M8e:return uZ;case I8e:return sZ;case O8e:return oZ;case P8e:return lZ;case B8e:return cZ}return e},"getTag"));oo=Id});function G8e(t){var e=t.length,r=new t.constructor(e);return e&&typeof t[0]=="string"&&z8e.call(t,"index")&&(r.index=t.index,r.input=t.input),r}var F8e,z8e,hZ,fZ=R(()=>{"use strict";F8e=Object.prototype,z8e=F8e.hasOwnProperty;o(G8e,"initCloneArray");hZ=G8e});function $8e(t,e){var r=e?Jp(t.buffer):t.buffer;return new t.constructor(r,t.byteOffset,t.byteLength)}var dZ,pZ=R(()=>{"use strict";n5();o($8e,"cloneDataView");dZ=$8e});function U8e(t){var e=new t.constructor(t.source,V8e.exec(t));return e.lastIndex=t.lastIndex,e}var V8e,mZ,gZ=R(()=>{"use strict";V8e=/\w*$/;o(U8e,"cloneRegExp");mZ=U8e});function H8e(t){return vZ?Object(vZ.call(t)):{}}var yZ,vZ,xZ,bZ=R(()=>{"use strict";vd();yZ=Ji?Ji.prototype:void 0,vZ=yZ?yZ.valueOf:void 0;o(H8e,"cloneSymbol");xZ=H8e});function u_e(t,e,r){var n=t.constructor;switch(e){case J8e:return Jp(t);case Y8e:case W8e:return new n(+t);case e_e:return dZ(t,r);case t_e:case r_e:case n_e:case i_e:case a_e:case s_e:case o_e:case l_e:case c_e:return i5(t,r);case q8e:return new n;case X8e:case Q8e:return new n(t);case j8e:return mZ(t);case K8e:return new n;case Z8e:return xZ(t)}}var Y8e,W8e,q8e,X8e,j8e,K8e,Q8e,Z8e,J8e,e_e,t_e,r_e,n_e,i_e,a_e,s_e,o_e,l_e,c_e,wZ,TZ=R(()=>{"use strict";n5();pZ();gZ();bZ();P_();Y8e="[object Boolean]",W8e="[object Date]",q8e="[object Map]",X8e="[object Number]",j8e="[object RegExp]",K8e="[object Set]",Q8e="[object String]",Z8e="[object Symbol]",J8e="[object ArrayBuffer]",e_e="[object DataView]",t_e="[object Float32Array]",r_e="[object Float64Array]",n_e="[object Int8Array]",i_e="[object Int16Array]",a_e="[object Int32Array]",s_e="[object Uint8Array]",o_e="[object Uint8ClampedArray]",l_e="[object Uint16Array]",c_e="[object Uint32Array]";o(u_e,"initCloneByTag");wZ=u_e});function f_e(t){return Wn(t)&&oo(t)==h_e}var h_e,kZ,EZ=R(()=>{"use strict";Od();Mo();h_e="[object Map]";o(f_e,"baseIsMap");kZ=f_e});var CZ,d_e,SZ,AZ=R(()=>{"use strict";EZ();Td();ov();CZ=Po&&Po.isMap,d_e=CZ?Oo(CZ):kZ,SZ=d_e});function m_e(t){return Wn(t)&&oo(t)==p_e}var p_e,_Z,LZ=R(()=>{"use strict";Od();Mo();p_e="[object Set]";o(m_e,"baseIsSet");_Z=m_e});var DZ,g_e,RZ,NZ=R(()=>{"use strict";LZ();Td();ov();DZ=Po&&Po.isSet,g_e=DZ?Oo(DZ):_Z,RZ=g_e});function Tw(t,e,r,n,i,a){var s,l=e&y_e,u=e&v_e,h=e&x_e;if(r&&(s=i?r(t,n,i,a):r(t)),s!==void 0)return s;if(!pn(t))return t;var f=wt(t);if(f){if(s=hZ(t),!l)return a5(t,s)}else{var d=oo(t),p=d==IZ||d==E_e;if(El(t))return r5(t,l);if(d==OZ||d==MZ||p&&!i){if(s=u||p?{}:l5(t),!l)return u?rZ(t,QQ(s,t)):eZ(t,jQ(s,t))}else{if(!Cn[d])return i?t:{};s=wZ(t,d,l)}}a||(a=new uc);var m=a.get(t);if(m)return m;a.set(t,s),RZ(t)?t.forEach(function(v){s.add(Tw(v,e,r,v,t,a))}):SZ(t)&&t.forEach(function(v,x){s.set(x,Tw(v,e,r,x,t,a))});var g=h?u?xw:Rv:u?bs:Dr,y=f?void 0:g(t);return iw(y||t,function(v,x){y&&(x=v,v=t[x]),dc(s,x,Tw(v,e,r,x,t,a))}),s}var y_e,v_e,x_e,MZ,b_e,w_e,T_e,k_e,IZ,E_e,C_e,S_e,OZ,A_e,__e,L_e,D_e,R_e,N_e,M_e,I_e,O_e,P_e,B_e,F_e,z_e,G_e,$_e,V_e,Cn,kw,mL=R(()=>{"use strict";iv();iL();am();KQ();ZQ();I_();B_();tZ();nZ();fL();dL();Od();fZ();TZ();z_();Bn();im();AZ();Js();NZ();vc();zh();y_e=1,v_e=2,x_e=4,MZ="[object Arguments]",b_e="[object Array]",w_e="[object Boolean]",T_e="[object Date]",k_e="[object Error]",IZ="[object Function]",E_e="[object GeneratorFunction]",C_e="[object Map]",S_e="[object Number]",OZ="[object Object]",A_e="[object RegExp]",__e="[object Set]",L_e="[object String]",D_e="[object Symbol]",R_e="[object WeakMap]",N_e="[object ArrayBuffer]",M_e="[object DataView]",I_e="[object Float32Array]",O_e="[object Float64Array]",P_e="[object Int8Array]",B_e="[object Int16Array]",F_e="[object Int32Array]",z_e="[object Uint8Array]",G_e="[object Uint8ClampedArray]",$_e="[object Uint16Array]",V_e="[object Uint32Array]",Cn={};Cn[MZ]=Cn[b_e]=Cn[N_e]=Cn[M_e]=Cn[w_e]=Cn[T_e]=Cn[I_e]=Cn[O_e]=Cn[P_e]=Cn[B_e]=Cn[F_e]=Cn[C_e]=Cn[S_e]=Cn[OZ]=Cn[A_e]=Cn[__e]=Cn[L_e]=Cn[D_e]=Cn[z_e]=Cn[G_e]=Cn[$_e]=Cn[V_e]=!0;Cn[k_e]=Cn[IZ]=Cn[R_e]=!1;o(Tw,"baseClone");kw=Tw});function H_e(t){return kw(t,U_e)}var U_e,Qr,gL=R(()=>{"use strict";mL();U_e=4;o(H_e,"clone");Qr=H_e});function q_e(t){return kw(t,Y_e|W_e)}var Y_e,W_e,yL,PZ=R(()=>{"use strict";mL();Y_e=1,W_e=4;o(q_e,"cloneDeep");yL=q_e});function X_e(t){for(var e=-1,r=t==null?0:t.length,n=0,i=[];++e{"use strict";o(X_e,"compact");wc=X_e});function K_e(t){return this.__data__.set(t,j_e),this}var j_e,FZ,zZ=R(()=>{"use strict";j_e="__lodash_hash_undefined__";o(K_e,"setCacheAdd");FZ=K_e});function Q_e(t){return this.__data__.has(t)}var GZ,$Z=R(()=>{"use strict";o(Q_e,"setCacheHas");GZ=Q_e});function Ew(t){var e=-1,r=t==null?0:t.length;for(this.__data__=new bd;++e{"use strict";e5();zZ();$Z();o(Ew,"SetCache");Ew.prototype.add=Ew.prototype.push=FZ;Ew.prototype.has=GZ;_m=Ew});function Z_e(t,e){for(var r=-1,n=t==null?0:t.length;++r{"use strict";o(Z_e,"arraySome");Sw=Z_e});function J_e(t,e){return t.has(e)}var Lm,Aw=R(()=>{"use strict";o(J_e,"cacheHas");Lm=J_e});function r9e(t,e,r,n,i,a){var s=r&e9e,l=t.length,u=e.length;if(l!=u&&!(s&&u>l))return!1;var h=a.get(t),f=a.get(e);if(h&&f)return h==e&&f==t;var d=-1,p=!0,m=r&t9e?new _m:void 0;for(a.set(t,e),a.set(e,t);++d{"use strict";Cw();vL();Aw();e9e=1,t9e=2;o(r9e,"equalArrays");_w=r9e});function n9e(t){var e=-1,r=Array(t.size);return t.forEach(function(n,i){r[++e]=[i,n]}),r}var VZ,UZ=R(()=>{"use strict";o(n9e,"mapToArray");VZ=n9e});function i9e(t){var e=-1,r=Array(t.size);return t.forEach(function(n){r[++e]=n}),r}var Dm,Lw=R(()=>{"use strict";o(i9e,"setToArray");Dm=i9e});function v9e(t,e,r,n,i,a,s){switch(r){case y9e:if(t.byteLength!=e.byteLength||t.byteOffset!=e.byteOffset)return!1;t=t.buffer,e=e.buffer;case g9e:return!(t.byteLength!=e.byteLength||!a(new Zp(t),new Zp(e)));case o9e:case l9e:case h9e:return No(+t,+e);case c9e:return t.name==e.name&&t.message==e.message;case f9e:case p9e:return t==e+"";case u9e:var l=VZ;case d9e:var u=n&a9e;if(l||(l=Dm),t.size!=e.size&&!u)return!1;var h=s.get(t);if(h)return h==e;n|=s9e,s.set(t,e);var f=_w(l(t),l(e),n,i,a,s);return s.delete(t),f;case m9e:if(bL)return bL.call(t)==bL.call(e)}return!1}var a9e,s9e,o9e,l9e,c9e,u9e,h9e,f9e,d9e,p9e,m9e,g9e,y9e,HZ,bL,YZ,WZ=R(()=>{"use strict";vd();O_();xd();xL();UZ();Lw();a9e=1,s9e=2,o9e="[object Boolean]",l9e="[object Date]",c9e="[object Error]",u9e="[object Map]",h9e="[object Number]",f9e="[object RegExp]",d9e="[object Set]",p9e="[object String]",m9e="[object Symbol]",g9e="[object ArrayBuffer]",y9e="[object DataView]",HZ=Ji?Ji.prototype:void 0,bL=HZ?HZ.valueOf:void 0;o(v9e,"equalByTag");YZ=v9e});function T9e(t,e,r,n,i,a){var s=r&x9e,l=Rv(t),u=l.length,h=Rv(e),f=h.length;if(u!=f&&!s)return!1;for(var d=u;d--;){var p=l[d];if(!(s?p in e:w9e.call(e,p)))return!1}var m=a.get(t),g=a.get(e);if(m&&g)return m==e&&g==t;var y=!0;a.set(t,e),a.set(e,t);for(var v=s;++d{"use strict";fL();x9e=1,b9e=Object.prototype,w9e=b9e.hasOwnProperty;o(T9e,"equalObjects");qZ=T9e});function C9e(t,e,r,n,i,a){var s=wt(t),l=wt(e),u=s?KZ:oo(t),h=l?KZ:oo(e);u=u==jZ?Dw:u,h=h==jZ?Dw:h;var f=u==Dw,d=h==Dw,p=u==h;if(p&&El(t)){if(!El(e))return!1;s=!0,f=!1}if(p&&!f)return a||(a=new uc),s||Bh(t)?_w(t,e,r,n,i,a):YZ(t,e,u,r,n,i,a);if(!(r&k9e)){var m=f&&QZ.call(t,"__wrapped__"),g=d&&QZ.call(e,"__wrapped__");if(m||g){var y=m?t.value():t,v=g?e.value():e;return a||(a=new uc),i(y,v,r,n,a)}}return p?(a||(a=new uc),qZ(t,e,r,n,i,a)):!1}var k9e,jZ,KZ,Dw,E9e,QZ,ZZ,JZ=R(()=>{"use strict";iv();xL();WZ();XZ();Od();Bn();im();lv();k9e=1,jZ="[object Arguments]",KZ="[object Array]",Dw="[object Object]",E9e=Object.prototype,QZ=E9e.hasOwnProperty;o(C9e,"baseIsEqualDeep");ZZ=C9e});function eJ(t,e,r,n,i){return t===e?!0:t==null||e==null||!Wn(t)&&!Wn(e)?t!==t&&e!==e:ZZ(t,e,r,n,eJ,i)}var Rw,wL=R(()=>{"use strict";JZ();Mo();o(eJ,"baseIsEqual");Rw=eJ});function _9e(t,e,r,n){var i=r.length,a=i,s=!n;if(t==null)return!a;for(t=Object(t);i--;){var l=r[i];if(s&&l[2]?l[1]!==t[l[0]]:!(l[0]in t))return!1}for(;++i{"use strict";iv();wL();S9e=1,A9e=2;o(_9e,"baseIsMatch");tJ=_9e});function L9e(t){return t===t&&!pn(t)}var Nw,TL=R(()=>{"use strict";Js();o(L9e,"isStrictComparable");Nw=L9e});function D9e(t){for(var e=Dr(t),r=e.length;r--;){var n=e[r],i=t[n];e[r]=[n,i,Nw(i)]}return e}var nJ,iJ=R(()=>{"use strict";TL();vc();o(D9e,"getMatchData");nJ=D9e});function R9e(t,e){return function(r){return r==null?!1:r[t]===e&&(e!==void 0||t in Object(r))}}var Mw,kL=R(()=>{"use strict";o(R9e,"matchesStrictComparable");Mw=R9e});function N9e(t){var e=nJ(t);return e.length==1&&e[0][2]?Mw(e[0][0],e[0][1]):function(r){return r===t||tJ(r,t,e)}}var aJ,sJ=R(()=>{"use strict";rJ();iJ();kL();o(N9e,"baseMatches");aJ=N9e});function M9e(t,e){return t!=null&&e in Object(t)}var oJ,lJ=R(()=>{"use strict";o(M9e,"baseHasIn");oJ=M9e});function I9e(t,e,r){e=Hh(e,t);for(var n=-1,i=e.length,a=!1;++n{"use strict";Lv();rm();Bn();uv();c5();km();o(I9e,"hasPath");Iw=I9e});function O9e(t,e){return t!=null&&Iw(t,e,oJ)}var Ow,CL=R(()=>{"use strict";lJ();EL();o(O9e,"hasIn");Ow=O9e});function F9e(t,e){return Tm(t)&&Nw(e)?Mw(xc(t),e):function(r){var n=BQ(r,t);return n===void 0&&n===e?Ow(r,t):Rw(e,n,P9e|B9e)}}var P9e,B9e,cJ,uJ=R(()=>{"use strict";wL();FQ();CL();cw();TL();kL();km();P9e=1,B9e=2;o(F9e,"baseMatchesProperty");cJ=F9e});function z9e(t){return function(e){return e?.[t]}}var Pw,SL=R(()=>{"use strict";o(z9e,"baseProperty");Pw=z9e});function G9e(t){return function(e){return Yh(e,t)}}var hJ,fJ=R(()=>{"use strict";Dv();o(G9e,"basePropertyDeep");hJ=G9e});function $9e(t){return Tm(t)?Pw(xc(t)):hJ(t)}var dJ,pJ=R(()=>{"use strict";SL();fJ();cw();km();o($9e,"property");dJ=$9e});function V9e(t){return typeof t=="function"?t:t==null?ea:typeof t=="object"?wt(t)?cJ(t[0],t[1]):aJ(t):dJ(t)}var cn,Qa=R(()=>{"use strict";sJ();uJ();Eu();Bn();pJ();o(V9e,"baseIteratee");cn=V9e});function U9e(t,e,r,n){for(var i=-1,a=t==null?0:t.length;++i{"use strict";o(U9e,"arrayAggregator");mJ=U9e});function H9e(t,e){return t&&Qp(t,e,Dr)}var Rm,Bw=R(()=>{"use strict";t5();vc();o(H9e,"baseForOwn");Rm=H9e});function Y9e(t,e){return function(r,n){if(r==null)return r;if(!ei(r))return t(r,n);for(var i=r.length,a=e?i:-1,s=Object(r);(e?a--:++a{"use strict";Io();o(Y9e,"createBaseEach");yJ=Y9e});var W9e,As,qh=R(()=>{"use strict";Bw();vJ();W9e=yJ(Rm),As=W9e});function q9e(t,e,r,n){return As(t,function(i,a,s){e(n,i,r(i),s)}),n}var xJ,bJ=R(()=>{"use strict";qh();o(q9e,"baseAggregator");xJ=q9e});function X9e(t,e){return function(r,n){var i=wt(r)?mJ:xJ,a=e?e():{};return i(r,t,cn(n,2),a)}}var wJ,TJ=R(()=>{"use strict";gJ();bJ();Qa();Bn();o(X9e,"createAggregator");wJ=X9e});var j9e,Fw,kJ=R(()=>{"use strict";Ro();j9e=o(function(){return Jn.Date.now()},"now"),Fw=j9e});var EJ,K9e,Q9e,Xh,CJ=R(()=>{"use strict";sm();xd();Ed();zh();EJ=Object.prototype,K9e=EJ.hasOwnProperty,Q9e=pc(function(t,e){t=Object(t);var r=-1,n=e.length,i=n>2?e[2]:void 0;for(i&&eo(e[0],e[1],i)&&(n=1);++r{"use strict";o(Z9e,"arrayIncludesWith");zw=Z9e});function eLe(t,e,r,n){var i=-1,a=ow,s=!0,l=t.length,u=[],h=e.length;if(!l)return u;r&&(e=Ss(e,Oo(r))),n?(a=zw,s=!1):e.length>=J9e&&(a=Lm,s=!1,e=new _m(e));e:for(;++i{"use strict";Cw();sL();AL();Md();Td();Aw();J9e=200;o(eLe,"baseDifference");SJ=eLe});var tLe,jh,_J=R(()=>{"use strict";AJ();Cm();sm();u5();tLe=pc(function(t,e){return wd(t)?SJ(t,bc(e,1,wd,!0)):[]}),jh=tLe});function rLe(t){var e=t==null?0:t.length;return e?t[e-1]:void 0}var ma,LJ=R(()=>{"use strict";o(rLe,"last");ma=rLe});function nLe(t,e,r){var n=t==null?0:t.length;return n?(e=r||e===void 0?1:yc(e),dw(t,e<0?0:e,n)):[]}var fi,DJ=R(()=>{"use strict";lL();xm();o(nLe,"drop");fi=nLe});function iLe(t,e,r){var n=t==null?0:t.length;return n?(e=r||e===void 0?1:yc(e),e=n-e,dw(t,0,e<0?0:e)):[]}var Ru,RJ=R(()=>{"use strict";lL();xm();o(iLe,"dropRight");Ru=iLe});function aLe(t){return typeof t=="function"?t:ea}var Nm,Gw=R(()=>{"use strict";Eu();o(aLe,"castFunction");Nm=aLe});function sLe(t,e){var r=wt(t)?iw:As;return r(t,Nm(e))}var Ee,$w=R(()=>{"use strict";iL();qh();Gw();Bn();o(sLe,"forEach");Ee=sLe});var NJ=R(()=>{"use strict";$w()});function oLe(t,e){for(var r=-1,n=t==null?0:t.length;++r{"use strict";o(oLe,"arrayEvery");MJ=oLe});function lLe(t,e){var r=!0;return As(t,function(n,i,a){return r=!!e(n,i,a),r}),r}var OJ,PJ=R(()=>{"use strict";qh();o(lLe,"baseEvery");OJ=lLe});function cLe(t,e,r){var n=wt(t)?MJ:OJ;return r&&eo(t,e,r)&&(e=void 0),n(t,cn(e,3))}var Ia,BJ=R(()=>{"use strict";IJ();PJ();Qa();Bn();Ed();o(cLe,"every");Ia=cLe});function uLe(t,e){var r=[];return As(t,function(n,i,a){e(n,i,a)&&r.push(n)}),r}var Vw,_L=R(()=>{"use strict";qh();o(uLe,"baseFilter");Vw=uLe});function hLe(t,e){var r=wt(t)?Sm:Vw;return r(t,cn(e,3))}var $r,LL=R(()=>{"use strict";pw();_L();Qa();Bn();o(hLe,"filter");$r=hLe});function fLe(t){return function(e,r,n){var i=Object(e);if(!ei(e)){var a=cn(r,3);e=Dr(e),r=o(function(l){return a(i[l],l,i)},"predicate")}var s=t(e,r,n);return s>-1?i[a?e[s]:s]:void 0}}var FJ,zJ=R(()=>{"use strict";Qa();Io();vc();o(fLe,"createFind");FJ=fLe});function pLe(t,e,r){var n=t==null?0:t.length;if(!n)return-1;var i=r==null?0:yc(r);return i<0&&(i=dLe(n+i,0)),aw(t,cn(e,3),i)}var dLe,GJ,$J=R(()=>{"use strict";aL();Qa();xm();dLe=Math.max;o(pLe,"findIndex");GJ=pLe});var mLe,Za,VJ=R(()=>{"use strict";zJ();$J();mLe=FJ(GJ),Za=mLe});function gLe(t){return t&&t.length?t[0]:void 0}var na,UJ=R(()=>{"use strict";o(gLe,"head");na=gLe});var HJ=R(()=>{"use strict";UJ()});function yLe(t,e){var r=-1,n=ei(t)?Array(t.length):[];return As(t,function(i,a,s){n[++r]=e(i,a,s)}),n}var Uw,DL=R(()=>{"use strict";qh();Io();o(yLe,"baseMap");Uw=yLe});function vLe(t,e){var r=wt(t)?Ss:Uw;return r(t,cn(e,3))}var qe,Mm=R(()=>{"use strict";Md();Qa();DL();Bn();o(vLe,"map");qe=vLe});function xLe(t,e){return bc(qe(t,e),1)}var ga,RL=R(()=>{"use strict";Cm();Mm();o(xLe,"flatMap");ga=xLe});function bLe(t,e){return t==null?t:Qp(t,Nm(e),bs)}var NL,YJ=R(()=>{"use strict";t5();Gw();zh();o(bLe,"forIn");NL=bLe});function wLe(t,e){return t&&Rm(t,Nm(e))}var ML,WJ=R(()=>{"use strict";Bw();Gw();o(wLe,"forOwn");ML=wLe});var TLe,kLe,ELe,IL,qJ=R(()=>{"use strict";Kp();TJ();TLe=Object.prototype,kLe=TLe.hasOwnProperty,ELe=wJ(function(t,e,r){kLe.call(t,r)?t[r].push(e):hc(t,r,[e])}),IL=ELe});function CLe(t,e){return t>e}var XJ,jJ=R(()=>{"use strict";o(CLe,"baseGt");XJ=CLe});function _Le(t,e){return t!=null&&ALe.call(t,e)}var SLe,ALe,KJ,QJ=R(()=>{"use strict";SLe=Object.prototype,ALe=SLe.hasOwnProperty;o(_Le,"baseHas");KJ=_Le});function LLe(t,e){return t!=null&&Iw(t,e,KJ)}var Xe,ZJ=R(()=>{"use strict";QJ();EL();o(LLe,"has");Xe=LLe});function RLe(t){return typeof t=="string"||!wt(t)&&Wn(t)&&fa(t)==DLe}var DLe,di,Hw=R(()=>{"use strict";wu();Bn();Mo();DLe="[object String]";o(RLe,"isString");di=RLe});function NLe(t,e){return Ss(e,function(r){return t[r]})}var JJ,eee=R(()=>{"use strict";Md();o(NLe,"baseValues");JJ=NLe});function MLe(t){return t==null?[]:JJ(t,Dr(t))}var or,OL=R(()=>{"use strict";eee();vc();o(MLe,"values");or=MLe});function OLe(t,e,r,n){t=ei(t)?t:or(t),r=r&&!n?yc(r):0;var i=t.length;return r<0&&(r=ILe(i+r,0)),di(t)?r<=i&&t.indexOf(e,r)>-1:!!i&&bm(t,e,r)>-1}var ILe,Fn,tee=R(()=>{"use strict";sw();Io();Hw();xm();OL();ILe=Math.max;o(OLe,"includes");Fn=OLe});function BLe(t,e,r){var n=t==null?0:t.length;if(!n)return-1;var i=r==null?0:yc(r);return i<0&&(i=PLe(n+i,0)),bm(t,e,i)}var PLe,Yw,ree=R(()=>{"use strict";sw();xm();PLe=Math.max;o(BLe,"indexOf");Yw=BLe});function VLe(t){if(t==null)return!0;if(ei(t)&&(wt(t)||typeof t=="string"||typeof t.splice=="function"||El(t)||Bh(t)||kl(t)))return!t.length;var e=oo(t);if(e==FLe||e==zLe)return!t.size;if(fc(t))return!wm(t).length;for(var r in t)if($Le.call(t,r))return!1;return!0}var FLe,zLe,GLe,$Le,Qt,Ww=R(()=>{"use strict";lw();Od();rm();Bn();Io();im();tm();lv();FLe="[object Map]",zLe="[object Set]",GLe=Object.prototype,$Le=GLe.hasOwnProperty;o(VLe,"isEmpty");Qt=VLe});function HLe(t){return Wn(t)&&fa(t)==ULe}var ULe,nee,iee=R(()=>{"use strict";wu();Mo();ULe="[object RegExp]";o(HLe,"baseIsRegExp");nee=HLe});var aee,YLe,zo,see=R(()=>{"use strict";iee();Td();ov();aee=Po&&Po.isRegExp,YLe=aee?Oo(aee):nee,zo=YLe});function WLe(t){return t===void 0}var er,oee=R(()=>{"use strict";o(WLe,"isUndefined");er=WLe});function qLe(t,e){return t{"use strict";o(qLe,"baseLt");qw=qLe});function XLe(t,e){var r={};return e=cn(e,3),Rm(t,function(n,i,a){hc(r,i,e(n,i,a))}),r}var Pd,lee=R(()=>{"use strict";Kp();Bw();Qa();o(XLe,"mapValues");Pd=XLe});function jLe(t,e,r){for(var n=-1,i=t.length;++n{"use strict";Nd();o(jLe,"baseExtremum");Im=jLe});function KLe(t){return t&&t.length?Im(t,ea,XJ):void 0}var _s,cee=R(()=>{"use strict";Xw();jJ();Eu();o(KLe,"max");_s=KLe});function QLe(t){return t&&t.length?Im(t,ea,qw):void 0}var Ll,BL=R(()=>{"use strict";Xw();PL();Eu();o(QLe,"min");Ll=QLe});function ZLe(t,e){return t&&t.length?Im(t,cn(e,2),qw):void 0}var Bd,uee=R(()=>{"use strict";Xw();Qa();PL();o(ZLe,"minBy");Bd=ZLe});function eDe(t){if(typeof t!="function")throw new TypeError(JLe);return function(){var e=arguments;switch(e.length){case 0:return!t.call(this);case 1:return!t.call(this,e[0]);case 2:return!t.call(this,e[0],e[1]);case 3:return!t.call(this,e[0],e[1],e[2])}return!t.apply(this,e)}}var JLe,hee,fee=R(()=>{"use strict";JLe="Expected a function";o(eDe,"negate");hee=eDe});function tDe(t,e,r,n){if(!pn(t))return t;e=Hh(e,t);for(var i=-1,a=e.length,s=a-1,l=t;l!=null&&++i{"use strict";am();Lv();uv();Js();km();o(tDe,"baseSet");dee=tDe});function rDe(t,e,r){for(var n=-1,i=e.length,a={};++n{"use strict";Dv();pee();Lv();o(rDe,"basePickBy");jw=rDe});function nDe(t,e){if(t==null)return{};var r=Ss(xw(t),function(n){return[n]});return e=cn(e),jw(t,r,function(n,i){return e(n,i[0])})}var Ls,mee=R(()=>{"use strict";Md();Qa();FL();dL();o(nDe,"pickBy");Ls=nDe});function iDe(t,e){var r=t.length;for(t.sort(e);r--;)t[r]=t[r].value;return t}var gee,yee=R(()=>{"use strict";o(iDe,"baseSortBy");gee=iDe});function aDe(t,e){if(t!==e){var r=t!==void 0,n=t===null,i=t===t,a=so(t),s=e!==void 0,l=e===null,u=e===e,h=so(e);if(!l&&!h&&!a&&t>e||a&&s&&u&&!l&&!h||n&&s&&u||!r&&u||!i)return 1;if(!n&&!a&&!h&&t{"use strict";Nd();o(aDe,"compareAscending");vee=aDe});function sDe(t,e,r){for(var n=-1,i=t.criteria,a=e.criteria,s=i.length,l=r.length;++n=l)return u;var h=r[n];return u*(h=="desc"?-1:1)}}return t.index-e.index}var bee,wee=R(()=>{"use strict";xee();o(sDe,"compareMultiple");bee=sDe});function oDe(t,e,r){e.length?e=Ss(e,function(a){return wt(a)?function(s){return Yh(s,a.length===1?a[0]:a)}:a}):e=[ea];var n=-1;e=Ss(e,Oo(cn));var i=Uw(t,function(a,s,l){var u=Ss(e,function(h){return h(a)});return{criteria:u,index:++n,value:a}});return gee(i,function(a,s){return bee(a,s,r)})}var Tee,kee=R(()=>{"use strict";Md();Dv();Qa();DL();yee();Td();wee();Eu();Bn();o(oDe,"baseOrderBy");Tee=oDe});var lDe,Eee,Cee=R(()=>{"use strict";SL();lDe=Pw("length"),Eee=lDe});function bDe(t){for(var e=See.lastIndex=0;See.test(t);)++e;return e}var Aee,cDe,uDe,hDe,fDe,dDe,pDe,zL,GL,mDe,_ee,Lee,Dee,gDe,Ree,Nee,yDe,vDe,xDe,See,Mee,Iee=R(()=>{"use strict";Aee="\\ud800-\\udfff",cDe="\\u0300-\\u036f",uDe="\\ufe20-\\ufe2f",hDe="\\u20d0-\\u20ff",fDe=cDe+uDe+hDe,dDe="\\ufe0e\\ufe0f",pDe="["+Aee+"]",zL="["+fDe+"]",GL="\\ud83c[\\udffb-\\udfff]",mDe="(?:"+zL+"|"+GL+")",_ee="[^"+Aee+"]",Lee="(?:\\ud83c[\\udde6-\\uddff]){2}",Dee="[\\ud800-\\udbff][\\udc00-\\udfff]",gDe="\\u200d",Ree=mDe+"?",Nee="["+dDe+"]?",yDe="(?:"+gDe+"(?:"+[_ee,Lee,Dee].join("|")+")"+Nee+Ree+")*",vDe=Nee+Ree+yDe,xDe="(?:"+[_ee+zL+"?",zL,Lee,Dee,pDe].join("|")+")",See=RegExp(GL+"(?="+GL+")|"+xDe+vDe,"g");o(bDe,"unicodeSize");Mee=bDe});function wDe(t){return YQ(t)?Mee(t):Eee(t)}var Oee,Pee=R(()=>{"use strict";Cee();WQ();Iee();o(wDe,"stringSize");Oee=wDe});function TDe(t,e){return jw(t,e,function(r,n){return Ow(t,n)})}var Bee,Fee=R(()=>{"use strict";FL();CL();o(TDe,"basePick");Bee=TDe});var kDe,Fd,zee=R(()=>{"use strict";Fee();HQ();kDe=UQ(function(t,e){return t==null?{}:Bee(t,e)}),Fd=kDe});function SDe(t,e,r,n){for(var i=-1,a=CDe(EDe((e-t)/(r||1)),0),s=Array(a);a--;)s[n?a:++i]=t,t+=r;return s}var EDe,CDe,Gee,$ee=R(()=>{"use strict";EDe=Math.ceil,CDe=Math.max;o(SDe,"baseRange");Gee=SDe});function ADe(t){return function(e,r,n){return n&&typeof n!="number"&&eo(e,r,n)&&(r=n=void 0),e=vm(e),r===void 0?(r=e,e=0):r=vm(r),n=n===void 0?e{"use strict";$ee();Ed();rL();o(ADe,"createRange");Vee=ADe});var _De,Go,Hee=R(()=>{"use strict";Uee();_De=Vee(),Go=_De});function LDe(t,e,r,n,i){return i(t,function(a,s,l){r=n?(n=!1,a):e(r,a,s,l)}),r}var Yee,Wee=R(()=>{"use strict";o(LDe,"baseReduce");Yee=LDe});function DDe(t,e,r){var n=wt(t)?qQ:Yee,i=arguments.length<3;return n(t,cn(e,4),r,i,As)}var Vr,$L=R(()=>{"use strict";XQ();qh();Qa();Wee();Bn();o(DDe,"reduce");Vr=DDe});function RDe(t,e){var r=wt(t)?Sm:Vw;return r(t,hee(cn(e,3)))}var Kh,qee=R(()=>{"use strict";pw();_L();Qa();Bn();fee();o(RDe,"reject");Kh=RDe});function IDe(t){if(t==null)return 0;if(ei(t))return di(t)?Oee(t):t.length;var e=oo(t);return e==NDe||e==MDe?t.size:wm(t).length}var NDe,MDe,VL,Xee=R(()=>{"use strict";lw();Od();Io();Hw();Pee();NDe="[object Map]",MDe="[object Set]";o(IDe,"size");VL=IDe});function ODe(t,e){var r;return As(t,function(n,i,a){return r=e(n,i,a),!r}),!!r}var jee,Kee=R(()=>{"use strict";qh();o(ODe,"baseSome");jee=ODe});function PDe(t,e,r){var n=wt(t)?Sw:jee;return r&&eo(t,e,r)&&(e=void 0),n(t,cn(e,3))}var Nv,Qee=R(()=>{"use strict";vL();Qa();Kee();Bn();Ed();o(PDe,"some");Nv=PDe});var BDe,Tc,Zee=R(()=>{"use strict";Cm();kee();sm();Ed();BDe=pc(function(t,e){if(t==null)return[];var r=e.length;return r>1&&eo(t,e[0],e[1])?e=[]:r>2&&eo(e[0],e[1],e[2])&&(e=[e[0]]),Tee(t,bc(e,1),[])}),Tc=BDe});var FDe,zDe,Jee,ete=R(()=>{"use strict";pL();nL();Lw();FDe=1/0,zDe=Wh&&1/Dm(new Wh([,-0]))[1]==FDe?function(t){return new Wh(t)}:qn,Jee=zDe});function $De(t,e,r){var n=-1,i=ow,a=t.length,s=!0,l=[],u=l;if(r)s=!1,i=zw;else if(a>=GDe){var h=e?null:Jee(t);if(h)return Dm(h);s=!1,i=Lm,u=new _m}else u=e?[]:l;e:for(;++n{"use strict";Cw();sL();AL();Aw();ete();Lw();GDe=200;o($De,"baseUniq");Om=$De});var VDe,UL,tte=R(()=>{"use strict";Cm();sm();Kw();u5();VDe=pc(function(t){return Om(bc(t,1,wd,!0))}),UL=VDe});function UDe(t){return t&&t.length?Om(t):[]}var Pm,rte=R(()=>{"use strict";Kw();o(UDe,"uniq");Pm=UDe});function HDe(t,e){return t&&t.length?Om(t,cn(e,2)):[]}var nte,ite=R(()=>{"use strict";Qa();Kw();o(HDe,"uniqBy");nte=HDe});function WDe(t){var e=++YDe;return uw(t)+e}var YDe,zd,ate=R(()=>{"use strict";oL();YDe=0;o(WDe,"uniqueId");zd=WDe});function qDe(t,e,r){for(var n=-1,i=t.length,a=e.length,s={};++n{"use strict";o(qDe,"baseZipObject");ste=qDe});function XDe(t,e){return ste(t||[],e||[],dc)}var Qw,lte=R(()=>{"use strict";am();ote();o(XDe,"zipObject");Qw=XDe});var Pt=R(()=>{"use strict";NQ();gL();PZ();BZ();Y_();CJ();_J();DJ();RJ();NJ();BJ();LL();VJ();HJ();RL();fw();$w();YJ();WJ();qJ();ZJ();Eu();tee();ree();Bn();Ww();Jy();Js();see();Hw();oee();vc();LJ();Mm();lee();cee();X_();BL();uee();nL();kJ();zee();mee();Hee();$L();qee();Xee();Qee();Zee();tte();rte();ate();OL();lte();});function ute(t,e){t[e]?t[e]++:t[e]=1}function hte(t,e){--t[e]||delete t[e]}function Mv(t,e,r,n){var i=""+e,a=""+r;if(!t&&i>a){var s=i;i=a,a=s}return i+cte+a+cte+(er(n)?jDe:n)}function KDe(t,e,r,n){var i=""+e,a=""+r;if(!t&&i>a){var s=i;i=a,a=s}var l={v:i,w:a};return n&&(l.name=n),l}function HL(t,e){return Mv(t,e.v,e.w,e.name)}var jDe,Gd,cte,lr,Zw=R(()=>{"use strict";Pt();jDe="\0",Gd="\0",cte="",lr=class{static{o(this,"Graph")}constructor(e={}){this._isDirected=Xe(e,"directed")?e.directed:!0,this._isMultigraph=Xe(e,"multigraph")?e.multigraph:!1,this._isCompound=Xe(e,"compound")?e.compound:!1,this._label=void 0,this._defaultNodeLabelFn=ws(void 0),this._defaultEdgeLabelFn=ws(void 0),this._nodes={},this._isCompound&&(this._parent={},this._children={},this._children[Gd]={}),this._in={},this._preds={},this._out={},this._sucs={},this._edgeObjs={},this._edgeLabels={}}isDirected(){return this._isDirected}isMultigraph(){return this._isMultigraph}isCompound(){return this._isCompound}setGraph(e){return this._label=e,this}graph(){return this._label}setDefaultNodeLabel(e){return wi(e)||(e=ws(e)),this._defaultNodeLabelFn=e,this}nodeCount(){return this._nodeCount}nodes(){return Dr(this._nodes)}sources(){var e=this;return $r(this.nodes(),function(r){return Qt(e._in[r])})}sinks(){var e=this;return $r(this.nodes(),function(r){return Qt(e._out[r])})}setNodes(e,r){var n=arguments,i=this;return Ee(e,function(a){n.length>1?i.setNode(a,r):i.setNode(a)}),this}setNode(e,r){return Xe(this._nodes,e)?(arguments.length>1&&(this._nodes[e]=r),this):(this._nodes[e]=arguments.length>1?r:this._defaultNodeLabelFn(e),this._isCompound&&(this._parent[e]=Gd,this._children[e]={},this._children[Gd][e]=!0),this._in[e]={},this._preds[e]={},this._out[e]={},this._sucs[e]={},++this._nodeCount,this)}node(e){return this._nodes[e]}hasNode(e){return Xe(this._nodes,e)}removeNode(e){var r=this;if(Xe(this._nodes,e)){var n=o(function(i){r.removeEdge(r._edgeObjs[i])},"removeEdge");delete this._nodes[e],this._isCompound&&(this._removeFromParentsChildList(e),delete this._parent[e],Ee(this.children(e),function(i){r.setParent(i)}),delete this._children[e]),Ee(Dr(this._in[e]),n),delete this._in[e],delete this._preds[e],Ee(Dr(this._out[e]),n),delete this._out[e],delete this._sucs[e],--this._nodeCount}return this}setParent(e,r){if(!this._isCompound)throw new Error("Cannot set parent in a non-compound graph");if(er(r))r=Gd;else{r+="";for(var n=r;!er(n);n=this.parent(n))if(n===e)throw new Error("Setting "+r+" as parent of "+e+" would create a cycle");this.setNode(r)}return this.setNode(e),this._removeFromParentsChildList(e),this._parent[e]=r,this._children[r][e]=!0,this}_removeFromParentsChildList(e){delete this._children[this._parent[e]][e]}parent(e){if(this._isCompound){var r=this._parent[e];if(r!==Gd)return r}}children(e){if(er(e)&&(e=Gd),this._isCompound){var r=this._children[e];if(r)return Dr(r)}else{if(e===Gd)return this.nodes();if(this.hasNode(e))return[]}}predecessors(e){var r=this._preds[e];if(r)return Dr(r)}successors(e){var r=this._sucs[e];if(r)return Dr(r)}neighbors(e){var r=this.predecessors(e);if(r)return UL(r,this.successors(e))}isLeaf(e){var r;return this.isDirected()?r=this.successors(e):r=this.neighbors(e),r.length===0}filterNodes(e){var r=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});r.setGraph(this.graph());var n=this;Ee(this._nodes,function(s,l){e(l)&&r.setNode(l,s)}),Ee(this._edgeObjs,function(s){r.hasNode(s.v)&&r.hasNode(s.w)&&r.setEdge(s,n.edge(s))});var i={};function a(s){var l=n.parent(s);return l===void 0||r.hasNode(l)?(i[s]=l,l):l in i?i[l]:a(l)}return o(a,"findParent"),this._isCompound&&Ee(r.nodes(),function(s){r.setParent(s,a(s))}),r}setDefaultEdgeLabel(e){return wi(e)||(e=ws(e)),this._defaultEdgeLabelFn=e,this}edgeCount(){return this._edgeCount}edges(){return or(this._edgeObjs)}setPath(e,r){var n=this,i=arguments;return Vr(e,function(a,s){return i.length>1?n.setEdge(a,s,r):n.setEdge(a,s),s}),this}setEdge(){var e,r,n,i,a=!1,s=arguments[0];typeof s=="object"&&s!==null&&"v"in s?(e=s.v,r=s.w,n=s.name,arguments.length===2&&(i=arguments[1],a=!0)):(e=s,r=arguments[1],n=arguments[3],arguments.length>2&&(i=arguments[2],a=!0)),e=""+e,r=""+r,er(n)||(n=""+n);var l=Mv(this._isDirected,e,r,n);if(Xe(this._edgeLabels,l))return a&&(this._edgeLabels[l]=i),this;if(!er(n)&&!this._isMultigraph)throw new Error("Cannot set a named edge when isMultigraph = false");this.setNode(e),this.setNode(r),this._edgeLabels[l]=a?i:this._defaultEdgeLabelFn(e,r,n);var u=KDe(this._isDirected,e,r,n);return e=u.v,r=u.w,Object.freeze(u),this._edgeObjs[l]=u,ute(this._preds[r],e),ute(this._sucs[e],r),this._in[r][l]=u,this._out[e][l]=u,this._edgeCount++,this}edge(e,r,n){var i=arguments.length===1?HL(this._isDirected,arguments[0]):Mv(this._isDirected,e,r,n);return this._edgeLabels[i]}hasEdge(e,r,n){var i=arguments.length===1?HL(this._isDirected,arguments[0]):Mv(this._isDirected,e,r,n);return Xe(this._edgeLabels,i)}removeEdge(e,r,n){var i=arguments.length===1?HL(this._isDirected,arguments[0]):Mv(this._isDirected,e,r,n),a=this._edgeObjs[i];return a&&(e=a.v,r=a.w,delete this._edgeLabels[i],delete this._edgeObjs[i],hte(this._preds[r],e),hte(this._sucs[e],r),delete this._in[r][i],delete this._out[e][i],this._edgeCount--),this}inEdges(e,r){var n=this._in[e];if(n){var i=or(n);return r?$r(i,function(a){return a.v===r}):i}}outEdges(e,r){var n=this._out[e];if(n){var i=or(n);return r?$r(i,function(a){return a.w===r}):i}}nodeEdges(e,r){var n=this.inEdges(e,r);if(n)return n.concat(this.outEdges(e,r))}};lr.prototype._nodeCount=0;lr.prototype._edgeCount=0;o(ute,"incrementOrInitEntry");o(hte,"decrementOrRemoveEntry");o(Mv,"edgeArgsToId");o(KDe,"edgeArgsToObj");o(HL,"edgeObjToId")});var ya=R(()=>{"use strict";Zw()});function fte(t){t._prev._next=t._next,t._next._prev=t._prev,delete t._next,delete t._prev}function QDe(t,e){if(t!=="_next"&&t!=="_prev")return e}var Jw,dte=R(()=>{"use strict";Jw=class{static{o(this,"List")}constructor(){var e={};e._next=e._prev=e,this._sentinel=e}dequeue(){var e=this._sentinel,r=e._prev;if(r!==e)return fte(r),r}enqueue(e){var r=this._sentinel;e._prev&&e._next&&fte(e),e._next=r._next,r._next._prev=e,r._next=e,e._prev=r}toString(){for(var e=[],r=this._sentinel,n=r._prev;n!==r;)e.push(JSON.stringify(n,QDe)),n=n._prev;return"["+e.join(", ")+"]"}};o(fte,"unlink");o(QDe,"filterOutLinks")});function pte(t,e){if(t.nodeCount()<=1)return[];var r=eRe(t,e||ZDe),n=JDe(r.graph,r.buckets,r.zeroIdx);return Gr(qe(n,function(i){return t.outEdges(i.v,i.w)}))}function JDe(t,e,r){for(var n=[],i=e[e.length-1],a=e[0],s;t.nodeCount();){for(;s=a.dequeue();)YL(t,e,r,s);for(;s=i.dequeue();)YL(t,e,r,s);if(t.nodeCount()){for(var l=e.length-2;l>0;--l)if(s=e[l].dequeue(),s){n=n.concat(YL(t,e,r,s,!0));break}}}return n}function YL(t,e,r,n,i){var a=i?[]:void 0;return Ee(t.inEdges(n.v),function(s){var l=t.edge(s),u=t.node(s.v);i&&a.push({v:s.v,w:s.w}),u.out-=l,WL(e,r,u)}),Ee(t.outEdges(n.v),function(s){var l=t.edge(s),u=s.w,h=t.node(u);h.in-=l,WL(e,r,h)}),t.removeNode(n.v),a}function eRe(t,e){var r=new lr,n=0,i=0;Ee(t.nodes(),function(l){r.setNode(l,{v:l,in:0,out:0})}),Ee(t.edges(),function(l){var u=r.edge(l.v,l.w)||0,h=e(l),f=u+h;r.setEdge(l.v,l.w,f),i=Math.max(i,r.node(l.v).out+=h),n=Math.max(n,r.node(l.w).in+=h)});var a=Go(i+n+3).map(function(){return new Jw}),s=n+1;return Ee(r.nodes(),function(l){WL(a,s,r.node(l))}),{graph:r,buckets:a,zeroIdx:s}}function WL(t,e,r){r.out?r.in?t[r.out-r.in+e].enqueue(r):t[t.length-1].enqueue(r):t[0].enqueue(r)}var ZDe,mte=R(()=>{"use strict";Pt();ya();dte();ZDe=ws(1);o(pte,"greedyFAS");o(JDe,"doGreedyFAS");o(YL,"removeNode");o(eRe,"buildState");o(WL,"assignBucket")});function gte(t){var e=t.graph().acyclicer==="greedy"?pte(t,r(t)):tRe(t);Ee(e,function(n){var i=t.edge(n);t.removeEdge(n),i.forwardName=n.name,i.reversed=!0,t.setEdge(n.w,n.v,i,zd("rev"))});function r(n){return function(i){return n.edge(i).weight}}o(r,"weightFn")}function tRe(t){var e=[],r={},n={};function i(a){Xe(n,a)||(n[a]=!0,r[a]=!0,Ee(t.outEdges(a),function(s){Xe(r,s.w)?e.push(s):i(s.w)}),delete r[a])}return o(i,"dfs"),Ee(t.nodes(),i),e}function yte(t){Ee(t.edges(),function(e){var r=t.edge(e);if(r.reversed){t.removeEdge(e);var n=r.forwardName;delete r.reversed,delete r.forwardName,t.setEdge(e.w,e.v,r,n)}})}var qL=R(()=>{"use strict";Pt();mte();o(gte,"run");o(tRe,"dfsFAS");o(yte,"undo")});function kc(t,e,r,n){var i;do i=zd(n);while(t.hasNode(i));return r.dummy=e,t.setNode(i,r),i}function xte(t){var e=new lr().setGraph(t.graph());return Ee(t.nodes(),function(r){e.setNode(r,t.node(r))}),Ee(t.edges(),function(r){var n=e.edge(r.v,r.w)||{weight:0,minlen:1},i=t.edge(r);e.setEdge(r.v,r.w,{weight:n.weight+i.weight,minlen:Math.max(n.minlen,i.minlen)})}),e}function eT(t){var e=new lr({multigraph:t.isMultigraph()}).setGraph(t.graph());return Ee(t.nodes(),function(r){t.children(r).length||e.setNode(r,t.node(r))}),Ee(t.edges(),function(r){e.setEdge(r,t.edge(r))}),e}function XL(t,e){var r=t.x,n=t.y,i=e.x-r,a=e.y-n,s=t.width/2,l=t.height/2;if(!i&&!a)throw new Error("Not possible to find intersection inside of the rectangle");var u,h;return Math.abs(a)*s>Math.abs(i)*l?(a<0&&(l=-l),u=l*i/a,h=l):(i<0&&(s=-s),u=s,h=s*a/i),{x:r+u,y:n+h}}function Qh(t){var e=qe(Go(KL(t)+1),function(){return[]});return Ee(t.nodes(),function(r){var n=t.node(r),i=n.rank;er(i)||(e[i][n.order]=r)}),e}function bte(t){var e=Ll(qe(t.nodes(),function(r){return t.node(r).rank}));Ee(t.nodes(),function(r){var n=t.node(r);Xe(n,"rank")&&(n.rank-=e)})}function wte(t){var e=Ll(qe(t.nodes(),function(a){return t.node(a).rank})),r=[];Ee(t.nodes(),function(a){var s=t.node(a).rank-e;r[s]||(r[s]=[]),r[s].push(a)});var n=0,i=t.graph().nodeRankFactor;Ee(r,function(a,s){er(a)&&s%i!==0?--n:n&&Ee(a,function(l){t.node(l).rank+=n})})}function jL(t,e,r,n){var i={width:0,height:0};return arguments.length>=4&&(i.rank=r,i.order=n),kc(t,"border",i,e)}function KL(t){return _s(qe(t.nodes(),function(e){var r=t.node(e).rank;if(!er(r))return r}))}function Tte(t,e){var r={lhs:[],rhs:[]};return Ee(t,function(n){e(n)?r.lhs.push(n):r.rhs.push(n)}),r}function kte(t,e){var r=Fw();try{return e()}finally{console.log(t+" time: "+(Fw()-r)+"ms")}}function Ete(t,e){return e()}var Ec=R(()=>{"use strict";Pt();ya();o(kc,"addDummyNode");o(xte,"simplify");o(eT,"asNonCompoundGraph");o(XL,"intersectRect");o(Qh,"buildLayerMatrix");o(bte,"normalizeRanks");o(wte,"removeEmptyRanks");o(jL,"addBorderNode");o(KL,"maxRank");o(Tte,"partition");o(kte,"time");o(Ete,"notime")});function Ste(t){function e(r){var n=t.children(r),i=t.node(r);if(n.length&&Ee(n,e),Xe(i,"minRank")){i.borderLeft=[],i.borderRight=[];for(var a=i.minRank,s=i.maxRank+1;a{"use strict";Pt();Ec();o(Ste,"addBorderSegments");o(Cte,"addBorderNode")});function Lte(t){var e=t.graph().rankdir.toLowerCase();(e==="lr"||e==="rl")&&Rte(t)}function Dte(t){var e=t.graph().rankdir.toLowerCase();(e==="bt"||e==="rl")&&rRe(t),(e==="lr"||e==="rl")&&(nRe(t),Rte(t))}function Rte(t){Ee(t.nodes(),function(e){_te(t.node(e))}),Ee(t.edges(),function(e){_te(t.edge(e))})}function _te(t){var e=t.width;t.width=t.height,t.height=e}function rRe(t){Ee(t.nodes(),function(e){QL(t.node(e))}),Ee(t.edges(),function(e){var r=t.edge(e);Ee(r.points,QL),Xe(r,"y")&&QL(r)})}function QL(t){t.y=-t.y}function nRe(t){Ee(t.nodes(),function(e){ZL(t.node(e))}),Ee(t.edges(),function(e){var r=t.edge(e);Ee(r.points,ZL),Xe(r,"x")&&ZL(r)})}function ZL(t){var e=t.x;t.x=t.y,t.y=e}var Nte=R(()=>{"use strict";Pt();o(Lte,"adjust");o(Dte,"undo");o(Rte,"swapWidthHeight");o(_te,"swapWidthHeightOne");o(rRe,"reverseY");o(QL,"reverseYOne");o(nRe,"swapXY");o(ZL,"swapXYOne")});function Mte(t){t.graph().dummyChains=[],Ee(t.edges(),function(e){aRe(t,e)})}function aRe(t,e){var r=e.v,n=t.node(r).rank,i=e.w,a=t.node(i).rank,s=e.name,l=t.edge(e),u=l.labelRank;if(a!==n+1){t.removeEdge(e);var h,f,d;for(d=0,++n;n{"use strict";Pt();Ec();o(Mte,"run");o(aRe,"normalizeEdge");o(Ite,"undo")});function Iv(t){var e={};function r(n){var i=t.node(n);if(Xe(e,n))return i.rank;e[n]=!0;var a=Ll(qe(t.outEdges(n),function(s){return r(s.w)-t.edge(s).minlen}));return(a===Number.POSITIVE_INFINITY||a===void 0||a===null)&&(a=0),i.rank=a}o(r,"dfs"),Ee(t.sources(),r)}function $d(t,e){return t.node(e.w).rank-t.node(e.v).rank-t.edge(e).minlen}var tT=R(()=>{"use strict";Pt();o(Iv,"longestPath");o($d,"slack")});function rT(t){var e=new lr({directed:!1}),r=t.nodes()[0],n=t.nodeCount();e.setNode(r,{});for(var i,a;sRe(e,t){"use strict";Pt();ya();tT();o(rT,"feasibleTree");o(sRe,"tightTree");o(oRe,"findMinSlackEdge");o(lRe,"shiftRanks")});var Pte=R(()=>{"use strict"});var tD=R(()=>{"use strict"});var tGt,rD=R(()=>{"use strict";Pt();tD();tGt=ws(1)});var Bte=R(()=>{"use strict";rD()});var nD=R(()=>{"use strict"});var Fte=R(()=>{"use strict";nD()});var fGt,zte=R(()=>{"use strict";Pt();fGt=ws(1)});function iD(t){var e={},r={},n=[];function i(a){if(Xe(r,a))throw new Ov;Xe(e,a)||(r[a]=!0,e[a]=!0,Ee(t.predecessors(a),i),delete r[a],n.push(a))}if(o(i,"visit"),Ee(t.sinks(),i),VL(e)!==t.nodeCount())throw new Ov;return n}function Ov(){}var aD=R(()=>{"use strict";Pt();iD.CycleException=Ov;o(iD,"topsort");o(Ov,"CycleException");Ov.prototype=new Error});var Gte=R(()=>{"use strict";aD()});function nT(t,e,r){wt(e)||(e=[e]);var n=(t.isDirected()?t.successors:t.neighbors).bind(t),i=[],a={};return Ee(e,function(s){if(!t.hasNode(s))throw new Error("Graph does not have node: "+s);$te(t,s,r==="post",a,n,i)}),i}function $te(t,e,r,n,i,a){Xe(n,e)||(n[e]=!0,r||a.push(e),Ee(i(e),function(s){$te(t,s,r,n,i,a)}),r&&a.push(e))}var sD=R(()=>{"use strict";Pt();o(nT,"dfs");o($te,"doDfs")});function oD(t,e){return nT(t,e,"post")}var Vte=R(()=>{"use strict";sD();o(oD,"postorder")});function lD(t,e){return nT(t,e,"pre")}var Ute=R(()=>{"use strict";sD();o(lD,"preorder")});var Hte=R(()=>{"use strict";tD();Zw()});var Yte=R(()=>{"use strict";Pte();rD();Bte();Fte();zte();Gte();Vte();Ute();Hte();nD();aD()});function Jh(t){t=xte(t),Iv(t);var e=rT(t);uD(e),cD(e,t);for(var r,n;r=jte(e);)n=Kte(e,t,r),Qte(e,t,r,n)}function cD(t,e){var r=oD(t,t.nodes());r=r.slice(0,r.length-1),Ee(r,function(n){dRe(t,e,n)})}function dRe(t,e,r){var n=t.node(r),i=n.parent;t.edge(r,i).cutvalue=qte(t,e,r)}function qte(t,e,r){var n=t.node(r),i=n.parent,a=!0,s=e.edge(r,i),l=0;return s||(a=!1,s=e.edge(i,r)),l=s.weight,Ee(e.nodeEdges(r),function(u){var h=u.v===r,f=h?u.w:u.v;if(f!==i){var d=h===a,p=e.edge(u).weight;if(l+=d?p:-p,mRe(t,r,f)){var m=t.edge(r,f).cutvalue;l+=d?-m:m}}}),l}function uD(t,e){arguments.length<2&&(e=t.nodes()[0]),Xte(t,{},1,e)}function Xte(t,e,r,n,i){var a=r,s=t.node(n);return e[n]=!0,Ee(t.neighbors(n),function(l){Xe(e,l)||(r=Xte(t,e,r,l,n))}),s.low=a,s.lim=r++,i?s.parent=i:delete s.parent,r}function jte(t){return Za(t.edges(),function(e){return t.edge(e).cutvalue<0})}function Kte(t,e,r){var n=r.v,i=r.w;e.hasEdge(n,i)||(n=r.w,i=r.v);var a=t.node(n),s=t.node(i),l=a,u=!1;a.lim>s.lim&&(l=s,u=!0);var h=$r(e.edges(),function(f){return u===Wte(t,t.node(f.v),l)&&u!==Wte(t,t.node(f.w),l)});return Bd(h,function(f){return $d(e,f)})}function Qte(t,e,r,n){var i=r.v,a=r.w;t.removeEdge(i,a),t.setEdge(n.v,n.w,{}),uD(t),cD(t,e),pRe(t,e)}function pRe(t,e){var r=Za(t.nodes(),function(i){return!e.node(i).parent}),n=lD(t,r);n=n.slice(1),Ee(n,function(i){var a=t.node(i).parent,s=e.edge(i,a),l=!1;s||(s=e.edge(a,i),l=!0),e.node(i).rank=e.node(a).rank+(l?s.minlen:-s.minlen)})}function mRe(t,e,r){return t.hasEdge(e,r)}function Wte(t,e,r){return r.low<=e.lim&&e.lim<=r.lim}var Zte=R(()=>{"use strict";Pt();Yte();Ec();eD();tT();Jh.initLowLimValues=uD;Jh.initCutValues=cD;Jh.calcCutValue=qte;Jh.leaveEdge=jte;Jh.enterEdge=Kte;Jh.exchangeEdges=Qte;o(Jh,"networkSimplex");o(cD,"initCutValues");o(dRe,"assignCutValue");o(qte,"calcCutValue");o(uD,"initLowLimValues");o(Xte,"dfsAssignLowLim");o(jte,"leaveEdge");o(Kte,"enterEdge");o(Qte,"exchangeEdges");o(pRe,"updateRanks");o(mRe,"isTreeEdge");o(Wte,"isDescendant")});function hD(t){switch(t.graph().ranker){case"network-simplex":Jte(t);break;case"tight-tree":yRe(t);break;case"longest-path":gRe(t);break;default:Jte(t)}}function yRe(t){Iv(t),rT(t)}function Jte(t){Jh(t)}var gRe,fD=R(()=>{"use strict";eD();Zte();tT();o(hD,"rank");gRe=Iv;o(yRe,"tightTreeRanker");o(Jte,"networkSimplexRanker")});function ere(t){var e=kc(t,"root",{},"_root"),r=vRe(t),n=_s(or(r))-1,i=2*n+1;t.graph().nestingRoot=e,Ee(t.edges(),function(s){t.edge(s).minlen*=i});var a=xRe(t)+1;Ee(t.children(),function(s){tre(t,e,i,a,n,r,s)}),t.graph().nodeRankFactor=i}function tre(t,e,r,n,i,a,s){var l=t.children(s);if(!l.length){s!==e&&t.setEdge(e,s,{weight:0,minlen:r});return}var u=jL(t,"_bt"),h=jL(t,"_bb"),f=t.node(s);t.setParent(u,s),f.borderTop=u,t.setParent(h,s),f.borderBottom=h,Ee(l,function(d){tre(t,e,r,n,i,a,d);var p=t.node(d),m=p.borderTop?p.borderTop:d,g=p.borderBottom?p.borderBottom:d,y=p.borderTop?n:2*n,v=m!==g?1:i-a[s]+1;t.setEdge(u,m,{weight:y,minlen:v,nestingEdge:!0}),t.setEdge(g,h,{weight:y,minlen:v,nestingEdge:!0})}),t.parent(s)||t.setEdge(e,u,{weight:0,minlen:i+a[s]})}function vRe(t){var e={};function r(n,i){var a=t.children(n);a&&a.length&&Ee(a,function(s){r(s,i+1)}),e[n]=i}return o(r,"dfs"),Ee(t.children(),function(n){r(n,1)}),e}function xRe(t){return Vr(t.edges(),function(e,r){return e+t.edge(r).weight},0)}function rre(t){var e=t.graph();t.removeNode(e.nestingRoot),delete e.nestingRoot,Ee(t.edges(),function(r){var n=t.edge(r);n.nestingEdge&&t.removeEdge(r)})}var nre=R(()=>{"use strict";Pt();Ec();o(ere,"run");o(tre,"dfs");o(vRe,"treeDepths");o(xRe,"sumWeights");o(rre,"cleanup")});function ire(t,e,r){var n={},i;Ee(r,function(a){for(var s=t.parent(a),l,u;s;){if(l=t.parent(s),l?(u=n[l],n[l]=s):(u=i,i=s),u&&u!==s){e.setEdge(u,s);return}s=l}})}var are=R(()=>{"use strict";Pt();o(ire,"addSubgraphConstraints")});function sre(t,e,r){var n=wRe(t),i=new lr({compound:!0}).setGraph({root:n}).setDefaultNodeLabel(function(a){return t.node(a)});return Ee(t.nodes(),function(a){var s=t.node(a),l=t.parent(a);(s.rank===e||s.minRank<=e&&e<=s.maxRank)&&(i.setNode(a),i.setParent(a,l||n),Ee(t[r](a),function(u){var h=u.v===a?u.w:u.v,f=i.edge(h,a),d=er(f)?0:f.weight;i.setEdge(h,a,{weight:t.edge(u).weight+d})}),Xe(s,"minRank")&&i.setNode(a,{borderLeft:s.borderLeft[e],borderRight:s.borderRight[e]}))}),i}function wRe(t){for(var e;t.hasNode(e=zd("_root")););return e}var ore=R(()=>{"use strict";Pt();ya();o(sre,"buildLayerGraph");o(wRe,"createRootNode")});function lre(t,e){for(var r=0,n=1;n0;)f%2&&(d+=l[f+1]),f=f-1>>1,l[f]+=h.weight;u+=h.weight*d})),u}var cre=R(()=>{"use strict";Pt();o(lre,"crossCount");o(TRe,"twoLayerCrossCount")});function ure(t){var e={},r=$r(t.nodes(),function(l){return!t.children(l).length}),n=_s(qe(r,function(l){return t.node(l).rank})),i=qe(Go(n+1),function(){return[]});function a(l){if(!Xe(e,l)){e[l]=!0;var u=t.node(l);i[u.rank].push(l),Ee(t.successors(l),a)}}o(a,"dfs");var s=Tc(r,function(l){return t.node(l).rank});return Ee(s,a),i}var hre=R(()=>{"use strict";Pt();o(ure,"initOrder")});function fre(t,e){return qe(e,function(r){var n=t.inEdges(r);if(n.length){var i=Vr(n,function(a,s){var l=t.edge(s),u=t.node(s.v);return{sum:a.sum+l.weight*u.order,weight:a.weight+l.weight}},{sum:0,weight:0});return{v:r,barycenter:i.sum/i.weight,weight:i.weight}}else return{v:r}})}var dre=R(()=>{"use strict";Pt();o(fre,"barycenter")});function pre(t,e){var r={};Ee(t,function(i,a){var s=r[i.v]={indegree:0,in:[],out:[],vs:[i.v],i:a};er(i.barycenter)||(s.barycenter=i.barycenter,s.weight=i.weight)}),Ee(e.edges(),function(i){var a=r[i.v],s=r[i.w];!er(a)&&!er(s)&&(s.indegree++,a.out.push(r[i.w]))});var n=$r(r,function(i){return!i.indegree});return kRe(n)}function kRe(t){var e=[];function r(a){return function(s){s.merged||(er(s.barycenter)||er(a.barycenter)||s.barycenter>=a.barycenter)&&ERe(a,s)}}o(r,"handleIn");function n(a){return function(s){s.in.push(a),--s.indegree===0&&t.push(s)}}for(o(n,"handleOut");t.length;){var i=t.pop();e.push(i),Ee(i.in.reverse(),r(i)),Ee(i.out,n(i))}return qe($r(e,function(a){return!a.merged}),function(a){return Fd(a,["vs","i","barycenter","weight"])})}function ERe(t,e){var r=0,n=0;t.weight&&(r+=t.barycenter*t.weight,n+=t.weight),e.weight&&(r+=e.barycenter*e.weight,n+=e.weight),t.vs=e.vs.concat(t.vs),t.barycenter=r/n,t.weight=n,t.i=Math.min(e.i,t.i),e.merged=!0}var mre=R(()=>{"use strict";Pt();o(pre,"resolveConflicts");o(kRe,"doResolveConflicts");o(ERe,"mergeEntries")});function yre(t,e){var r=Tte(t,function(f){return Xe(f,"barycenter")}),n=r.lhs,i=Tc(r.rhs,function(f){return-f.i}),a=[],s=0,l=0,u=0;n.sort(CRe(!!e)),u=gre(a,i,u),Ee(n,function(f){u+=f.vs.length,a.push(f.vs),s+=f.barycenter*f.weight,l+=f.weight,u=gre(a,i,u)});var h={vs:Gr(a)};return l&&(h.barycenter=s/l,h.weight=l),h}function gre(t,e,r){for(var n;e.length&&(n=ma(e)).i<=r;)e.pop(),t.push(n.vs),r++;return r}function CRe(t){return function(e,r){return e.barycenterr.barycenter?1:t?r.i-e.i:e.i-r.i}}var vre=R(()=>{"use strict";Pt();Ec();o(yre,"sort");o(gre,"consumeUnsortable");o(CRe,"compareWithBias")});function dD(t,e,r,n){var i=t.children(e),a=t.node(e),s=a?a.borderLeft:void 0,l=a?a.borderRight:void 0,u={};s&&(i=$r(i,function(g){return g!==s&&g!==l}));var h=fre(t,i);Ee(h,function(g){if(t.children(g.v).length){var y=dD(t,g.v,r,n);u[g.v]=y,Xe(y,"barycenter")&&ARe(g,y)}});var f=pre(h,r);SRe(f,u);var d=yre(f,n);if(s&&(d.vs=Gr([s,d.vs,l]),t.predecessors(s).length)){var p=t.node(t.predecessors(s)[0]),m=t.node(t.predecessors(l)[0]);Xe(d,"barycenter")||(d.barycenter=0,d.weight=0),d.barycenter=(d.barycenter*d.weight+p.order+m.order)/(d.weight+2),d.weight+=2}return d}function SRe(t,e){Ee(t,function(r){r.vs=Gr(r.vs.map(function(n){return e[n]?e[n].vs:n}))})}function ARe(t,e){er(t.barycenter)?(t.barycenter=e.barycenter,t.weight=e.weight):(t.barycenter=(t.barycenter*t.weight+e.barycenter*e.weight)/(t.weight+e.weight),t.weight+=e.weight)}var xre=R(()=>{"use strict";Pt();dre();mre();vre();o(dD,"sortSubgraph");o(SRe,"expandSubgraphs");o(ARe,"mergeBarycenters")});function Tre(t){var e=KL(t),r=bre(t,Go(1,e+1),"inEdges"),n=bre(t,Go(e-1,-1,-1),"outEdges"),i=ure(t);wre(t,i);for(var a=Number.POSITIVE_INFINITY,s,l=0,u=0;u<4;++l,++u){_Re(l%2?r:n,l%4>=2),i=Qh(t);var h=lre(t,i);h{"use strict";Pt();ya();Ec();are();ore();cre();hre();xre();o(Tre,"order");o(bre,"buildLayerGraphs");o(_Re,"sweepLayerGraphs");o(wre,"assignOrder")});function Ere(t){var e=DRe(t);Ee(t.graph().dummyChains,function(r){for(var n=t.node(r),i=n.edgeObj,a=LRe(t,e,i.v,i.w),s=a.path,l=a.lca,u=0,h=s[u],f=!0;r!==i.w;){if(n=t.node(r),f){for(;(h=s[u])!==l&&t.node(h).maxRanks||l>e[u].lim));for(h=u,u=n;(u=t.parent(u))!==h;)a.push(u);return{path:i.concat(a.reverse()),lca:h}}function DRe(t){var e={},r=0;function n(i){var a=r;Ee(t.children(i),n),e[i]={low:a,lim:r++}}return o(n,"dfs"),Ee(t.children(),n),e}var Cre=R(()=>{"use strict";Pt();o(Ere,"parentDummyChains");o(LRe,"findPath");o(DRe,"postorder")});function RRe(t,e){var r={};function n(i,a){var s=0,l=0,u=i.length,h=ma(a);return Ee(a,function(f,d){var p=MRe(t,f),m=p?t.node(p).order:u;(p||f===h)&&(Ee(a.slice(l,d+1),function(g){Ee(t.predecessors(g),function(y){var v=t.node(y),x=v.order;(xh)&&Sre(r,p,f)})})}o(n,"scan");function i(a,s){var l=-1,u,h=0;return Ee(s,function(f,d){if(t.node(f).dummy==="border"){var p=t.predecessors(f);p.length&&(u=t.node(p[0]).order,n(s,h,d,l,u),h=d,l=u)}n(s,h,s.length,u,a.length)}),s}return o(i,"visitLayer"),Vr(e,i),r}function MRe(t,e){if(t.node(e).dummy)return Za(t.predecessors(e),function(r){return t.node(r).dummy})}function Sre(t,e,r){if(e>r){var n=e;e=r,r=n}var i=t[e];i||(t[e]=i={}),i[r]=!0}function IRe(t,e,r){if(e>r){var n=e;e=r,r=n}return Xe(t[e],r)}function ORe(t,e,r,n){var i={},a={},s={};return Ee(e,function(l){Ee(l,function(u,h){i[u]=u,a[u]=u,s[u]=h})}),Ee(e,function(l){var u=-1;Ee(l,function(h){var f=n(h);if(f.length){f=Tc(f,function(y){return s[y]});for(var d=(f.length-1)/2,p=Math.floor(d),m=Math.ceil(d);p<=m;++p){var g=f[p];a[h]===h&&u{"use strict";Pt();ya();Ec();o(RRe,"findType1Conflicts");o(NRe,"findType2Conflicts");o(MRe,"findOtherInnerSegmentNode");o(Sre,"addConflict");o(IRe,"hasConflict");o(ORe,"verticalAlignment");o(PRe,"horizontalCompaction");o(BRe,"buildBlockGraph");o(FRe,"findSmallestWidthAlignment");o(zRe,"alignCoordinates");o(GRe,"balance");o(Are,"positionX");o($Re,"sep");o(VRe,"width")});function Lre(t){t=eT(t),URe(t),ML(Are(t),function(e,r){t.node(r).x=e})}function URe(t){var e=Qh(t),r=t.graph().ranksep,n=0;Ee(e,function(i){var a=_s(qe(i,function(s){return t.node(s).height}));Ee(i,function(s){t.node(s).y=n+a/2}),n+=a+r})}var Dre=R(()=>{"use strict";Pt();Ec();_re();o(Lre,"position");o(URe,"positionY")});function lo(t,e){var r=e&&e.debugTiming?kte:Ete;r("layout",function(){var n=r(" buildLayoutGraph",function(){return eNe(t)});r(" runLayout",function(){HRe(n,r)}),r(" updateInputGraph",function(){YRe(t,n)})})}function HRe(t,e){e(" makeSpaceForEdgeLabels",function(){tNe(t)}),e(" removeSelfEdges",function(){uNe(t)}),e(" acyclic",function(){gte(t)}),e(" nestingGraph.run",function(){ere(t)}),e(" rank",function(){hD(eT(t))}),e(" injectEdgeLabelProxies",function(){rNe(t)}),e(" removeEmptyRanks",function(){wte(t)}),e(" nestingGraph.cleanup",function(){rre(t)}),e(" normalizeRanks",function(){bte(t)}),e(" assignRankMinMax",function(){nNe(t)}),e(" removeEdgeLabelProxies",function(){iNe(t)}),e(" normalize.run",function(){Mte(t)}),e(" parentDummyChains",function(){Ere(t)}),e(" addBorderSegments",function(){Ste(t)}),e(" order",function(){Tre(t)}),e(" insertSelfEdges",function(){hNe(t)}),e(" adjustCoordinateSystem",function(){Lte(t)}),e(" position",function(){Lre(t)}),e(" positionSelfEdges",function(){fNe(t)}),e(" removeBorderNodes",function(){cNe(t)}),e(" normalize.undo",function(){Ite(t)}),e(" fixupEdgeLabelCoords",function(){oNe(t)}),e(" undoCoordinateSystem",function(){Dte(t)}),e(" translateGraph",function(){aNe(t)}),e(" assignNodeIntersects",function(){sNe(t)}),e(" reversePoints",function(){lNe(t)}),e(" acyclic.undo",function(){yte(t)})}function YRe(t,e){Ee(t.nodes(),function(r){var n=t.node(r),i=e.node(r);n&&(n.x=i.x,n.y=i.y,e.children(r).length&&(n.width=i.width,n.height=i.height))}),Ee(t.edges(),function(r){var n=t.edge(r),i=e.edge(r);n.points=i.points,Xe(i,"x")&&(n.x=i.x,n.y=i.y)}),t.graph().width=e.graph().width,t.graph().height=e.graph().height}function eNe(t){var e=new lr({multigraph:!0,compound:!0}),r=mD(t.graph());return e.setGraph(Gh({},qRe,pD(r,WRe),Fd(r,XRe))),Ee(t.nodes(),function(n){var i=mD(t.node(n));e.setNode(n,Xh(pD(i,jRe),KRe)),e.setParent(n,t.parent(n))}),Ee(t.edges(),function(n){var i=mD(t.edge(n));e.setEdge(n,Gh({},ZRe,pD(i,QRe),Fd(i,JRe)))}),e}function tNe(t){var e=t.graph();e.ranksep/=2,Ee(t.edges(),function(r){var n=t.edge(r);n.minlen*=2,n.labelpos.toLowerCase()!=="c"&&(e.rankdir==="TB"||e.rankdir==="BT"?n.width+=n.labeloffset:n.height+=n.labeloffset)})}function rNe(t){Ee(t.edges(),function(e){var r=t.edge(e);if(r.width&&r.height){var n=t.node(e.v),i=t.node(e.w),a={rank:(i.rank-n.rank)/2+n.rank,e};kc(t,"edge-proxy",a,"_ep")}})}function nNe(t){var e=0;Ee(t.nodes(),function(r){var n=t.node(r);n.borderTop&&(n.minRank=t.node(n.borderTop).rank,n.maxRank=t.node(n.borderBottom).rank,e=_s(e,n.maxRank))}),t.graph().maxRank=e}function iNe(t){Ee(t.nodes(),function(e){var r=t.node(e);r.dummy==="edge-proxy"&&(t.edge(r.e).labelRank=r.rank,t.removeNode(e))})}function aNe(t){var e=Number.POSITIVE_INFINITY,r=0,n=Number.POSITIVE_INFINITY,i=0,a=t.graph(),s=a.marginx||0,l=a.marginy||0;function u(h){var f=h.x,d=h.y,p=h.width,m=h.height;e=Math.min(e,f-p/2),r=Math.max(r,f+p/2),n=Math.min(n,d-m/2),i=Math.max(i,d+m/2)}o(u,"getExtremes"),Ee(t.nodes(),function(h){u(t.node(h))}),Ee(t.edges(),function(h){var f=t.edge(h);Xe(f,"x")&&u(f)}),e-=s,n-=l,Ee(t.nodes(),function(h){var f=t.node(h);f.x-=e,f.y-=n}),Ee(t.edges(),function(h){var f=t.edge(h);Ee(f.points,function(d){d.x-=e,d.y-=n}),Xe(f,"x")&&(f.x-=e),Xe(f,"y")&&(f.y-=n)}),a.width=r-e+s,a.height=i-n+l}function sNe(t){Ee(t.edges(),function(e){var r=t.edge(e),n=t.node(e.v),i=t.node(e.w),a,s;r.points?(a=r.points[0],s=r.points[r.points.length-1]):(r.points=[],a=i,s=n),r.points.unshift(XL(n,a)),r.points.push(XL(i,s))})}function oNe(t){Ee(t.edges(),function(e){var r=t.edge(e);if(Xe(r,"x"))switch((r.labelpos==="l"||r.labelpos==="r")&&(r.width-=r.labeloffset),r.labelpos){case"l":r.x-=r.width/2+r.labeloffset;break;case"r":r.x+=r.width/2+r.labeloffset;break}})}function lNe(t){Ee(t.edges(),function(e){var r=t.edge(e);r.reversed&&r.points.reverse()})}function cNe(t){Ee(t.nodes(),function(e){if(t.children(e).length){var r=t.node(e),n=t.node(r.borderTop),i=t.node(r.borderBottom),a=t.node(ma(r.borderLeft)),s=t.node(ma(r.borderRight));r.width=Math.abs(s.x-a.x),r.height=Math.abs(i.y-n.y),r.x=a.x+r.width/2,r.y=n.y+r.height/2}}),Ee(t.nodes(),function(e){t.node(e).dummy==="border"&&t.removeNode(e)})}function uNe(t){Ee(t.edges(),function(e){if(e.v===e.w){var r=t.node(e.v);r.selfEdges||(r.selfEdges=[]),r.selfEdges.push({e,label:t.edge(e)}),t.removeEdge(e)}})}function hNe(t){var e=Qh(t);Ee(e,function(r){var n=0;Ee(r,function(i,a){var s=t.node(i);s.order=a+n,Ee(s.selfEdges,function(l){kc(t,"selfedge",{width:l.label.width,height:l.label.height,rank:s.rank,order:a+ ++n,e:l.e,label:l.label},"_se")}),delete s.selfEdges})})}function fNe(t){Ee(t.nodes(),function(e){var r=t.node(e);if(r.dummy==="selfedge"){var n=t.node(r.e.v),i=n.x+n.width/2,a=n.y,s=r.x-i,l=n.height/2;t.setEdge(r.e,r.label),t.removeNode(e),r.label.points=[{x:i+2*s/3,y:a-l},{x:i+5*s/6,y:a-l},{x:i+s,y:a},{x:i+5*s/6,y:a+l},{x:i+2*s/3,y:a+l}],r.label.x=r.x,r.label.y=r.y}})}function pD(t,e){return Pd(Fd(t,e),Number)}function mD(t){var e={};return Ee(t,function(r,n){e[n.toLowerCase()]=r}),e}var WRe,qRe,XRe,jRe,KRe,QRe,ZRe,JRe,Rre=R(()=>{"use strict";Pt();ya();Ate();Nte();qL();JL();fD();nre();kre();Cre();Dre();Ec();o(lo,"layout");o(HRe,"runLayout");o(YRe,"updateInputGraph");WRe=["nodesep","edgesep","ranksep","marginx","marginy"],qRe={ranksep:50,edgesep:20,nodesep:50,rankdir:"tb"},XRe=["acyclicer","ranker","rankdir","align"],jRe=["width","height"],KRe={width:0,height:0},QRe=["minlen","weight","width","height","labeloffset"],ZRe={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},JRe=["labelpos"];o(eNe,"buildLayoutGraph");o(tNe,"makeSpaceForEdgeLabels");o(rNe,"injectEdgeLabelProxies");o(nNe,"assignRankMinMax");o(iNe,"removeEdgeLabelProxies");o(aNe,"translateGraph");o(sNe,"assignNodeIntersects");o(oNe,"fixupEdgeLabelCoords");o(lNe,"reversePointsForReversedEdges");o(cNe,"removeBorderNodes");o(uNe,"removeSelfEdges");o(hNe,"insertSelfEdges");o(fNe,"positionSelfEdges");o(pD,"selectNumberAttrs");o(mD,"canonicalize")});var Vd=R(()=>{"use strict";qL();Rre();JL();fD()});function zn(t){var e={options:{directed:t.isDirected(),multigraph:t.isMultigraph(),compound:t.isCompound()},nodes:dNe(t),edges:pNe(t)};return er(t.graph())||(e.value=Qr(t.graph())),e}function dNe(t){return qe(t.nodes(),function(e){var r=t.node(e),n=t.parent(e),i={v:e};return er(r)||(i.value=r),er(n)||(i.parent=n),i})}function pNe(t){return qe(t.edges(),function(e){var r=t.edge(e),n={v:e.v,w:e.w};return er(e.name)||(n.name=e.name),er(r)||(n.value=r),n})}var Pv=R(()=>{"use strict";Pt();Zw();o(zn,"write");o(dNe,"writeNodes");o(pNe,"writeEdges")});var cr,Ud,Mre,Ire,aT,mNe,Ore,Pre,gNe,Bm,Nre,Bre,Fre,zre,Gre,$re=R(()=>{"use strict";ut();ya();Pv();cr=new Map,Ud=new Map,Mre=new Map,Ire=o(()=>{Ud.clear(),Mre.clear(),cr.clear()},"clear"),aT=o((t,e)=>{let r=Ud.get(e)||[];return V.trace("In isDescendant",e," ",t," = ",r.includes(t)),r.includes(t)},"isDescendant"),mNe=o((t,e)=>{let r=Ud.get(e)||[];return V.info("Descendants of ",e," is ",r),V.info("Edge is ",t),t.v===e||t.w===e?!1:r?r.includes(t.v)||aT(t.v,e)||aT(t.w,e)||r.includes(t.w):(V.debug("Tilt, ",e,",not in descendants"),!1)},"edgeInCluster"),Ore=o((t,e,r,n)=>{V.warn("Copying children of ",t,"root",n,"data",e.node(t),n);let i=e.children(t)||[];t!==n&&i.push(t),V.warn("Copying (nodes) clusterId",t,"nodes",i),i.forEach(a=>{if(e.children(a).length>0)Ore(a,e,r,n);else{let s=e.node(a);V.info("cp ",a," to ",n," with parent ",t),r.setNode(a,s),n!==e.parent(a)&&(V.warn("Setting parent",a,e.parent(a)),r.setParent(a,e.parent(a))),t!==n&&a!==t?(V.debug("Setting parent",a,t),r.setParent(a,t)):(V.info("In copy ",t,"root",n,"data",e.node(t),n),V.debug("Not Setting parent for node=",a,"cluster!==rootId",t!==n,"node!==clusterId",a!==t));let l=e.edges(a);V.debug("Copying Edges",l),l.forEach(u=>{V.info("Edge",u);let h=e.edge(u.v,u.w,u.name);V.info("Edge data",h,n);try{mNe(u,n)?(V.info("Copying as ",u.v,u.w,h,u.name),r.setEdge(u.v,u.w,h,u.name),V.info("newGraph edges ",r.edges(),r.edge(r.edges()[0]))):V.info("Skipping copy of edge ",u.v,"-->",u.w," rootId: ",n," clusterId:",t)}catch(f){V.error(f)}})}V.debug("Removing node",a),e.removeNode(a)})},"copy"),Pre=o((t,e)=>{let r=e.children(t),n=[...r];for(let i of r)Mre.set(i,t),n=[...n,...Pre(i,e)];return n},"extractDescendants"),gNe=o((t,e,r)=>{let n=t.edges().filter(u=>u.v===e||u.w===e),i=t.edges().filter(u=>u.v===r||u.w===r),a=n.map(u=>({v:u.v===e?r:u.v,w:u.w===e?e:u.w})),s=i.map(u=>({v:u.v,w:u.w}));return a.filter(u=>s.some(h=>u.v===h.v&&u.w===h.w))},"findCommonEdges"),Bm=o((t,e,r)=>{let n=e.children(t);if(V.trace("Searching children of id ",t,n),n.length<1)return t;let i;for(let a of n){let s=Bm(a,e,r),l=gNe(e,r,s);if(s)if(l.length>0)i=s;else return s}return i},"findNonClusterChild"),Nre=o(t=>!cr.has(t)||!cr.get(t).externalConnections?t:cr.has(t)?cr.get(t).id:t,"getAnchorId"),Bre=o((t,e)=>{if(!t||e>10){V.debug("Opting out, no graph ");return}else V.debug("Opting in, graph ");t.nodes().forEach(function(r){t.children(r).length>0&&(V.warn("Cluster identified",r," Replacement id in edges: ",Bm(r,t,r)),Ud.set(r,Pre(r,t)),cr.set(r,{id:Bm(r,t,r),clusterData:t.node(r)}))}),t.nodes().forEach(function(r){let n=t.children(r),i=t.edges();n.length>0?(V.debug("Cluster identified",r,Ud),i.forEach(a=>{let s=aT(a.v,r),l=aT(a.w,r);s^l&&(V.warn("Edge: ",a," leaves cluster ",r),V.warn("Descendants of XXX ",r,": ",Ud.get(r)),cr.get(r).externalConnections=!0)})):V.debug("Not a cluster ",r,Ud)});for(let r of cr.keys()){let n=cr.get(r).id,i=t.parent(n);i!==r&&cr.has(i)&&!cr.get(i).externalConnections&&(cr.get(r).id=i)}t.edges().forEach(function(r){let n=t.edge(r);V.warn("Edge "+r.v+" -> "+r.w+": "+JSON.stringify(r)),V.warn("Edge "+r.v+" -> "+r.w+": "+JSON.stringify(t.edge(r)));let i=r.v,a=r.w;if(V.warn("Fix XXX",cr,"ids:",r.v,r.w,"Translating: ",cr.get(r.v)," --- ",cr.get(r.w)),cr.get(r.v)||cr.get(r.w)){if(V.warn("Fixing and trying - removing XXX",r.v,r.w,r.name),i=Nre(r.v),a=Nre(r.w),t.removeEdge(r.v,r.w,r.name),i!==r.v){let s=t.parent(i);cr.get(s).externalConnections=!0,n.fromCluster=r.v}if(a!==r.w){let s=t.parent(a);cr.get(s).externalConnections=!0,n.toCluster=r.w}V.warn("Fix Replacing with XXX",i,a,r.name),t.setEdge(i,a,n,r.name)}}),V.warn("Adjusted Graph",zn(t)),Fre(t,0),V.trace(cr)},"adjustClustersAndEdges"),Fre=o((t,e)=>{if(V.warn("extractor - ",e,zn(t),t.children("D")),e>10){V.error("Bailing out");return}let r=t.nodes(),n=!1;for(let i of r){let a=t.children(i);n=n||a.length>0}if(!n){V.debug("Done, no node has children",t.nodes());return}V.debug("Nodes = ",r,e);for(let i of r)if(V.debug("Extracting node",i,cr,cr.has(i)&&!cr.get(i).externalConnections,!t.parent(i),t.node(i),t.children("D")," Depth ",e),!cr.has(i))V.debug("Not a cluster",i,e);else if(!cr.get(i).externalConnections&&t.children(i)&&t.children(i).length>0){V.warn("Cluster without external connections, without a parent and with children",i,e);let s=t.graph().rankdir==="TB"?"LR":"TB";cr.get(i)?.clusterData?.dir&&(s=cr.get(i).clusterData.dir,V.warn("Fixing dir",cr.get(i).clusterData.dir,s));let l=new lr({multigraph:!0,compound:!0}).setGraph({rankdir:s,nodesep:50,ranksep:50,marginx:8,marginy:8}).setDefaultEdgeLabel(function(){return{}});V.warn("Old graph before copy",zn(t)),Ore(i,t,l,i),t.setNode(i,{clusterNode:!0,id:i,clusterData:cr.get(i).clusterData,label:cr.get(i).label,graph:l}),V.warn("New graph after copy node: (",i,")",zn(l)),V.debug("Old graph after copy",zn(t))}else V.warn("Cluster ** ",i," **not meeting the criteria !externalConnections:",!cr.get(i).externalConnections," no parent: ",!t.parent(i)," children ",t.children(i)&&t.children(i).length>0,t.children("D"),e),V.debug(cr);r=t.nodes(),V.warn("New list of nodes",r);for(let i of r){let a=t.node(i);V.warn(" Now next level",i,a),a?.clusterNode&&Fre(a.graph,e+1)}},"extractor"),zre=o((t,e)=>{if(e.length===0)return[];let r=Object.assign([],e);return e.forEach(n=>{let i=t.children(n),a=zre(t,i);r=[...r,...a]}),r},"sorter"),Gre=o(t=>zre(t,t.children()),"sortNodesByHierarchy")});var Ure={};hr(Ure,{render:()=>yNe});var Vre,yNe,Hre=R(()=>{"use strict";Vd();Pv();ya();Q9();ri();$re();tL();X9();K9();ut();_d();_t();Vre=o(async(t,e,r,n,i,a)=>{V.warn("Graph in recursive render:XAX",zn(e),i);let s=e.graph().rankdir;V.trace("Dir in recursive render - dir:",s);let l=t.insert("g").attr("class","root");e.nodes()?V.info("Recursive render XXX",e.nodes()):V.info("No nodes found for",e),e.edges().length>0&&V.info("Recursive edges",e.edge(e.edges()[0]));let u=l.insert("g").attr("class","clusters"),h=l.insert("g").attr("class","edgePaths"),f=l.insert("g").attr("class","edgeLabels"),d=l.insert("g").attr("class","nodes");await Promise.all(e.nodes().map(async function(y){let v=e.node(y);if(i!==void 0){let x=JSON.parse(JSON.stringify(i.clusterData));V.trace(`Setting data for parent cluster XXX + Node.id = `,y,` + data=`,x.height,` +Parent cluster`,i.height),e.setNode(i.id,x),e.parent(y)||(V.trace("Setting parent",y,i.id),e.setParent(y,i.id,x))}if(V.info("(Insert) Node XXX"+y+": "+JSON.stringify(e.node(y))),v?.clusterNode){V.info("Cluster identified XBX",y,v.width,e.node(y));let{ranksep:x,nodesep:b}=e.graph();v.graph.setGraph({...v.graph.graph(),ranksep:x+25,nodesep:b});let w=await Vre(d,v.graph,r,n,e.node(y),a),S=w.elem;ar(v,S),v.diff=w.diff||0,V.info("New compound node after recursive render XAX",y,"width",v.width,"height",v.height),lQ(S,v)}else e.children(y).length>0?(V.trace("Cluster - the non recursive path XBX",y,v.id,v,v.width,"Graph:",e),V.trace(Bm(v.id,e)),cr.set(v.id,{id:Bm(v.id,e),node:v})):(V.trace("Node - the non recursive path XAX",y,d,e.node(y),s),await rw(d,e.node(y),s))})),await o(async()=>{let y=e.edges().map(async function(v){let x=e.edge(v.v,v.w,v.name);V.info("Edge "+v.v+" -> "+v.w+": "+JSON.stringify(v)),V.info("Edge "+v.v+" -> "+v.w+": ",v," ",JSON.stringify(e.edge(v))),V.info("Fix",cr,"ids:",v.v,v.w,"Translating: ",cr.get(v.v),cr.get(v.w)),await Q5(f,x)});await Promise.all(y)},"processEdges")(),V.info("Graph before layout:",JSON.stringify(zn(e))),V.info("############################################# XXX"),V.info("### Layout ### XXX"),V.info("############################################# XXX"),lo(e),V.info("Graph after layout:",JSON.stringify(zn(e)));let m=0,{subGraphTitleTotalMargin:g}=io(a);return await Promise.all(Gre(e).map(async function(y){let v=e.node(y);if(V.info("Position XBX => "+y+": ("+v.x,","+v.y,") width: ",v.width," height: ",v.height),v?.clusterNode)v.y+=g,V.info("A tainted cluster node XBX1",y,v.id,v.width,v.height,v.x,v.y,e.parent(y)),cr.get(v.id).node=v,eL(v);else if(e.children(y).length>0){V.info("A pure cluster node XBX1",y,v.id,v.x,v.y,v.width,v.height,e.parent(y)),v.height+=g,e.node(v.parentId);let x=v?.padding/2||0,b=v?.labelBBox?.height||0,w=b-x||0;V.debug("OffsetY",w,"labelHeight",b,"halfPadding",x),await Y5(u,v),cr.get(v.id).node=v}else{let x=e.node(v.parentId);v.y+=g/2,V.info("A regular node XBX1 - using the padding",v.id,"parent",v.parentId,v.width,v.height,v.x,v.y,"offsetY",v.offsetY,"parent",x,x?.offsetY,v),eL(v)}})),e.edges().forEach(function(y){let v=e.edge(y);V.info("Edge "+y.v+" -> "+y.w+": "+JSON.stringify(v),v),v.points.forEach(S=>S.y+=g/2);let x=e.node(y.v);var b=e.node(y.w);let w=J5(h,v,cr,r,x,b,n);Z5(v,w)}),e.nodes().forEach(function(y){let v=e.node(y);V.info(y,v.type,v.diff),v.isGroup&&(m=v.diff)}),V.warn("Returning from recursive render XAX",l,m),{elem:l,diff:m}},"recursiveRender"),yNe=o(async(t,e)=>{let r=new lr({multigraph:!0,compound:!0}).setGraph({rankdir:t.direction,nodesep:t.config?.nodeSpacing||t.config?.flowchart?.nodeSpacing||t.nodeSpacing,ranksep:t.config?.rankSpacing||t.config?.flowchart?.rankSpacing||t.rankSpacing,marginx:8,marginy:8}).setDefaultEdgeLabel(function(){return{}}),n=e.select("g");ew(n,t.markers,t.type,t.diagramId),cQ(),lK(),rK(),Ire(),t.nodes.forEach(a=>{r.setNode(a.id,{...a}),a.parentId&&r.setParent(a.id,a.parentId)}),V.debug("Edges:",t.edges),t.edges.forEach(a=>{if(a.start===a.end){let s=a.start,l=s+"---"+s+"---1",u=s+"---"+s+"---2",h=r.node(s);r.setNode(l,{domId:l,id:l,parentId:h.parentId,labelStyle:"",label:"",padding:0,shape:"labelRect",style:"",width:10,height:10}),r.setParent(l,h.parentId),r.setNode(u,{domId:u,id:u,parentId:h.parentId,labelStyle:"",padding:0,shape:"labelRect",label:"",style:"",width:10,height:10}),r.setParent(u,h.parentId);let f=structuredClone(a),d=structuredClone(a),p=structuredClone(a);f.label="",f.arrowTypeEnd="none",f.id=s+"-cyclic-special-1",d.arrowTypeEnd="none",d.id=s+"-cyclic-special-mid",p.label="",h.isGroup&&(f.fromCluster=s,p.toCluster=s),p.id=s+"-cyclic-special-2",r.setEdge(s,l,f,s+"-cyclic-special-0"),r.setEdge(l,u,d,s+"-cyclic-special-1"),r.setEdge(u,s,p,s+"-cyc{"use strict";hQ();ut();Bv={},gD=o(t=>{for(let e of t)Bv[e.name]=e},"registerLayoutLoaders"),vNe=o(()=>{gD([{name:"dagre",loader:o(async()=>await Promise.resolve().then(()=>(Hre(),Ure)),"loader")}])},"registerDefaultLayoutLoaders");vNe();sT=o(async(t,e)=>{if(!(t.layoutAlgorithm in Bv))throw new Error(`Unknown layout algorithm: ${t.layoutAlgorithm}`);let r=Bv[t.layoutAlgorithm];return(await r.loader()).render(t,e,uQ,{algorithm:r.algorithm})},"render"),Yre=o((t="",{fallback:e="dagre"}={})=>{if(t in Bv)return t;if(e in Bv)return V.warn(`Layout algorithm ${t} is not registered. Using ${e} as fallback.`),e;throw new Error(`Both layout algorithms ${t} and ${e} are not registered.`)},"getRegisteredLayoutAlgorithm")});var lT,xNe,bNe,yD=R(()=>{"use strict";Yn();ut();lT=o((t,e,r,n)=>{t.attr("class",r);let{width:i,height:a,x:s,y:l}=xNe(t,e);Sr(t,a,i,n);let u=bNe(s,l,i,a,e);t.attr("viewBox",u),V.debug(`viewBox configured: ${u} with padding: ${e}`)},"setupViewPortForSVG"),xNe=o((t,e)=>{let r=t.node()?.getBBox()||{width:0,height:0,x:0,y:0};return{width:r.width+e*2,height:r.height+e*2,x:r.x,y:r.y}},"calculateDimensionsWithPadding"),bNe=o((t,e,r,n,i)=>`${t-i} ${e-i} ${r} ${n}`,"createViewBox")});var wNe,TNe,Wre,qre=R(()=>{"use strict";Zt();_t();ut();L9();oT();yD();xr();f9();wNe=o(function(t,e){return e.db.getClasses()},"getClasses"),TNe=o(async function(t,e,r,n){V.info("REF0:"),V.info("Drawing state diagram (v2)",e);let{securityLevel:i,flowchart:a,layout:s}=de(),l;i==="sandbox"&&(l=$e("#i"+e));let u=i==="sandbox"?l.nodes()[0].contentDocument:document;V.debug("Before getData: ");let h=n.db.getData();V.debug("Data: ",h);let f=I5(e,i),d=h9();h.type=n.type,h.layoutAlgorithm=Yre(s),h.layoutAlgorithm==="dagre"&&s==="elk"&&V.warn("flowchart-elk was moved to an external package in Mermaid v11. Please refer [release notes](https://github.com/mermaid-js/mermaid/releases/tag/v11.0.0) for more details. This diagram will be rendered using `dagre` layout as a fallback."),h.direction=d,h.nodeSpacing=a?.nodeSpacing||50,h.rankSpacing=a?.rankSpacing||50,h.markers=["point","circle","cross"],h.diagramId=e,V.debug("REF1:",h),await sT(h,f);let p=h.config.flowchart?.diagramPadding??8;Lt.insertTitle(f,"flowchartTitleText",a?.titleTopMargin||0,n.db.getDiagramTitle()),lT(f,p,"flowchart",a?.useMaxWidth||!1);for(let m of h.nodes){let g=$e(`#${e} [id="${m.id}"]`);if(!g||!m.link)continue;let y=u.createElementNS("http://www.w3.org/2000/svg","a");y.setAttributeNS("http://www.w3.org/2000/svg","class",m.cssClasses),y.setAttributeNS("http://www.w3.org/2000/svg","rel","noopener"),i==="sandbox"?y.setAttributeNS("http://www.w3.org/2000/svg","target","_top"):m.linkTarget&&y.setAttributeNS("http://www.w3.org/2000/svg","target",m.linkTarget);let v=g.insert(function(){return y},":first-child"),x=g.select(".label-container");x&&v.append(function(){return x.node()});let b=g.select(".label");b&&v.append(function(){return b.node()})}},"draw"),Wre={getClasses:wNe,draw:TNe}});var vD,Xre,jre=R(()=>{"use strict";vD=function(){var t=o(function(qi,ht,At,$t){for(At=At||{},$t=qi.length;$t--;At[qi[$t]]=ht);return At},"o"),e=[1,4],r=[1,3],n=[1,5],i=[1,8,9,10,11,27,34,36,38,42,58,81,82,83,84,85,86,99,102,103,106,108,111,112,113,118,119,120,121],a=[2,2],s=[1,13],l=[1,14],u=[1,15],h=[1,16],f=[1,23],d=[1,25],p=[1,26],m=[1,27],g=[1,49],y=[1,48],v=[1,29],x=[1,30],b=[1,31],w=[1,32],S=[1,33],T=[1,44],E=[1,46],_=[1,42],A=[1,47],L=[1,43],M=[1,50],N=[1,45],k=[1,51],I=[1,52],C=[1,34],O=[1,35],D=[1,36],P=[1,37],F=[1,57],B=[1,8,9,10,11,27,32,34,36,38,42,58,81,82,83,84,85,86,99,102,103,106,108,111,112,113,118,119,120,121],$=[1,61],z=[1,60],Y=[1,62],Q=[8,9,11,73,75],X=[1,88],ie=[1,93],j=[1,92],J=[1,89],Z=[1,85],H=[1,91],q=[1,87],K=[1,94],se=[1,90],ce=[1,95],ue=[1,86],te=[8,9,10,11,73,75],De=[8,9,10,11,44,73,75],oe=[8,9,10,11,29,42,44,46,48,50,52,54,56,58,61,63,65,66,68,73,75,86,99,102,103,106,108,111,112,113],ke=[8,9,11,42,58,73,75,86,99,102,103,106,108,111,112,113],Ie=[42,58,86,99,102,103,106,108,111,112,113],Se=[1,121],Ue=[1,120],Pe=[1,128],_e=[1,142],me=[1,143],W=[1,144],fe=[1,145],ge=[1,130],re=[1,132],he=[1,136],ne=[1,137],ae=[1,138],we=[1,139],Te=[1,140],Ce=[1,141],Ae=[1,146],Ge=[1,147],Me=[1,126],ye=[1,127],He=[1,134],ze=[1,129],Ze=[1,133],gt=[1,131],yt=[8,9,10,11,27,32,34,36,38,42,58,81,82,83,84,85,86,99,102,103,106,108,111,112,113,118,119,120,121],tt=[1,149],Ye=[8,9,11],Je=[8,9,10,11,14,42,58,86,102,103,106,108,111,112,113],Ve=[1,169],je=[1,165],kt=[1,166],at=[1,170],xt=[1,167],it=[1,168],dt=[75,113,116],lt=[8,9,10,11,12,14,27,29,32,42,58,73,81,82,83,84,85,86,87,102,106,108,111,112,113],It=[10,103],mt=[31,47,49,51,53,55,60,62,64,65,67,69,113,114,115],St=[1,235],gr=[1,233],xn=[1,237],jt=[1,231],rn=[1,232],Er=[1,234],Kn=[1,236],hn=[1,238],Qn=[1,255],on=[8,9,11,103],Rn=[8,9,10,11,58,81,102,103,106,107,108,109],Ha={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,start:3,graphConfig:4,document:5,line:6,statement:7,SEMI:8,NEWLINE:9,SPACE:10,EOF:11,GRAPH:12,NODIR:13,DIR:14,FirstStmtSeparator:15,ending:16,endToken:17,spaceList:18,spaceListNewline:19,vertexStatement:20,separator:21,styleStatement:22,linkStyleStatement:23,classDefStatement:24,classStatement:25,clickStatement:26,subgraph:27,textNoTags:28,SQS:29,text:30,SQE:31,end:32,direction:33,acc_title:34,acc_title_value:35,acc_descr:36,acc_descr_value:37,acc_descr_multiline_value:38,link:39,node:40,styledVertex:41,AMP:42,vertex:43,STYLE_SEPARATOR:44,idString:45,DOUBLECIRCLESTART:46,DOUBLECIRCLEEND:47,PS:48,PE:49,"(-":50,"-)":51,STADIUMSTART:52,STADIUMEND:53,SUBROUTINESTART:54,SUBROUTINEEND:55,VERTEX_WITH_PROPS_START:56,"NODE_STRING[field]":57,COLON:58,"NODE_STRING[value]":59,PIPE:60,CYLINDERSTART:61,CYLINDEREND:62,DIAMOND_START:63,DIAMOND_STOP:64,TAGEND:65,TRAPSTART:66,TRAPEND:67,INVTRAPSTART:68,INVTRAPEND:69,linkStatement:70,arrowText:71,TESTSTR:72,START_LINK:73,edgeText:74,LINK:75,edgeTextToken:76,STR:77,MD_STR:78,textToken:79,keywords:80,STYLE:81,LINKSTYLE:82,CLASSDEF:83,CLASS:84,CLICK:85,DOWN:86,UP:87,textNoTagsToken:88,stylesOpt:89,"idString[vertex]":90,"idString[class]":91,CALLBACKNAME:92,CALLBACKARGS:93,HREF:94,LINK_TARGET:95,"STR[link]":96,"STR[tooltip]":97,alphaNum:98,DEFAULT:99,numList:100,INTERPOLATE:101,NUM:102,COMMA:103,style:104,styleComponent:105,NODE_STRING:106,UNIT:107,BRKT:108,PCT:109,idStringToken:110,MINUS:111,MULT:112,UNICODE_TEXT:113,TEXT:114,TAGSTART:115,EDGE_TEXT:116,alphaNumToken:117,direction_tb:118,direction_bt:119,direction_rl:120,direction_lr:121,$accept:0,$end:1},terminals_:{2:"error",8:"SEMI",9:"NEWLINE",10:"SPACE",11:"EOF",12:"GRAPH",13:"NODIR",14:"DIR",27:"subgraph",29:"SQS",31:"SQE",32:"end",34:"acc_title",35:"acc_title_value",36:"acc_descr",37:"acc_descr_value",38:"acc_descr_multiline_value",42:"AMP",44:"STYLE_SEPARATOR",46:"DOUBLECIRCLESTART",47:"DOUBLECIRCLEEND",48:"PS",49:"PE",50:"(-",51:"-)",52:"STADIUMSTART",53:"STADIUMEND",54:"SUBROUTINESTART",55:"SUBROUTINEEND",56:"VERTEX_WITH_PROPS_START",57:"NODE_STRING[field]",58:"COLON",59:"NODE_STRING[value]",60:"PIPE",61:"CYLINDERSTART",62:"CYLINDEREND",63:"DIAMOND_START",64:"DIAMOND_STOP",65:"TAGEND",66:"TRAPSTART",67:"TRAPEND",68:"INVTRAPSTART",69:"INVTRAPEND",72:"TESTSTR",73:"START_LINK",75:"LINK",77:"STR",78:"MD_STR",81:"STYLE",82:"LINKSTYLE",83:"CLASSDEF",84:"CLASS",85:"CLICK",86:"DOWN",87:"UP",90:"idString[vertex]",91:"idString[class]",92:"CALLBACKNAME",93:"CALLBACKARGS",94:"HREF",95:"LINK_TARGET",96:"STR[link]",97:"STR[tooltip]",99:"DEFAULT",101:"INTERPOLATE",102:"NUM",103:"COMMA",106:"NODE_STRING",107:"UNIT",108:"BRKT",109:"PCT",111:"MINUS",112:"MULT",113:"UNICODE_TEXT",114:"TEXT",115:"TAGSTART",116:"EDGE_TEXT",118:"direction_tb",119:"direction_bt",120:"direction_rl",121:"direction_lr"},productions_:[0,[3,2],[5,0],[5,2],[6,1],[6,1],[6,1],[6,1],[6,1],[4,2],[4,2],[4,2],[4,3],[16,2],[16,1],[17,1],[17,1],[17,1],[15,1],[15,1],[15,2],[19,2],[19,2],[19,1],[19,1],[18,2],[18,1],[7,2],[7,2],[7,2],[7,2],[7,2],[7,2],[7,9],[7,6],[7,4],[7,1],[7,2],[7,2],[7,1],[21,1],[21,1],[21,1],[20,3],[20,4],[20,2],[20,1],[40,1],[40,5],[41,1],[41,3],[43,4],[43,4],[43,6],[43,4],[43,4],[43,4],[43,8],[43,4],[43,4],[43,4],[43,6],[43,4],[43,4],[43,4],[43,4],[43,4],[43,1],[39,2],[39,3],[39,3],[39,1],[39,3],[74,1],[74,2],[74,1],[74,1],[70,1],[71,3],[30,1],[30,2],[30,1],[30,1],[80,1],[80,1],[80,1],[80,1],[80,1],[80,1],[80,1],[80,1],[80,1],[80,1],[80,1],[28,1],[28,2],[28,1],[28,1],[24,5],[25,5],[26,2],[26,4],[26,3],[26,5],[26,3],[26,5],[26,5],[26,7],[26,2],[26,4],[26,2],[26,4],[26,4],[26,6],[22,5],[23,5],[23,5],[23,9],[23,9],[23,7],[23,7],[100,1],[100,3],[89,1],[89,3],[104,1],[104,2],[105,1],[105,1],[105,1],[105,1],[105,1],[105,1],[105,1],[105,1],[110,1],[110,1],[110,1],[110,1],[110,1],[110,1],[110,1],[110,1],[110,1],[110,1],[110,1],[79,1],[79,1],[79,1],[79,1],[88,1],[88,1],[88,1],[88,1],[88,1],[88,1],[88,1],[88,1],[88,1],[88,1],[88,1],[76,1],[76,1],[117,1],[117,1],[117,1],[117,1],[117,1],[117,1],[117,1],[117,1],[117,1],[117,1],[117,1],[45,1],[45,2],[98,1],[98,2],[33,1],[33,1],[33,1],[33,1]],performAction:o(function(ht,At,$t,rt,Ot,pe,ur){var be=pe.length-1;switch(Ot){case 2:this.$=[];break;case 3:(!Array.isArray(pe[be])||pe[be].length>0)&&pe[be-1].push(pe[be]),this.$=pe[be-1];break;case 4:case 176:this.$=pe[be];break;case 11:rt.setDirection("TB"),this.$="TB";break;case 12:rt.setDirection(pe[be-1]),this.$=pe[be-1];break;case 27:this.$=pe[be-1].nodes;break;case 28:case 29:case 30:case 31:case 32:this.$=[];break;case 33:this.$=rt.addSubGraph(pe[be-6],pe[be-1],pe[be-4]);break;case 34:this.$=rt.addSubGraph(pe[be-3],pe[be-1],pe[be-3]);break;case 35:this.$=rt.addSubGraph(void 0,pe[be-1],void 0);break;case 37:this.$=pe[be].trim(),rt.setAccTitle(this.$);break;case 38:case 39:this.$=pe[be].trim(),rt.setAccDescription(this.$);break;case 43:rt.addLink(pe[be-2].stmt,pe[be],pe[be-1]),this.$={stmt:pe[be],nodes:pe[be].concat(pe[be-2].nodes)};break;case 44:rt.addLink(pe[be-3].stmt,pe[be-1],pe[be-2]),this.$={stmt:pe[be-1],nodes:pe[be-1].concat(pe[be-3].nodes)};break;case 45:this.$={stmt:pe[be-1],nodes:pe[be-1]};break;case 46:this.$={stmt:pe[be],nodes:pe[be]};break;case 47:this.$=[pe[be]];break;case 48:this.$=pe[be-4].concat(pe[be]);break;case 49:this.$=pe[be];break;case 50:this.$=pe[be-2],rt.setClass(pe[be-2],pe[be]);break;case 51:this.$=pe[be-3],rt.addVertex(pe[be-3],pe[be-1],"square");break;case 52:this.$=pe[be-3],rt.addVertex(pe[be-3],pe[be-1],"doublecircle");break;case 53:this.$=pe[be-5],rt.addVertex(pe[be-5],pe[be-2],"circle");break;case 54:this.$=pe[be-3],rt.addVertex(pe[be-3],pe[be-1],"ellipse");break;case 55:this.$=pe[be-3],rt.addVertex(pe[be-3],pe[be-1],"stadium");break;case 56:this.$=pe[be-3],rt.addVertex(pe[be-3],pe[be-1],"subroutine");break;case 57:this.$=pe[be-7],rt.addVertex(pe[be-7],pe[be-1],"rect",void 0,void 0,void 0,Object.fromEntries([[pe[be-5],pe[be-3]]]));break;case 58:this.$=pe[be-3],rt.addVertex(pe[be-3],pe[be-1],"cylinder");break;case 59:this.$=pe[be-3],rt.addVertex(pe[be-3],pe[be-1],"round");break;case 60:this.$=pe[be-3],rt.addVertex(pe[be-3],pe[be-1],"diamond");break;case 61:this.$=pe[be-5],rt.addVertex(pe[be-5],pe[be-2],"hexagon");break;case 62:this.$=pe[be-3],rt.addVertex(pe[be-3],pe[be-1],"odd");break;case 63:this.$=pe[be-3],rt.addVertex(pe[be-3],pe[be-1],"trapezoid");break;case 64:this.$=pe[be-3],rt.addVertex(pe[be-3],pe[be-1],"inv_trapezoid");break;case 65:this.$=pe[be-3],rt.addVertex(pe[be-3],pe[be-1],"lean_right");break;case 66:this.$=pe[be-3],rt.addVertex(pe[be-3],pe[be-1],"lean_left");break;case 67:this.$=pe[be],rt.addVertex(pe[be]);break;case 68:pe[be-1].text=pe[be],this.$=pe[be-1];break;case 69:case 70:pe[be-2].text=pe[be-1],this.$=pe[be-2];break;case 71:this.$=pe[be];break;case 72:var Ir=rt.destructLink(pe[be],pe[be-2]);this.$={type:Ir.type,stroke:Ir.stroke,length:Ir.length,text:pe[be-1]};break;case 73:this.$={text:pe[be],type:"text"};break;case 74:this.$={text:pe[be-1].text+""+pe[be],type:pe[be-1].type};break;case 75:this.$={text:pe[be],type:"string"};break;case 76:this.$={text:pe[be],type:"markdown"};break;case 77:var Ir=rt.destructLink(pe[be]);this.$={type:Ir.type,stroke:Ir.stroke,length:Ir.length};break;case 78:this.$=pe[be-1];break;case 79:this.$={text:pe[be],type:"text"};break;case 80:this.$={text:pe[be-1].text+""+pe[be],type:pe[be-1].type};break;case 81:this.$={text:pe[be],type:"string"};break;case 82:case 97:this.$={text:pe[be],type:"markdown"};break;case 94:this.$={text:pe[be],type:"text"};break;case 95:this.$={text:pe[be-1].text+""+pe[be],type:pe[be-1].type};break;case 96:this.$={text:pe[be],type:"text"};break;case 98:this.$=pe[be-4],rt.addClass(pe[be-2],pe[be]);break;case 99:this.$=pe[be-4],rt.setClass(pe[be-2],pe[be]);break;case 100:case 108:this.$=pe[be-1],rt.setClickEvent(pe[be-1],pe[be]);break;case 101:case 109:this.$=pe[be-3],rt.setClickEvent(pe[be-3],pe[be-2]),rt.setTooltip(pe[be-3],pe[be]);break;case 102:this.$=pe[be-2],rt.setClickEvent(pe[be-2],pe[be-1],pe[be]);break;case 103:this.$=pe[be-4],rt.setClickEvent(pe[be-4],pe[be-3],pe[be-2]),rt.setTooltip(pe[be-4],pe[be]);break;case 104:this.$=pe[be-2],rt.setLink(pe[be-2],pe[be]);break;case 105:this.$=pe[be-4],rt.setLink(pe[be-4],pe[be-2]),rt.setTooltip(pe[be-4],pe[be]);break;case 106:this.$=pe[be-4],rt.setLink(pe[be-4],pe[be-2],pe[be]);break;case 107:this.$=pe[be-6],rt.setLink(pe[be-6],pe[be-4],pe[be]),rt.setTooltip(pe[be-6],pe[be-2]);break;case 110:this.$=pe[be-1],rt.setLink(pe[be-1],pe[be]);break;case 111:this.$=pe[be-3],rt.setLink(pe[be-3],pe[be-2]),rt.setTooltip(pe[be-3],pe[be]);break;case 112:this.$=pe[be-3],rt.setLink(pe[be-3],pe[be-2],pe[be]);break;case 113:this.$=pe[be-5],rt.setLink(pe[be-5],pe[be-4],pe[be]),rt.setTooltip(pe[be-5],pe[be-2]);break;case 114:this.$=pe[be-4],rt.addVertex(pe[be-2],void 0,void 0,pe[be]);break;case 115:this.$=pe[be-4],rt.updateLink([pe[be-2]],pe[be]);break;case 116:this.$=pe[be-4],rt.updateLink(pe[be-2],pe[be]);break;case 117:this.$=pe[be-8],rt.updateLinkInterpolate([pe[be-6]],pe[be-2]),rt.updateLink([pe[be-6]],pe[be]);break;case 118:this.$=pe[be-8],rt.updateLinkInterpolate(pe[be-6],pe[be-2]),rt.updateLink(pe[be-6],pe[be]);break;case 119:this.$=pe[be-6],rt.updateLinkInterpolate([pe[be-4]],pe[be]);break;case 120:this.$=pe[be-6],rt.updateLinkInterpolate(pe[be-4],pe[be]);break;case 121:case 123:this.$=[pe[be]];break;case 122:case 124:pe[be-2].push(pe[be]),this.$=pe[be-2];break;case 126:this.$=pe[be-1]+pe[be];break;case 174:this.$=pe[be];break;case 175:this.$=pe[be-1]+""+pe[be];break;case 177:this.$=pe[be-1]+""+pe[be];break;case 178:this.$={stmt:"dir",value:"TB"};break;case 179:this.$={stmt:"dir",value:"BT"};break;case 180:this.$={stmt:"dir",value:"RL"};break;case 181:this.$={stmt:"dir",value:"LR"};break}},"anonymous"),table:[{3:1,4:2,9:e,10:r,12:n},{1:[3]},t(i,a,{5:6}),{4:7,9:e,10:r,12:n},{4:8,9:e,10:r,12:n},{13:[1,9],14:[1,10]},{1:[2,1],6:11,7:12,8:s,9:l,10:u,11:h,20:17,22:18,23:19,24:20,25:21,26:22,27:f,33:24,34:d,36:p,38:m,40:28,41:38,42:g,43:39,45:40,58:y,81:v,82:x,83:b,84:w,85:S,86:T,99:E,102:_,103:A,106:L,108:M,110:41,111:N,112:k,113:I,118:C,119:O,120:D,121:P},t(i,[2,9]),t(i,[2,10]),t(i,[2,11]),{8:[1,54],9:[1,55],10:F,15:53,18:56},t(B,[2,3]),t(B,[2,4]),t(B,[2,5]),t(B,[2,6]),t(B,[2,7]),t(B,[2,8]),{8:$,9:z,11:Y,21:58,39:59,70:63,73:[1,64],75:[1,65]},{8:$,9:z,11:Y,21:66},{8:$,9:z,11:Y,21:67},{8:$,9:z,11:Y,21:68},{8:$,9:z,11:Y,21:69},{8:$,9:z,11:Y,21:70},{8:$,9:z,10:[1,71],11:Y,21:72},t(B,[2,36]),{35:[1,73]},{37:[1,74]},t(B,[2,39]),t(Q,[2,46],{18:75,10:F}),{10:[1,76]},{10:[1,77]},{10:[1,78]},{10:[1,79]},{14:X,42:ie,58:j,77:[1,83],86:J,92:[1,80],94:[1,81],98:82,102:Z,103:H,106:q,108:K,111:se,112:ce,113:ue,117:84},t(B,[2,178]),t(B,[2,179]),t(B,[2,180]),t(B,[2,181]),t(te,[2,47]),t(te,[2,49],{44:[1,96]}),t(De,[2,67],{110:109,29:[1,97],42:g,46:[1,98],48:[1,99],50:[1,100],52:[1,101],54:[1,102],56:[1,103],58:y,61:[1,104],63:[1,105],65:[1,106],66:[1,107],68:[1,108],86:T,99:E,102:_,103:A,106:L,108:M,111:N,112:k,113:I}),t(oe,[2,174]),t(oe,[2,135]),t(oe,[2,136]),t(oe,[2,137]),t(oe,[2,138]),t(oe,[2,139]),t(oe,[2,140]),t(oe,[2,141]),t(oe,[2,142]),t(oe,[2,143]),t(oe,[2,144]),t(oe,[2,145]),t(i,[2,12]),t(i,[2,18]),t(i,[2,19]),{9:[1,110]},t(ke,[2,26],{18:111,10:F}),t(B,[2,27]),{40:112,41:38,42:g,43:39,45:40,58:y,86:T,99:E,102:_,103:A,106:L,108:M,110:41,111:N,112:k,113:I},t(B,[2,40]),t(B,[2,41]),t(B,[2,42]),t(Ie,[2,71],{71:113,60:[1,115],72:[1,114]}),{74:116,76:117,77:[1,118],78:[1,119],113:Se,116:Ue},t([42,58,60,72,86,99,102,103,106,108,111,112,113],[2,77]),t(B,[2,28]),t(B,[2,29]),t(B,[2,30]),t(B,[2,31]),t(B,[2,32]),{10:Pe,12:_e,14:me,27:W,28:122,32:fe,42:ge,58:re,73:he,77:[1,124],78:[1,125],80:135,81:ne,82:ae,83:we,84:Te,85:Ce,86:Ae,87:Ge,88:123,102:Me,106:ye,108:He,111:ze,112:Ze,113:gt},t(yt,a,{5:148}),t(B,[2,37]),t(B,[2,38]),t(Q,[2,45],{42:tt}),{42:g,45:150,58:y,86:T,99:E,102:_,103:A,106:L,108:M,110:41,111:N,112:k,113:I},{99:[1,151],100:152,102:[1,153]},{42:g,45:154,58:y,86:T,99:E,102:_,103:A,106:L,108:M,110:41,111:N,112:k,113:I},{42:g,45:155,58:y,86:T,99:E,102:_,103:A,106:L,108:M,110:41,111:N,112:k,113:I},t(Ye,[2,100],{10:[1,156],93:[1,157]}),{77:[1,158]},t(Ye,[2,108],{117:160,10:[1,159],14:X,42:ie,58:j,86:J,102:Z,103:H,106:q,108:K,111:se,112:ce,113:ue}),t(Ye,[2,110],{10:[1,161]}),t(Je,[2,176]),t(Je,[2,163]),t(Je,[2,164]),t(Je,[2,165]),t(Je,[2,166]),t(Je,[2,167]),t(Je,[2,168]),t(Je,[2,169]),t(Je,[2,170]),t(Je,[2,171]),t(Je,[2,172]),t(Je,[2,173]),{42:g,45:162,58:y,86:T,99:E,102:_,103:A,106:L,108:M,110:41,111:N,112:k,113:I},{30:163,65:Ve,77:je,78:kt,79:164,113:at,114:xt,115:it},{30:171,65:Ve,77:je,78:kt,79:164,113:at,114:xt,115:it},{30:173,48:[1,172],65:Ve,77:je,78:kt,79:164,113:at,114:xt,115:it},{30:174,65:Ve,77:je,78:kt,79:164,113:at,114:xt,115:it},{30:175,65:Ve,77:je,78:kt,79:164,113:at,114:xt,115:it},{30:176,65:Ve,77:je,78:kt,79:164,113:at,114:xt,115:it},{106:[1,177]},{30:178,65:Ve,77:je,78:kt,79:164,113:at,114:xt,115:it},{30:179,63:[1,180],65:Ve,77:je,78:kt,79:164,113:at,114:xt,115:it},{30:181,65:Ve,77:je,78:kt,79:164,113:at,114:xt,115:it},{30:182,65:Ve,77:je,78:kt,79:164,113:at,114:xt,115:it},{30:183,65:Ve,77:je,78:kt,79:164,113:at,114:xt,115:it},t(oe,[2,175]),t(i,[2,20]),t(ke,[2,25]),t(Q,[2,43],{18:184,10:F}),t(Ie,[2,68],{10:[1,185]}),{10:[1,186]},{30:187,65:Ve,77:je,78:kt,79:164,113:at,114:xt,115:it},{75:[1,188],76:189,113:Se,116:Ue},t(dt,[2,73]),t(dt,[2,75]),t(dt,[2,76]),t(dt,[2,161]),t(dt,[2,162]),{8:$,9:z,10:Pe,11:Y,12:_e,14:me,21:191,27:W,29:[1,190],32:fe,42:ge,58:re,73:he,80:135,81:ne,82:ae,83:we,84:Te,85:Ce,86:Ae,87:Ge,88:192,102:Me,106:ye,108:He,111:ze,112:Ze,113:gt},t(lt,[2,94]),t(lt,[2,96]),t(lt,[2,97]),t(lt,[2,150]),t(lt,[2,151]),t(lt,[2,152]),t(lt,[2,153]),t(lt,[2,154]),t(lt,[2,155]),t(lt,[2,156]),t(lt,[2,157]),t(lt,[2,158]),t(lt,[2,159]),t(lt,[2,160]),t(lt,[2,83]),t(lt,[2,84]),t(lt,[2,85]),t(lt,[2,86]),t(lt,[2,87]),t(lt,[2,88]),t(lt,[2,89]),t(lt,[2,90]),t(lt,[2,91]),t(lt,[2,92]),t(lt,[2,93]),{6:11,7:12,8:s,9:l,10:u,11:h,20:17,22:18,23:19,24:20,25:21,26:22,27:f,32:[1,193],33:24,34:d,36:p,38:m,40:28,41:38,42:g,43:39,45:40,58:y,81:v,82:x,83:b,84:w,85:S,86:T,99:E,102:_,103:A,106:L,108:M,110:41,111:N,112:k,113:I,118:C,119:O,120:D,121:P},{10:F,18:194},{10:[1,195],42:g,58:y,86:T,99:E,102:_,103:A,106:L,108:M,110:109,111:N,112:k,113:I},{10:[1,196]},{10:[1,197],103:[1,198]},t(It,[2,121]),{10:[1,199],42:g,58:y,86:T,99:E,102:_,103:A,106:L,108:M,110:109,111:N,112:k,113:I},{10:[1,200],42:g,58:y,86:T,99:E,102:_,103:A,106:L,108:M,110:109,111:N,112:k,113:I},{77:[1,201]},t(Ye,[2,102],{10:[1,202]}),t(Ye,[2,104],{10:[1,203]}),{77:[1,204]},t(Je,[2,177]),{77:[1,205],95:[1,206]},t(te,[2,50],{110:109,42:g,58:y,86:T,99:E,102:_,103:A,106:L,108:M,111:N,112:k,113:I}),{31:[1,207],65:Ve,79:208,113:at,114:xt,115:it},t(mt,[2,79]),t(mt,[2,81]),t(mt,[2,82]),t(mt,[2,146]),t(mt,[2,147]),t(mt,[2,148]),t(mt,[2,149]),{47:[1,209],65:Ve,79:208,113:at,114:xt,115:it},{30:210,65:Ve,77:je,78:kt,79:164,113:at,114:xt,115:it},{49:[1,211],65:Ve,79:208,113:at,114:xt,115:it},{51:[1,212],65:Ve,79:208,113:at,114:xt,115:it},{53:[1,213],65:Ve,79:208,113:at,114:xt,115:it},{55:[1,214],65:Ve,79:208,113:at,114:xt,115:it},{58:[1,215]},{62:[1,216],65:Ve,79:208,113:at,114:xt,115:it},{64:[1,217],65:Ve,79:208,113:at,114:xt,115:it},{30:218,65:Ve,77:je,78:kt,79:164,113:at,114:xt,115:it},{31:[1,219],65:Ve,79:208,113:at,114:xt,115:it},{65:Ve,67:[1,220],69:[1,221],79:208,113:at,114:xt,115:it},{65:Ve,67:[1,223],69:[1,222],79:208,113:at,114:xt,115:it},t(Q,[2,44],{42:tt}),t(Ie,[2,70]),t(Ie,[2,69]),{60:[1,224],65:Ve,79:208,113:at,114:xt,115:it},t(Ie,[2,72]),t(dt,[2,74]),{30:225,65:Ve,77:je,78:kt,79:164,113:at,114:xt,115:it},t(yt,a,{5:226}),t(lt,[2,95]),t(B,[2,35]),{41:227,42:g,43:39,45:40,58:y,86:T,99:E,102:_,103:A,106:L,108:M,110:41,111:N,112:k,113:I},{10:St,58:gr,81:xn,89:228,102:jt,104:229,105:230,106:rn,107:Er,108:Kn,109:hn},{10:St,58:gr,81:xn,89:239,101:[1,240],102:jt,104:229,105:230,106:rn,107:Er,108:Kn,109:hn},{10:St,58:gr,81:xn,89:241,101:[1,242],102:jt,104:229,105:230,106:rn,107:Er,108:Kn,109:hn},{102:[1,243]},{10:St,58:gr,81:xn,89:244,102:jt,104:229,105:230,106:rn,107:Er,108:Kn,109:hn},{42:g,45:245,58:y,86:T,99:E,102:_,103:A,106:L,108:M,110:41,111:N,112:k,113:I},t(Ye,[2,101]),{77:[1,246]},{77:[1,247],95:[1,248]},t(Ye,[2,109]),t(Ye,[2,111],{10:[1,249]}),t(Ye,[2,112]),t(De,[2,51]),t(mt,[2,80]),t(De,[2,52]),{49:[1,250],65:Ve,79:208,113:at,114:xt,115:it},t(De,[2,59]),t(De,[2,54]),t(De,[2,55]),t(De,[2,56]),{106:[1,251]},t(De,[2,58]),t(De,[2,60]),{64:[1,252],65:Ve,79:208,113:at,114:xt,115:it},t(De,[2,62]),t(De,[2,63]),t(De,[2,65]),t(De,[2,64]),t(De,[2,66]),t([10,42,58,86,99,102,103,106,108,111,112,113],[2,78]),{31:[1,253],65:Ve,79:208,113:at,114:xt,115:it},{6:11,7:12,8:s,9:l,10:u,11:h,20:17,22:18,23:19,24:20,25:21,26:22,27:f,32:[1,254],33:24,34:d,36:p,38:m,40:28,41:38,42:g,43:39,45:40,58:y,81:v,82:x,83:b,84:w,85:S,86:T,99:E,102:_,103:A,106:L,108:M,110:41,111:N,112:k,113:I,118:C,119:O,120:D,121:P},t(te,[2,48]),t(Ye,[2,114],{103:Qn}),t(on,[2,123],{105:256,10:St,58:gr,81:xn,102:jt,106:rn,107:Er,108:Kn,109:hn}),t(Rn,[2,125]),t(Rn,[2,127]),t(Rn,[2,128]),t(Rn,[2,129]),t(Rn,[2,130]),t(Rn,[2,131]),t(Rn,[2,132]),t(Rn,[2,133]),t(Rn,[2,134]),t(Ye,[2,115],{103:Qn}),{10:[1,257]},t(Ye,[2,116],{103:Qn}),{10:[1,258]},t(It,[2,122]),t(Ye,[2,98],{103:Qn}),t(Ye,[2,99],{110:109,42:g,58:y,86:T,99:E,102:_,103:A,106:L,108:M,111:N,112:k,113:I}),t(Ye,[2,103]),t(Ye,[2,105],{10:[1,259]}),t(Ye,[2,106]),{95:[1,260]},{49:[1,261]},{60:[1,262]},{64:[1,263]},{8:$,9:z,11:Y,21:264},t(B,[2,34]),{10:St,58:gr,81:xn,102:jt,104:265,105:230,106:rn,107:Er,108:Kn,109:hn},t(Rn,[2,126]),{14:X,42:ie,58:j,86:J,98:266,102:Z,103:H,106:q,108:K,111:se,112:ce,113:ue,117:84},{14:X,42:ie,58:j,86:J,98:267,102:Z,103:H,106:q,108:K,111:se,112:ce,113:ue,117:84},{95:[1,268]},t(Ye,[2,113]),t(De,[2,53]),{30:269,65:Ve,77:je,78:kt,79:164,113:at,114:xt,115:it},t(De,[2,61]),t(yt,a,{5:270}),t(on,[2,124],{105:256,10:St,58:gr,81:xn,102:jt,106:rn,107:Er,108:Kn,109:hn}),t(Ye,[2,119],{117:160,10:[1,271],14:X,42:ie,58:j,86:J,102:Z,103:H,106:q,108:K,111:se,112:ce,113:ue}),t(Ye,[2,120],{117:160,10:[1,272],14:X,42:ie,58:j,86:J,102:Z,103:H,106:q,108:K,111:se,112:ce,113:ue}),t(Ye,[2,107]),{31:[1,273],65:Ve,79:208,113:at,114:xt,115:it},{6:11,7:12,8:s,9:l,10:u,11:h,20:17,22:18,23:19,24:20,25:21,26:22,27:f,32:[1,274],33:24,34:d,36:p,38:m,40:28,41:38,42:g,43:39,45:40,58:y,81:v,82:x,83:b,84:w,85:S,86:T,99:E,102:_,103:A,106:L,108:M,110:41,111:N,112:k,113:I,118:C,119:O,120:D,121:P},{10:St,58:gr,81:xn,89:275,102:jt,104:229,105:230,106:rn,107:Er,108:Kn,109:hn},{10:St,58:gr,81:xn,89:276,102:jt,104:229,105:230,106:rn,107:Er,108:Kn,109:hn},t(De,[2,57]),t(B,[2,33]),t(Ye,[2,117],{103:Qn}),t(Ye,[2,118],{103:Qn})],defaultActions:{},parseError:o(function(ht,At){if(At.recoverable)this.trace(ht);else{var $t=new Error(ht);throw $t.hash=At,$t}},"parseError"),parse:o(function(ht){var At=this,$t=[0],rt=[],Ot=[null],pe=[],ur=this.table,be="",Ir=0,Xc=0,M1=0,_b=2,I1=1,O1=pe.slice.call(arguments,1),ci=Object.create(this.lexer),ko={yy:{}};for(var ih in this.yy)Object.prototype.hasOwnProperty.call(this.yy,ih)&&(ko.yy[ih]=this.yy[ih]);ci.setInput(ht,ko.yy),ko.yy.lexer=ci,ko.yy.parser=this,typeof ci.yylloc>"u"&&(ci.yylloc={});var Us=ci.yylloc;pe.push(Us);var ah=ci.options&&ci.options.ranges;typeof ko.yy.parseError=="function"?this.parseError=ko.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function Lb(La){$t.length=$t.length-2*La,Ot.length=Ot.length-La,pe.length=pe.length-La}o(Lb,"popStack");function P1(){var La;return La=rt.pop()||ci.lex()||I1,typeof La!="number"&&(La instanceof Array&&(rt=La,La=rt.pop()),La=At.symbols_[La]||La),La}o(P1,"lex");for(var sa,jc,Kc,us,_i,Wl,sh={},zf,Hs,B1,Gf;;){if(Kc=$t[$t.length-1],this.defaultActions[Kc]?us=this.defaultActions[Kc]:((sa===null||typeof sa>"u")&&(sa=P1()),us=ur[Kc]&&ur[Kc][sa]),typeof us>"u"||!us.length||!us[0]){var F1="";Gf=[];for(zf in ur[Kc])this.terminals_[zf]&&zf>_b&&Gf.push("'"+this.terminals_[zf]+"'");ci.showPosition?F1="Parse error on line "+(Ir+1)+`: +`+ci.showPosition()+` +Expecting `+Gf.join(", ")+", got '"+(this.terminals_[sa]||sa)+"'":F1="Parse error on line "+(Ir+1)+": Unexpected "+(sa==I1?"end of input":"'"+(this.terminals_[sa]||sa)+"'"),this.parseError(F1,{text:ci.match,token:this.terminals_[sa]||sa,line:ci.yylineno,loc:Us,expected:Gf})}if(us[0]instanceof Array&&us.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Kc+", token: "+sa);switch(us[0]){case 1:$t.push(sa),Ot.push(ci.yytext),pe.push(ci.yylloc),$t.push(us[1]),sa=null,jc?(sa=jc,jc=null):(Xc=ci.yyleng,be=ci.yytext,Ir=ci.yylineno,Us=ci.yylloc,M1>0&&M1--);break;case 2:if(Hs=this.productions_[us[1]][1],sh.$=Ot[Ot.length-Hs],sh._$={first_line:pe[pe.length-(Hs||1)].first_line,last_line:pe[pe.length-1].last_line,first_column:pe[pe.length-(Hs||1)].first_column,last_column:pe[pe.length-1].last_column},ah&&(sh._$.range=[pe[pe.length-(Hs||1)].range[0],pe[pe.length-1].range[1]]),Wl=this.performAction.apply(sh,[be,Xc,Ir,ko.yy,us[1],Ot,pe].concat(O1)),typeof Wl<"u")return Wl;Hs&&($t=$t.slice(0,-1*Hs*2),Ot=Ot.slice(0,-1*Hs),pe=pe.slice(0,-1*Hs)),$t.push(this.productions_[us[1]][0]),Ot.push(sh.$),pe.push(sh._$),B1=ur[$t[$t.length-2]][$t[$t.length-1]],$t.push(B1);break;case 3:return!0}}return!0},"parse")},_a=function(){var qi={EOF:1,parseError:o(function(At,$t){if(this.yy.parser)this.yy.parser.parseError(At,$t);else throw new Error(At)},"parseError"),setInput:o(function(ht,At){return this.yy=At||this.yy||{},this._input=ht,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var ht=this._input[0];this.yytext+=ht,this.yyleng++,this.offset++,this.match+=ht,this.matched+=ht;var At=ht.match(/(?:\r\n?|\n).*/g);return At?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),ht},"input"),unput:o(function(ht){var At=ht.length,$t=ht.split(/(?:\r\n?|\n)/g);this._input=ht+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-At),this.offset-=At;var rt=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),$t.length-1&&(this.yylineno-=$t.length-1);var Ot=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:$t?($t.length===rt.length?this.yylloc.first_column:0)+rt[rt.length-$t.length].length-$t[0].length:this.yylloc.first_column-At},this.options.ranges&&(this.yylloc.range=[Ot[0],Ot[0]+this.yyleng-At]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(ht){this.unput(this.match.slice(ht))},"less"),pastInput:o(function(){var ht=this.matched.substr(0,this.matched.length-this.match.length);return(ht.length>20?"...":"")+ht.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var ht=this.match;return ht.length<20&&(ht+=this._input.substr(0,20-ht.length)),(ht.substr(0,20)+(ht.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var ht=this.pastInput(),At=new Array(ht.length+1).join("-");return ht+this.upcomingInput()+` +`+At+"^"},"showPosition"),test_match:o(function(ht,At){var $t,rt,Ot;if(this.options.backtrack_lexer&&(Ot={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(Ot.yylloc.range=this.yylloc.range.slice(0))),rt=ht[0].match(/(?:\r\n?|\n).*/g),rt&&(this.yylineno+=rt.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:rt?rt[rt.length-1].length-rt[rt.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+ht[0].length},this.yytext+=ht[0],this.match+=ht[0],this.matches=ht,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(ht[0].length),this.matched+=ht[0],$t=this.performAction.call(this,this.yy,this,At,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),$t)return $t;if(this._backtrack){for(var pe in Ot)this[pe]=Ot[pe];return!1}return!1},"test_match"),next:o(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var ht,At,$t,rt;this._more||(this.yytext="",this.match="");for(var Ot=this._currentRules(),pe=0;peAt[0].length)){if(At=$t,rt=pe,this.options.backtrack_lexer){if(ht=this.test_match($t,Ot[pe]),ht!==!1)return ht;if(this._backtrack){At=!1;continue}else return!1}else if(!this.options.flex)break}return At?(ht=this.test_match(At,Ot[rt]),ht!==!1?ht:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:o(function(){var At=this.next();return At||this.lex()},"lex"),begin:o(function(At){this.conditionStack.push(At)},"begin"),popState:o(function(){var At=this.conditionStack.length-1;return At>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:o(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:o(function(At){return At=this.conditionStack.length-1-Math.abs(At||0),At>=0?this.conditionStack[At]:"INITIAL"},"topState"),pushState:o(function(At){this.begin(At)},"pushState"),stateStackSize:o(function(){return this.conditionStack.length},"stateStackSize"),options:{},performAction:o(function(At,$t,rt,Ot){var pe=Ot;switch(rt){case 0:return this.begin("acc_title"),34;break;case 1:return this.popState(),"acc_title_value";break;case 2:return this.begin("acc_descr"),36;break;case 3:return this.popState(),"acc_descr_value";break;case 4:this.begin("acc_descr_multiline");break;case 5:this.popState();break;case 6:return"acc_descr_multiline_value";case 7:this.begin("callbackname");break;case 8:this.popState();break;case 9:this.popState(),this.begin("callbackargs");break;case 10:return 92;case 11:this.popState();break;case 12:return 93;case 13:return"MD_STR";case 14:this.popState();break;case 15:this.begin("md_string");break;case 16:return"STR";case 17:this.popState();break;case 18:this.pushState("string");break;case 19:return 81;case 20:return 99;case 21:return 82;case 22:return 101;case 23:return 83;case 24:return 84;case 25:return 94;case 26:this.begin("click");break;case 27:this.popState();break;case 28:return 85;case 29:return At.lex.firstGraph()&&this.begin("dir"),12;break;case 30:return At.lex.firstGraph()&&this.begin("dir"),12;break;case 31:return At.lex.firstGraph()&&this.begin("dir"),12;break;case 32:return 27;case 33:return 32;case 34:return 95;case 35:return 95;case 36:return 95;case 37:return 95;case 38:return this.popState(),13;break;case 39:return this.popState(),14;break;case 40:return this.popState(),14;break;case 41:return this.popState(),14;break;case 42:return this.popState(),14;break;case 43:return this.popState(),14;break;case 44:return this.popState(),14;break;case 45:return this.popState(),14;break;case 46:return this.popState(),14;break;case 47:return this.popState(),14;break;case 48:return this.popState(),14;break;case 49:return 118;case 50:return 119;case 51:return 120;case 52:return 121;case 53:return 102;case 54:return 108;case 55:return 44;case 56:return 58;case 57:return 42;case 58:return 8;case 59:return 103;case 60:return 112;case 61:return this.popState(),75;break;case 62:return this.pushState("edgeText"),73;break;case 63:return 116;case 64:return this.popState(),75;break;case 65:return this.pushState("thickEdgeText"),73;break;case 66:return 116;case 67:return this.popState(),75;break;case 68:return this.pushState("dottedEdgeText"),73;break;case 69:return 116;case 70:return 75;case 71:return this.popState(),51;break;case 72:return"TEXT";case 73:return this.pushState("ellipseText"),50;break;case 74:return this.popState(),53;break;case 75:return this.pushState("text"),52;break;case 76:return this.popState(),55;break;case 77:return this.pushState("text"),54;break;case 78:return 56;case 79:return this.pushState("text"),65;break;case 80:return this.popState(),62;break;case 81:return this.pushState("text"),61;break;case 82:return this.popState(),47;break;case 83:return this.pushState("text"),46;break;case 84:return this.popState(),67;break;case 85:return this.popState(),69;break;case 86:return 114;case 87:return this.pushState("trapText"),66;break;case 88:return this.pushState("trapText"),68;break;case 89:return 115;case 90:return 65;case 91:return 87;case 92:return"SEP";case 93:return 86;case 94:return 112;case 95:return 108;case 96:return 42;case 97:return 106;case 98:return 111;case 99:return 113;case 100:return this.popState(),60;break;case 101:return this.pushState("text"),60;break;case 102:return this.popState(),49;break;case 103:return this.pushState("text"),48;break;case 104:return this.popState(),31;break;case 105:return this.pushState("text"),29;break;case 106:return this.popState(),64;break;case 107:return this.pushState("text"),63;break;case 108:return"TEXT";case 109:return"QUOTE";case 110:return 9;case 111:return 10;case 112:return 11}},"anonymous"),rules:[/^(?:accTitle\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*\{\s*)/,/^(?:[\}])/,/^(?:[^\}]*)/,/^(?:call[\s]+)/,/^(?:\([\s]*\))/,/^(?:\()/,/^(?:[^(]*)/,/^(?:\))/,/^(?:[^)]*)/,/^(?:[^`"]+)/,/^(?:[`]["])/,/^(?:["][`])/,/^(?:[^"]+)/,/^(?:["])/,/^(?:["])/,/^(?:style\b)/,/^(?:default\b)/,/^(?:linkStyle\b)/,/^(?:interpolate\b)/,/^(?:classDef\b)/,/^(?:class\b)/,/^(?:href[\s])/,/^(?:click[\s]+)/,/^(?:[\s\n])/,/^(?:[^\s\n]*)/,/^(?:flowchart-elk\b)/,/^(?:graph\b)/,/^(?:flowchart\b)/,/^(?:subgraph\b)/,/^(?:end\b\s*)/,/^(?:_self\b)/,/^(?:_blank\b)/,/^(?:_parent\b)/,/^(?:_top\b)/,/^(?:(\r?\n)*\s*\n)/,/^(?:\s*LR\b)/,/^(?:\s*RL\b)/,/^(?:\s*TB\b)/,/^(?:\s*BT\b)/,/^(?:\s*TD\b)/,/^(?:\s*BR\b)/,/^(?:\s*<)/,/^(?:\s*>)/,/^(?:\s*\^)/,/^(?:\s*v\b)/,/^(?:.*direction\s+TB[^\n]*)/,/^(?:.*direction\s+BT[^\n]*)/,/^(?:.*direction\s+RL[^\n]*)/,/^(?:.*direction\s+LR[^\n]*)/,/^(?:[0-9]+)/,/^(?:#)/,/^(?::::)/,/^(?::)/,/^(?:&)/,/^(?:;)/,/^(?:,)/,/^(?:\*)/,/^(?:\s*[xo<]?--+[-xo>]\s*)/,/^(?:\s*[xo<]?--\s*)/,/^(?:[^-]|-(?!-)+)/,/^(?:\s*[xo<]?==+[=xo>]\s*)/,/^(?:\s*[xo<]?==\s*)/,/^(?:[^=]|=(?!))/,/^(?:\s*[xo<]?-?\.+-[xo>]?\s*)/,/^(?:\s*[xo<]?-\.\s*)/,/^(?:[^\.]|\.(?!))/,/^(?:\s*~~[\~]+\s*)/,/^(?:[-/\)][\)])/,/^(?:[^\(\)\[\]\{\}]|!\)+)/,/^(?:\(-)/,/^(?:\]\))/,/^(?:\(\[)/,/^(?:\]\])/,/^(?:\[\[)/,/^(?:\[\|)/,/^(?:>)/,/^(?:\)\])/,/^(?:\[\()/,/^(?:\)\)\))/,/^(?:\(\(\()/,/^(?:[\\(?=\])][\]])/,/^(?:\/(?=\])\])/,/^(?:\/(?!\])|\\(?!\])|[^\\\[\]\(\)\{\}\/]+)/,/^(?:\[\/)/,/^(?:\[\\)/,/^(?:<)/,/^(?:>)/,/^(?:\^)/,/^(?:\\\|)/,/^(?:v\b)/,/^(?:\*)/,/^(?:#)/,/^(?:&)/,/^(?:([A-Za-z0-9!"\#$%&'*+\.`?\\_\/]|-(?=[^\>\-\.])|(?!))+)/,/^(?:-)/,/^(?:[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|[\uFFD2-\uFFD7\uFFDA-\uFFDC])/,/^(?:\|)/,/^(?:\|)/,/^(?:\))/,/^(?:\()/,/^(?:\])/,/^(?:\[)/,/^(?:(\}))/,/^(?:\{)/,/^(?:[^\[\]\(\)\{\}\|\"]+)/,/^(?:")/,/^(?:(\r?\n)+)/,/^(?:\s)/,/^(?:$)/],conditions:{callbackargs:{rules:[11,12,15,18,70,73,75,77,81,83,87,88,101,103,105,107],inclusive:!1},callbackname:{rules:[8,9,10,15,18,70,73,75,77,81,83,87,88,101,103,105,107],inclusive:!1},href:{rules:[15,18,70,73,75,77,81,83,87,88,101,103,105,107],inclusive:!1},click:{rules:[15,18,27,28,70,73,75,77,81,83,87,88,101,103,105,107],inclusive:!1},dottedEdgeText:{rules:[15,18,67,69,70,73,75,77,81,83,87,88,101,103,105,107],inclusive:!1},thickEdgeText:{rules:[15,18,64,66,70,73,75,77,81,83,87,88,101,103,105,107],inclusive:!1},edgeText:{rules:[15,18,61,63,70,73,75,77,81,83,87,88,101,103,105,107],inclusive:!1},trapText:{rules:[15,18,70,73,75,77,81,83,84,85,86,87,88,101,103,105,107],inclusive:!1},ellipseText:{rules:[15,18,70,71,72,73,75,77,81,83,87,88,101,103,105,107],inclusive:!1},text:{rules:[15,18,70,73,74,75,76,77,80,81,82,83,87,88,100,101,102,103,104,105,106,107,108],inclusive:!1},vertex:{rules:[15,18,70,73,75,77,81,83,87,88,101,103,105,107],inclusive:!1},dir:{rules:[15,18,38,39,40,41,42,43,44,45,46,47,48,70,73,75,77,81,83,87,88,101,103,105,107],inclusive:!1},acc_descr_multiline:{rules:[5,6,15,18,70,73,75,77,81,83,87,88,101,103,105,107],inclusive:!1},acc_descr:{rules:[3,15,18,70,73,75,77,81,83,87,88,101,103,105,107],inclusive:!1},acc_title:{rules:[1,15,18,70,73,75,77,81,83,87,88,101,103,105,107],inclusive:!1},md_string:{rules:[13,14,15,18,70,73,75,77,81,83,87,88,101,103,105,107],inclusive:!1},string:{rules:[15,16,17,18,70,73,75,77,81,83,87,88,101,103,105,107],inclusive:!1},INITIAL:{rules:[0,2,4,7,15,18,19,20,21,22,23,24,25,26,29,30,31,32,33,34,35,36,37,49,50,51,52,53,54,55,56,57,58,59,60,61,62,64,65,67,68,70,73,75,77,78,79,81,83,87,88,89,90,91,92,93,94,95,96,97,98,99,101,103,105,107,109,110,111,112],inclusive:!0}}};return qi}();Ha.lexer=_a;function To(){this.yy={}}return o(To,"Parser"),To.prototype=Ha,Ha.Parser=To,new To}();vD.parser=vD;Xre=vD});var kNe,ENe,Kre,Qre=R(()=>{"use strict";al();kNe=o((t,e)=>{let r=X1,n=r(t,"r"),i=r(t,"g"),a=r(t,"b");return Ws(n,i,a,e)},"fade"),ENe=o(t=>`.label { + font-family: ${t.fontFamily}; + color: ${t.nodeTextColor||t.textColor}; + } + .cluster-label text { + fill: ${t.titleColor}; + } + .cluster-label span { + color: ${t.titleColor}; + } + .cluster-label span p { + background-color: transparent; + } + + .label text,span { + fill: ${t.nodeTextColor||t.textColor}; + color: ${t.nodeTextColor||t.textColor}; + } + + .node rect, + .node circle, + .node ellipse, + .node polygon, + .node path { + fill: ${t.mainBkg}; + stroke: ${t.nodeBorder}; + stroke-width: 1px; + } + .rough-node .label text , .node .label text { + text-anchor: middle; + } + // .flowchart-label .text-outer-tspan { + // text-anchor: middle; + // } + // .flowchart-label .text-inner-tspan { + // text-anchor: start; + // } + + .node .katex path { + fill: #000; + stroke: #000; + stroke-width: 1px; + } + + .node .label { + text-align: center; + } + .node.clickable { + cursor: pointer; + } + + .arrowheadPath { + fill: ${t.arrowheadColor}; + } + + .edgePath .path { + stroke: ${t.lineColor}; + stroke-width: 2.0px; + } + + .flowchart-link { + stroke: ${t.lineColor}; + fill: none; + } + + .edgeLabel { + background-color: ${t.edgeLabelBackground}; + p { + background-color: ${t.edgeLabelBackground}; + } + rect { + opacity: 0.5; + background-color: ${t.edgeLabelBackground}; + fill: ${t.edgeLabelBackground}; + } + text-align: center; + } + + /* For html labels only */ + .labelBkg { + background-color: ${kNe(t.edgeLabelBackground,.5)}; + // background-color: + } + + .cluster rect { + fill: ${t.clusterBkg}; + stroke: ${t.clusterBorder}; + stroke-width: 1px; + } + + .cluster text { + fill: ${t.titleColor}; + } + + .cluster span { + color: ${t.titleColor}; + } + /* .cluster div { + color: ${t.titleColor}; + } */ + + div.mermaidTooltip { + position: absolute; + text-align: center; + max-width: 200px; + padding: 2px; + font-family: ${t.fontFamily}; + font-size: 12px; + background: ${t.tertiaryColor}; + border: 1px solid ${t.border2}; + border-radius: 2px; + pointer-events: none; + z-index: 100; + } + + .flowchartTitleText { + text-anchor: middle; + font-size: 18px; + fill: ${t.textColor}; + } +`,"getStyles"),Kre=ENe});var cT={};hr(cT,{diagram:()=>CNe});var CNe,uT=R(()=>{"use strict";_t();f9();qre();jre();Qre();CNe={parser:Xre,db:A5,renderer:Wre,styles:Kre,init:o(t=>{t.flowchart||(t.flowchart={}),t.layout&&iS({layout:t.layout}),t.flowchart.arrowMarkerAbsolute=t.arrowMarkerAbsolute,iS({flowchart:{arrowMarkerAbsolute:t.arrowMarkerAbsolute}}),A5.clear(),A5.setGen("gen-2")},"init")}});var xD,rne,nne=R(()=>{"use strict";xD=function(){var t=o(function(A,L,M,N){for(M=M||{},N=A.length;N--;M[A[N]]=L);return M},"o"),e=[6,8,10,20,22,24,26,27,28],r=[1,10],n=[1,11],i=[1,12],a=[1,13],s=[1,14],l=[1,15],u=[1,21],h=[1,22],f=[1,23],d=[1,24],p=[1,25],m=[6,8,10,13,15,18,19,20,22,24,26,27,28,41,42,43,44,45],g=[1,34],y=[27,28,46,47],v=[41,42,43,44,45],x=[17,34],b=[1,54],w=[1,53],S=[17,34,36,38],T={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,start:3,ER_DIAGRAM:4,document:5,EOF:6,line:7,SPACE:8,statement:9,NEWLINE:10,entityName:11,relSpec:12,":":13,role:14,BLOCK_START:15,attributes:16,BLOCK_STOP:17,SQS:18,SQE:19,title:20,title_value:21,acc_title:22,acc_title_value:23,acc_descr:24,acc_descr_value:25,acc_descr_multiline_value:26,ALPHANUM:27,ENTITY_NAME:28,attribute:29,attributeType:30,attributeName:31,attributeKeyTypeList:32,attributeComment:33,ATTRIBUTE_WORD:34,attributeKeyType:35,COMMA:36,ATTRIBUTE_KEY:37,COMMENT:38,cardinality:39,relType:40,ZERO_OR_ONE:41,ZERO_OR_MORE:42,ONE_OR_MORE:43,ONLY_ONE:44,MD_PARENT:45,NON_IDENTIFYING:46,IDENTIFYING:47,WORD:48,$accept:0,$end:1},terminals_:{2:"error",4:"ER_DIAGRAM",6:"EOF",8:"SPACE",10:"NEWLINE",13:":",15:"BLOCK_START",17:"BLOCK_STOP",18:"SQS",19:"SQE",20:"title",21:"title_value",22:"acc_title",23:"acc_title_value",24:"acc_descr",25:"acc_descr_value",26:"acc_descr_multiline_value",27:"ALPHANUM",28:"ENTITY_NAME",34:"ATTRIBUTE_WORD",36:"COMMA",37:"ATTRIBUTE_KEY",38:"COMMENT",41:"ZERO_OR_ONE",42:"ZERO_OR_MORE",43:"ONE_OR_MORE",44:"ONLY_ONE",45:"MD_PARENT",46:"NON_IDENTIFYING",47:"IDENTIFYING",48:"WORD"},productions_:[0,[3,3],[5,0],[5,2],[7,2],[7,1],[7,1],[7,1],[9,5],[9,4],[9,3],[9,1],[9,7],[9,6],[9,4],[9,2],[9,2],[9,2],[9,1],[11,1],[11,1],[16,1],[16,2],[29,2],[29,3],[29,3],[29,4],[30,1],[31,1],[32,1],[32,3],[35,1],[33,1],[12,3],[39,1],[39,1],[39,1],[39,1],[39,1],[40,1],[40,1],[14,1],[14,1],[14,1]],performAction:o(function(L,M,N,k,I,C,O){var D=C.length-1;switch(I){case 1:break;case 2:this.$=[];break;case 3:C[D-1].push(C[D]),this.$=C[D-1];break;case 4:case 5:this.$=C[D];break;case 6:case 7:this.$=[];break;case 8:k.addEntity(C[D-4]),k.addEntity(C[D-2]),k.addRelationship(C[D-4],C[D],C[D-2],C[D-3]);break;case 9:k.addEntity(C[D-3]),k.addAttributes(C[D-3],C[D-1]);break;case 10:k.addEntity(C[D-2]);break;case 11:k.addEntity(C[D]);break;case 12:k.addEntity(C[D-6],C[D-4]),k.addAttributes(C[D-6],C[D-1]);break;case 13:k.addEntity(C[D-5],C[D-3]);break;case 14:k.addEntity(C[D-3],C[D-1]);break;case 15:case 16:this.$=C[D].trim(),k.setAccTitle(this.$);break;case 17:case 18:this.$=C[D].trim(),k.setAccDescription(this.$);break;case 19:case 43:this.$=C[D];break;case 20:case 41:case 42:this.$=C[D].replace(/"/g,"");break;case 21:case 29:this.$=[C[D]];break;case 22:C[D].push(C[D-1]),this.$=C[D];break;case 23:this.$={attributeType:C[D-1],attributeName:C[D]};break;case 24:this.$={attributeType:C[D-2],attributeName:C[D-1],attributeKeyTypeList:C[D]};break;case 25:this.$={attributeType:C[D-2],attributeName:C[D-1],attributeComment:C[D]};break;case 26:this.$={attributeType:C[D-3],attributeName:C[D-2],attributeKeyTypeList:C[D-1],attributeComment:C[D]};break;case 27:case 28:case 31:this.$=C[D];break;case 30:C[D-2].push(C[D]),this.$=C[D-2];break;case 32:this.$=C[D].replace(/"/g,"");break;case 33:this.$={cardA:C[D],relType:C[D-1],cardB:C[D-2]};break;case 34:this.$=k.Cardinality.ZERO_OR_ONE;break;case 35:this.$=k.Cardinality.ZERO_OR_MORE;break;case 36:this.$=k.Cardinality.ONE_OR_MORE;break;case 37:this.$=k.Cardinality.ONLY_ONE;break;case 38:this.$=k.Cardinality.MD_PARENT;break;case 39:this.$=k.Identification.NON_IDENTIFYING;break;case 40:this.$=k.Identification.IDENTIFYING;break}},"anonymous"),table:[{3:1,4:[1,2]},{1:[3]},t(e,[2,2],{5:3}),{6:[1,4],7:5,8:[1,6],9:7,10:[1,8],11:9,20:r,22:n,24:i,26:a,27:s,28:l},t(e,[2,7],{1:[2,1]}),t(e,[2,3]),{9:16,11:9,20:r,22:n,24:i,26:a,27:s,28:l},t(e,[2,5]),t(e,[2,6]),t(e,[2,11],{12:17,39:20,15:[1,18],18:[1,19],41:u,42:h,43:f,44:d,45:p}),{21:[1,26]},{23:[1,27]},{25:[1,28]},t(e,[2,18]),t(m,[2,19]),t(m,[2,20]),t(e,[2,4]),{11:29,27:s,28:l},{16:30,17:[1,31],29:32,30:33,34:g},{11:35,27:s,28:l},{40:36,46:[1,37],47:[1,38]},t(y,[2,34]),t(y,[2,35]),t(y,[2,36]),t(y,[2,37]),t(y,[2,38]),t(e,[2,15]),t(e,[2,16]),t(e,[2,17]),{13:[1,39]},{17:[1,40]},t(e,[2,10]),{16:41,17:[2,21],29:32,30:33,34:g},{31:42,34:[1,43]},{34:[2,27]},{19:[1,44]},{39:45,41:u,42:h,43:f,44:d,45:p},t(v,[2,39]),t(v,[2,40]),{14:46,27:[1,49],28:[1,48],48:[1,47]},t(e,[2,9]),{17:[2,22]},t(x,[2,23],{32:50,33:51,35:52,37:b,38:w}),t([17,34,37,38],[2,28]),t(e,[2,14],{15:[1,55]}),t([27,28],[2,33]),t(e,[2,8]),t(e,[2,41]),t(e,[2,42]),t(e,[2,43]),t(x,[2,24],{33:56,36:[1,57],38:w}),t(x,[2,25]),t(S,[2,29]),t(x,[2,32]),t(S,[2,31]),{16:58,17:[1,59],29:32,30:33,34:g},t(x,[2,26]),{35:60,37:b},{17:[1,61]},t(e,[2,13]),t(S,[2,30]),t(e,[2,12])],defaultActions:{34:[2,27],41:[2,22]},parseError:o(function(L,M){if(M.recoverable)this.trace(L);else{var N=new Error(L);throw N.hash=M,N}},"parseError"),parse:o(function(L){var M=this,N=[0],k=[],I=[null],C=[],O=this.table,D="",P=0,F=0,B=0,$=2,z=1,Y=C.slice.call(arguments,1),Q=Object.create(this.lexer),X={yy:{}};for(var ie in this.yy)Object.prototype.hasOwnProperty.call(this.yy,ie)&&(X.yy[ie]=this.yy[ie]);Q.setInput(L,X.yy),X.yy.lexer=Q,X.yy.parser=this,typeof Q.yylloc>"u"&&(Q.yylloc={});var j=Q.yylloc;C.push(j);var J=Q.options&&Q.options.ranges;typeof X.yy.parseError=="function"?this.parseError=X.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function Z(Pe){N.length=N.length-2*Pe,I.length=I.length-Pe,C.length=C.length-Pe}o(Z,"popStack");function H(){var Pe;return Pe=k.pop()||Q.lex()||z,typeof Pe!="number"&&(Pe instanceof Array&&(k=Pe,Pe=k.pop()),Pe=M.symbols_[Pe]||Pe),Pe}o(H,"lex");for(var q,K,se,ce,ue,te,De={},oe,ke,Ie,Se;;){if(se=N[N.length-1],this.defaultActions[se]?ce=this.defaultActions[se]:((q===null||typeof q>"u")&&(q=H()),ce=O[se]&&O[se][q]),typeof ce>"u"||!ce.length||!ce[0]){var Ue="";Se=[];for(oe in O[se])this.terminals_[oe]&&oe>$&&Se.push("'"+this.terminals_[oe]+"'");Q.showPosition?Ue="Parse error on line "+(P+1)+`: +`+Q.showPosition()+` +Expecting `+Se.join(", ")+", got '"+(this.terminals_[q]||q)+"'":Ue="Parse error on line "+(P+1)+": Unexpected "+(q==z?"end of input":"'"+(this.terminals_[q]||q)+"'"),this.parseError(Ue,{text:Q.match,token:this.terminals_[q]||q,line:Q.yylineno,loc:j,expected:Se})}if(ce[0]instanceof Array&&ce.length>1)throw new Error("Parse Error: multiple actions possible at state: "+se+", token: "+q);switch(ce[0]){case 1:N.push(q),I.push(Q.yytext),C.push(Q.yylloc),N.push(ce[1]),q=null,K?(q=K,K=null):(F=Q.yyleng,D=Q.yytext,P=Q.yylineno,j=Q.yylloc,B>0&&B--);break;case 2:if(ke=this.productions_[ce[1]][1],De.$=I[I.length-ke],De._$={first_line:C[C.length-(ke||1)].first_line,last_line:C[C.length-1].last_line,first_column:C[C.length-(ke||1)].first_column,last_column:C[C.length-1].last_column},J&&(De._$.range=[C[C.length-(ke||1)].range[0],C[C.length-1].range[1]]),te=this.performAction.apply(De,[D,F,P,X.yy,ce[1],I,C].concat(Y)),typeof te<"u")return te;ke&&(N=N.slice(0,-1*ke*2),I=I.slice(0,-1*ke),C=C.slice(0,-1*ke)),N.push(this.productions_[ce[1]][0]),I.push(De.$),C.push(De._$),Ie=O[N[N.length-2]][N[N.length-1]],N.push(Ie);break;case 3:return!0}}return!0},"parse")},E=function(){var A={EOF:1,parseError:o(function(M,N){if(this.yy.parser)this.yy.parser.parseError(M,N);else throw new Error(M)},"parseError"),setInput:o(function(L,M){return this.yy=M||this.yy||{},this._input=L,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var L=this._input[0];this.yytext+=L,this.yyleng++,this.offset++,this.match+=L,this.matched+=L;var M=L.match(/(?:\r\n?|\n).*/g);return M?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),L},"input"),unput:o(function(L){var M=L.length,N=L.split(/(?:\r\n?|\n)/g);this._input=L+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-M),this.offset-=M;var k=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),N.length-1&&(this.yylineno-=N.length-1);var I=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:N?(N.length===k.length?this.yylloc.first_column:0)+k[k.length-N.length].length-N[0].length:this.yylloc.first_column-M},this.options.ranges&&(this.yylloc.range=[I[0],I[0]+this.yyleng-M]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(L){this.unput(this.match.slice(L))},"less"),pastInput:o(function(){var L=this.matched.substr(0,this.matched.length-this.match.length);return(L.length>20?"...":"")+L.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var L=this.match;return L.length<20&&(L+=this._input.substr(0,20-L.length)),(L.substr(0,20)+(L.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var L=this.pastInput(),M=new Array(L.length+1).join("-");return L+this.upcomingInput()+` +`+M+"^"},"showPosition"),test_match:o(function(L,M){var N,k,I;if(this.options.backtrack_lexer&&(I={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(I.yylloc.range=this.yylloc.range.slice(0))),k=L[0].match(/(?:\r\n?|\n).*/g),k&&(this.yylineno+=k.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:k?k[k.length-1].length-k[k.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+L[0].length},this.yytext+=L[0],this.match+=L[0],this.matches=L,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(L[0].length),this.matched+=L[0],N=this.performAction.call(this,this.yy,this,M,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),N)return N;if(this._backtrack){for(var C in I)this[C]=I[C];return!1}return!1},"test_match"),next:o(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var L,M,N,k;this._more||(this.yytext="",this.match="");for(var I=this._currentRules(),C=0;CM[0].length)){if(M=N,k=C,this.options.backtrack_lexer){if(L=this.test_match(N,I[C]),L!==!1)return L;if(this._backtrack){M=!1;continue}else return!1}else if(!this.options.flex)break}return M?(L=this.test_match(M,I[k]),L!==!1?L:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:o(function(){var M=this.next();return M||this.lex()},"lex"),begin:o(function(M){this.conditionStack.push(M)},"begin"),popState:o(function(){var M=this.conditionStack.length-1;return M>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:o(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:o(function(M){return M=this.conditionStack.length-1-Math.abs(M||0),M>=0?this.conditionStack[M]:"INITIAL"},"topState"),pushState:o(function(M){this.begin(M)},"pushState"),stateStackSize:o(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:o(function(M,N,k,I){var C=I;switch(k){case 0:return this.begin("acc_title"),22;break;case 1:return this.popState(),"acc_title_value";break;case 2:return this.begin("acc_descr"),24;break;case 3:return this.popState(),"acc_descr_value";break;case 4:this.begin("acc_descr_multiline");break;case 5:this.popState();break;case 6:return"acc_descr_multiline_value";case 7:return 10;case 8:break;case 9:return 8;case 10:return 28;case 11:return 48;case 12:return 4;case 13:return this.begin("block"),15;break;case 14:return 36;case 15:break;case 16:return 37;case 17:return 34;case 18:return 34;case 19:return 38;case 20:break;case 21:return this.popState(),17;break;case 22:return N.yytext[0];case 23:return 18;case 24:return 19;case 25:return 41;case 26:return 43;case 27:return 43;case 28:return 43;case 29:return 41;case 30:return 41;case 31:return 42;case 32:return 42;case 33:return 42;case 34:return 42;case 35:return 42;case 36:return 43;case 37:return 42;case 38:return 43;case 39:return 44;case 40:return 44;case 41:return 44;case 42:return 44;case 43:return 41;case 44:return 42;case 45:return 43;case 46:return 45;case 47:return 46;case 48:return 47;case 49:return 47;case 50:return 46;case 51:return 46;case 52:return 46;case 53:return 27;case 54:return N.yytext[0];case 55:return 6}},"anonymous"),rules:[/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:[\s]+)/i,/^(?:"[^"%\r\n\v\b\\]+")/i,/^(?:"[^"]*")/i,/^(?:erDiagram\b)/i,/^(?:\{)/i,/^(?:,)/i,/^(?:\s+)/i,/^(?:\b((?:PK)|(?:FK)|(?:UK))\b)/i,/^(?:(.*?)[~](.*?)*[~])/i,/^(?:[\*A-Za-z_][A-Za-z0-9\-_\[\]\(\)]*)/i,/^(?:"[^"]*")/i,/^(?:[\n]+)/i,/^(?:\})/i,/^(?:.)/i,/^(?:\[)/i,/^(?:\])/i,/^(?:one or zero\b)/i,/^(?:one or more\b)/i,/^(?:one or many\b)/i,/^(?:1\+)/i,/^(?:\|o\b)/i,/^(?:zero or one\b)/i,/^(?:zero or more\b)/i,/^(?:zero or many\b)/i,/^(?:0\+)/i,/^(?:\}o\b)/i,/^(?:many\(0\))/i,/^(?:many\(1\))/i,/^(?:many\b)/i,/^(?:\}\|)/i,/^(?:one\b)/i,/^(?:only one\b)/i,/^(?:1\b)/i,/^(?:\|\|)/i,/^(?:o\|)/i,/^(?:o\{)/i,/^(?:\|\{)/i,/^(?:\s*u\b)/i,/^(?:\.\.)/i,/^(?:--)/i,/^(?:to\b)/i,/^(?:optionally to\b)/i,/^(?:\.-)/i,/^(?:-\.)/i,/^(?:[A-Za-z_][A-Za-z0-9\-_]*)/i,/^(?:.)/i,/^(?:$)/i],conditions:{acc_descr_multiline:{rules:[5,6],inclusive:!1},acc_descr:{rules:[3],inclusive:!1},acc_title:{rules:[1],inclusive:!1},block:{rules:[14,15,16,17,18,19,20,21,22],inclusive:!1},INITIAL:{rules:[0,2,4,7,8,9,10,11,12,13,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55],inclusive:!0}}};return A}();T.lexer=E;function _(){this.yy={}}return o(_,"Parser"),_.prototype=T,T.Parser=_,new _}();xD.parser=xD;rne=xD});var Hd,bD,NNe,MNe,ine,INe,ONe,PNe,BNe,FNe,ane,sne=R(()=>{"use strict";ut();_t();bi();Hd=new Map,bD=[],NNe={ZERO_OR_ONE:"ZERO_OR_ONE",ZERO_OR_MORE:"ZERO_OR_MORE",ONE_OR_MORE:"ONE_OR_MORE",ONLY_ONE:"ONLY_ONE",MD_PARENT:"MD_PARENT"},MNe={NON_IDENTIFYING:"NON_IDENTIFYING",IDENTIFYING:"IDENTIFYING"},ine=o(function(t,e=void 0){return Hd.has(t)?!Hd.get(t).alias&&e&&(Hd.get(t).alias=e,V.info(`Add alias '${e}' to entity '${t}'`)):(Hd.set(t,{attributes:[],alias:e}),V.info("Added new entity :",t)),Hd.get(t)},"addEntity"),INe=o(()=>Hd,"getEntities"),ONe=o(function(t,e){let r=ine(t),n;for(n=e.length-1;n>=0;n--)r.attributes.push(e[n]),V.debug("Added attribute ",e[n].attributeName)},"addAttributes"),PNe=o(function(t,e,r,n){let i={entityA:t,roleA:e,entityB:r,relSpec:n};bD.push(i),V.debug("Added new relationship :",i)},"addRelationship"),BNe=o(()=>bD,"getRelationships"),FNe=o(function(){Hd=new Map,bD=[],vr()},"clear"),ane={Cardinality:NNe,Identification:MNe,getConfig:o(()=>de().er,"getConfig"),addEntity:ine,addAttributes:ONe,getEntities:INe,addRelationship:PNe,getRelationships:BNe,clear:FNe,setAccTitle:kr,getAccTitle:Ar,setAccDescription:_r,getAccDescription:Lr,setDiagramTitle:nn,getDiagramTitle:Xr}});var Dl,zNe,$o,one=R(()=>{"use strict";Dl={ONLY_ONE_START:"ONLY_ONE_START",ONLY_ONE_END:"ONLY_ONE_END",ZERO_OR_ONE_START:"ZERO_OR_ONE_START",ZERO_OR_ONE_END:"ZERO_OR_ONE_END",ONE_OR_MORE_START:"ONE_OR_MORE_START",ONE_OR_MORE_END:"ONE_OR_MORE_END",ZERO_OR_MORE_START:"ZERO_OR_MORE_START",ZERO_OR_MORE_END:"ZERO_OR_MORE_END",MD_PARENT_END:"MD_PARENT_END",MD_PARENT_START:"MD_PARENT_START"},zNe=o(function(t,e){let r;t.append("defs").append("marker").attr("id",Dl.MD_PARENT_START).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",Dl.MD_PARENT_END).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",Dl.ONLY_ONE_START).attr("refX",0).attr("refY",9).attr("markerWidth",18).attr("markerHeight",18).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M9,0 L9,18 M15,0 L15,18"),t.append("defs").append("marker").attr("id",Dl.ONLY_ONE_END).attr("refX",18).attr("refY",9).attr("markerWidth",18).attr("markerHeight",18).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M3,0 L3,18 M9,0 L9,18"),r=t.append("defs").append("marker").attr("id",Dl.ZERO_OR_ONE_START).attr("refX",0).attr("refY",9).attr("markerWidth",30).attr("markerHeight",18).attr("orient","auto"),r.append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",21).attr("cy",9).attr("r",6),r.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M9,0 L9,18"),r=t.append("defs").append("marker").attr("id",Dl.ZERO_OR_ONE_END).attr("refX",30).attr("refY",9).attr("markerWidth",30).attr("markerHeight",18).attr("orient","auto"),r.append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",9).attr("cy",9).attr("r",6),r.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M21,0 L21,18"),t.append("defs").append("marker").attr("id",Dl.ONE_OR_MORE_START).attr("refX",18).attr("refY",18).attr("markerWidth",45).attr("markerHeight",36).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M0,18 Q 18,0 36,18 Q 18,36 0,18 M42,9 L42,27"),t.append("defs").append("marker").attr("id",Dl.ONE_OR_MORE_END).attr("refX",27).attr("refY",18).attr("markerWidth",45).attr("markerHeight",36).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M3,9 L3,27 M9,18 Q27,0 45,18 Q27,36 9,18"),r=t.append("defs").append("marker").attr("id",Dl.ZERO_OR_MORE_START).attr("refX",18).attr("refY",18).attr("markerWidth",57).attr("markerHeight",36).attr("orient","auto"),r.append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",48).attr("cy",18).attr("r",6),r.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M0,18 Q18,0 36,18 Q18,36 0,18"),r=t.append("defs").append("marker").attr("id",Dl.ZERO_OR_MORE_END).attr("refX",39).attr("refY",18).attr("markerWidth",57).attr("markerHeight",36).attr("orient","auto"),r.append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",9).attr("cy",18).attr("r",6),r.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M21,18 Q39,0 57,18 Q39,36 21,18")},"insertMarkers"),$o={ERMarkers:Dl,insertMarkers:zNe}});var lne,cne=R(()=>{"use strict";lne=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i});function GNe(t){return typeof t=="string"&&lne.test(t)}var une,hne=R(()=>{"use strict";cne();o(GNe,"validate");une=GNe});function fne(t,e=0){return va[t[e+0]]+va[t[e+1]]+va[t[e+2]]+va[t[e+3]]+"-"+va[t[e+4]]+va[t[e+5]]+"-"+va[t[e+6]]+va[t[e+7]]+"-"+va[t[e+8]]+va[t[e+9]]+"-"+va[t[e+10]]+va[t[e+11]]+va[t[e+12]]+va[t[e+13]]+va[t[e+14]]+va[t[e+15]]}var va,dne=R(()=>{"use strict";va=[];for(let t=0;t<256;++t)va.push((t+256).toString(16).slice(1));o(fne,"unsafeStringify")});function $Ne(t){if(!une(t))throw TypeError("Invalid UUID");let e,r=new Uint8Array(16);return r[0]=(e=parseInt(t.slice(0,8),16))>>>24,r[1]=e>>>16&255,r[2]=e>>>8&255,r[3]=e&255,r[4]=(e=parseInt(t.slice(9,13),16))>>>8,r[5]=e&255,r[6]=(e=parseInt(t.slice(14,18),16))>>>8,r[7]=e&255,r[8]=(e=parseInt(t.slice(19,23),16))>>>8,r[9]=e&255,r[10]=(e=parseInt(t.slice(24,36),16))/1099511627776&255,r[11]=e/4294967296&255,r[12]=e>>>24&255,r[13]=e>>>16&255,r[14]=e>>>8&255,r[15]=e&255,r}var pne,mne=R(()=>{"use strict";hne();o($Ne,"parse");pne=$Ne});function VNe(t){t=unescape(encodeURIComponent(t));let e=[];for(let r=0;r{"use strict";dne();mne();o(VNe,"stringToBytes");UNe="6ba7b810-9dad-11d1-80b4-00c04fd430c8",HNe="6ba7b811-9dad-11d1-80b4-00c04fd430c8";o(wD,"v35")});function YNe(t,e,r,n){switch(t){case 0:return e&r^~e&n;case 1:return e^r^n;case 2:return e&r^e&n^r&n;case 3:return e^r^n}}function TD(t,e){return t<>>32-e}function WNe(t){let e=[1518500249,1859775393,2400959708,3395469782],r=[1732584193,4023233417,2562383102,271733878,3285377520];if(typeof t=="string"){let s=unescape(encodeURIComponent(t));t=[];for(let l=0;l>>0;p=d,d=f,f=TD(h,30)>>>0,h=u,u=y}r[0]=r[0]+u>>>0,r[1]=r[1]+h>>>0,r[2]=r[2]+f>>>0,r[3]=r[3]+d>>>0,r[4]=r[4]+p>>>0}return[r[0]>>24&255,r[0]>>16&255,r[0]>>8&255,r[0]&255,r[1]>>24&255,r[1]>>16&255,r[1]>>8&255,r[1]&255,r[2]>>24&255,r[2]>>16&255,r[2]>>8&255,r[2]&255,r[3]>>24&255,r[3]>>16&255,r[3]>>8&255,r[3]&255,r[4]>>24&255,r[4]>>16&255,r[4]>>8&255,r[4]&255]}var yne,vne=R(()=>{"use strict";o(YNe,"f");o(TD,"ROTL");o(WNe,"sha1");yne=WNe});var qNe,kD,xne=R(()=>{"use strict";gne();vne();qNe=wD("v5",80,yne),kD=qNe});var bne=R(()=>{"use strict";xne()});function nMe(t="",e=""){let r=t.replace(XNe,"");return`${Tne(e)}${Tne(r)}${kD(t,rMe)}`}function Tne(t=""){return t.length>0?`${t}-`:""}var XNe,Ii,Fv,jNe,KNe,QNe,ZNe,kne,JNe,wne,eMe,tMe,rMe,Ene,Cne=R(()=>{"use strict";ya();Zt();Vd();_t();ut();xr();one();Yn();rr();bne();XNe=/[^\dA-Za-z](\W)*/g,Ii={},Fv=new Map,jNe=o(function(t){let e=Object.keys(t);for(let r of e)Ii[r]=t[r]},"setConf"),KNe=o((t,e,r)=>{let n=Ii.entityPadding/3,i=Ii.entityPadding/3,a=Ii.fontSize*.85,s=e.node().getBBox(),l=[],u=!1,h=!1,f=0,d=0,p=0,m=0,g=s.height+n*2,y=1;r.forEach(w=>{w.attributeKeyTypeList!==void 0&&w.attributeKeyTypeList.length>0&&(u=!0),w.attributeComment!==void 0&&(h=!0)}),r.forEach(w=>{let S=`${e.node().id}-attr-${y}`,T=0,E=gh(w.attributeType),_=t.append("text").classed("er entityLabel",!0).attr("id",`${S}-type`).attr("x",0).attr("y",0).style("dominant-baseline","middle").style("text-anchor","left").style("font-family",de().fontFamily).style("font-size",a+"px").text(E),A=t.append("text").classed("er entityLabel",!0).attr("id",`${S}-name`).attr("x",0).attr("y",0).style("dominant-baseline","middle").style("text-anchor","left").style("font-family",de().fontFamily).style("font-size",a+"px").text(w.attributeName),L={};L.tn=_,L.nn=A;let M=_.node().getBBox(),N=A.node().getBBox();if(f=Math.max(f,M.width),d=Math.max(d,N.width),T=Math.max(M.height,N.height),u){let k=w.attributeKeyTypeList!==void 0?w.attributeKeyTypeList.join(","):"",I=t.append("text").classed("er entityLabel",!0).attr("id",`${S}-key`).attr("x",0).attr("y",0).style("dominant-baseline","middle").style("text-anchor","left").style("font-family",de().fontFamily).style("font-size",a+"px").text(k);L.kn=I;let C=I.node().getBBox();p=Math.max(p,C.width),T=Math.max(T,C.height)}if(h){let k=t.append("text").classed("er entityLabel",!0).attr("id",`${S}-comment`).attr("x",0).attr("y",0).style("dominant-baseline","middle").style("text-anchor","left").style("font-family",de().fontFamily).style("font-size",a+"px").text(w.attributeComment||"");L.cn=k;let I=k.node().getBBox();m=Math.max(m,I.width),T=Math.max(T,I.height)}L.height=T,l.push(L),g+=T+n*2,y+=1});let v=4;u&&(v+=2),h&&(v+=2);let x=f+d+p+m,b={width:Math.max(Ii.minEntityWidth,Math.max(s.width+Ii.entityPadding*2,x+i*v)),height:r.length>0?g:Math.max(Ii.minEntityHeight,s.height+Ii.entityPadding*2)};if(r.length>0){let w=Math.max(0,(b.width-x-i*v)/(v/2));e.attr("transform","translate("+b.width/2+","+(n+s.height/2)+")");let S=s.height+n*2,T="attributeBoxOdd";l.forEach(E=>{let _=S+n+E.height/2;E.tn.attr("transform","translate("+i+","+_+")");let A=t.insert("rect","#"+E.tn.node().id).classed(`er ${T}`,!0).attr("x",0).attr("y",S).attr("width",f+i*2+w).attr("height",E.height+n*2),L=parseFloat(A.attr("x"))+parseFloat(A.attr("width"));E.nn.attr("transform","translate("+(L+i)+","+_+")");let M=t.insert("rect","#"+E.nn.node().id).classed(`er ${T}`,!0).attr("x",L).attr("y",S).attr("width",d+i*2+w).attr("height",E.height+n*2),N=parseFloat(M.attr("x"))+parseFloat(M.attr("width"));if(u){E.kn.attr("transform","translate("+(N+i)+","+_+")");let k=t.insert("rect","#"+E.kn.node().id).classed(`er ${T}`,!0).attr("x",N).attr("y",S).attr("width",p+i*2+w).attr("height",E.height+n*2);N=parseFloat(k.attr("x"))+parseFloat(k.attr("width"))}h&&(E.cn.attr("transform","translate("+(N+i)+","+_+")"),t.insert("rect","#"+E.cn.node().id).classed(`er ${T}`,"true").attr("x",N).attr("y",S).attr("width",m+i*2+w).attr("height",E.height+n*2)),S+=E.height+n*2,T=T==="attributeBoxOdd"?"attributeBoxEven":"attributeBoxOdd"})}else b.height=Math.max(Ii.minEntityHeight,g),e.attr("transform","translate("+b.width/2+","+b.height/2+")");return b},"drawAttributes"),QNe=o(function(t,e,r){let n=[...e.keys()],i;return n.forEach(function(a){let s=nMe(a,"entity");Fv.set(a,s);let l=t.append("g").attr("id",s);i=i===void 0?s:i;let u="text-"+s,h=l.append("text").classed("er entityLabel",!0).attr("id",u).attr("x",0).attr("y",0).style("dominant-baseline","middle").style("text-anchor","middle").style("font-family",de().fontFamily).style("font-size",Ii.fontSize+"px").text(e.get(a).alias??a),{width:f,height:d}=KNe(l,h,e.get(a).attributes),m=l.insert("rect","#"+u).classed("er entityBox",!0).attr("x",0).attr("y",0).attr("width",f).attr("height",d).node().getBBox();r.setNode(s,{width:m.width,height:m.height,shape:"rect",id:s})}),i},"drawEntities"),ZNe=o(function(t,e){e.nodes().forEach(function(r){r!==void 0&&e.node(r)!==void 0&&t.select("#"+r).attr("transform","translate("+(e.node(r).x-e.node(r).width/2)+","+(e.node(r).y-e.node(r).height/2)+" )")})},"adjustEntities"),kne=o(function(t){return(t.entityA+t.roleA+t.entityB).replace(/\s/g,"")},"getEdgeName"),JNe=o(function(t,e){return t.forEach(function(r){e.setEdge(Fv.get(r.entityA),Fv.get(r.entityB),{relationship:r},kne(r))}),t},"addRelationships"),wne=0,eMe=o(function(t,e,r,n,i){wne++;let a=r.edge(Fv.get(e.entityA),Fv.get(e.entityB),kne(e)),s=ha().x(function(y){return y.x}).y(function(y){return y.y}).curve(vs),l=t.insert("path","#"+n).classed("er relationshipLine",!0).attr("d",s(a.points)).style("stroke",Ii.stroke).style("fill","none");e.relSpec.relType===i.db.Identification.NON_IDENTIFYING&&l.attr("stroke-dasharray","8,8");let u="";switch(Ii.arrowMarkerAbsolute&&(u=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search,u=u.replace(/\(/g,"\\("),u=u.replace(/\)/g,"\\)")),e.relSpec.cardA){case i.db.Cardinality.ZERO_OR_ONE:l.attr("marker-end","url("+u+"#"+$o.ERMarkers.ZERO_OR_ONE_END+")");break;case i.db.Cardinality.ZERO_OR_MORE:l.attr("marker-end","url("+u+"#"+$o.ERMarkers.ZERO_OR_MORE_END+")");break;case i.db.Cardinality.ONE_OR_MORE:l.attr("marker-end","url("+u+"#"+$o.ERMarkers.ONE_OR_MORE_END+")");break;case i.db.Cardinality.ONLY_ONE:l.attr("marker-end","url("+u+"#"+$o.ERMarkers.ONLY_ONE_END+")");break;case i.db.Cardinality.MD_PARENT:l.attr("marker-end","url("+u+"#"+$o.ERMarkers.MD_PARENT_END+")");break}switch(e.relSpec.cardB){case i.db.Cardinality.ZERO_OR_ONE:l.attr("marker-start","url("+u+"#"+$o.ERMarkers.ZERO_OR_ONE_START+")");break;case i.db.Cardinality.ZERO_OR_MORE:l.attr("marker-start","url("+u+"#"+$o.ERMarkers.ZERO_OR_MORE_START+")");break;case i.db.Cardinality.ONE_OR_MORE:l.attr("marker-start","url("+u+"#"+$o.ERMarkers.ONE_OR_MORE_START+")");break;case i.db.Cardinality.ONLY_ONE:l.attr("marker-start","url("+u+"#"+$o.ERMarkers.ONLY_ONE_START+")");break;case i.db.Cardinality.MD_PARENT:l.attr("marker-start","url("+u+"#"+$o.ERMarkers.MD_PARENT_START+")");break}let h=l.node().getTotalLength(),f=l.node().getPointAtLength(h*.5),d="rel"+wne,p=e.roleA.split(/
    /g),m=t.append("text").classed("er relationshipLabel",!0).attr("id",d).attr("x",f.x).attr("y",f.y).style("text-anchor","middle").style("dominant-baseline","middle").style("font-family",de().fontFamily).style("font-size",Ii.fontSize+"px");if(p.length==1)m.text(e.roleA);else{let y=-(p.length-1)*.5;p.forEach((v,x)=>{m.append("tspan").attr("x",f.x).attr("dy",`${x===0?y:1}em`).text(v)})}let g=m.node().getBBox();t.insert("rect","#"+d).classed("er relationshipLabelBox",!0).attr("x",f.x-g.width/2).attr("y",f.y-g.height/2).attr("width",g.width).attr("height",g.height)},"drawRelationshipFromLayout"),tMe=o(function(t,e,r,n){Ii=de().er,V.info("Drawing ER diagram");let i=de().securityLevel,a;i==="sandbox"&&(a=$e("#i"+e));let l=(i==="sandbox"?$e(a.nodes()[0].contentDocument.body):$e("body")).select(`[id='${e}']`);$o.insertMarkers(l,Ii);let u;u=new lr({multigraph:!0,directed:!0,compound:!1}).setGraph({rankdir:Ii.layoutDirection,marginx:20,marginy:20,nodesep:100,edgesep:100,ranksep:100}).setDefaultEdgeLabel(function(){return{}});let h=QNe(l,n.db.getEntities(),u),f=JNe(n.db.getRelationships(),u);lo(u),ZNe(l,u),f.forEach(function(y){eMe(l,y,u,h,n)});let d=Ii.diagramPadding;Lt.insertTitle(l,"entityTitleText",Ii.titleTopMargin,n.db.getDiagramTitle());let p=l.node().getBBox(),m=p.width+d*2,g=p.height+d*2;Sr(l,g,m,Ii.useMaxWidth),l.attr("viewBox",`${p.x-d} ${p.y-d} ${m} ${g}`)},"draw"),rMe="28e9f9db-3c8d-5aa5-9faf-44286ae5937c";o(nMe,"generateId");o(Tne,"strWithHyphen");Ene={setConf:jNe,draw:tMe}});var iMe,Sne,Ane=R(()=>{"use strict";iMe=o(t=>` + .entityBox { + fill: ${t.mainBkg}; + stroke: ${t.nodeBorder}; + } + + .attributeBoxOdd { + fill: ${t.attributeBackgroundColorOdd}; + stroke: ${t.nodeBorder}; + } + + .attributeBoxEven { + fill: ${t.attributeBackgroundColorEven}; + stroke: ${t.nodeBorder}; + } + + .relationshipLabelBox { + fill: ${t.tertiaryColor}; + opacity: 0.7; + background-color: ${t.tertiaryColor}; + rect { + opacity: 0.5; + } + } + + .relationshipLine { + stroke: ${t.lineColor}; + } + + .entityTitleText { + text-anchor: middle; + font-size: 18px; + fill: ${t.textColor}; + } + #MD_PARENT_START { + fill: #f5f5f5 !important; + stroke: ${t.lineColor} !important; + stroke-width: 1; + } + #MD_PARENT_END { + fill: #f5f5f5 !important; + stroke: ${t.lineColor} !important; + stroke-width: 1; + } + +`,"getStyles"),Sne=iMe});var _ne={};hr(_ne,{diagram:()=>aMe});var aMe,Lne=R(()=>{"use strict";nne();sne();Cne();Ane();aMe={parser:rne,db:ane,renderer:Ene,styles:Sne}});function Xn(t){return typeof t=="object"&&t!==null&&typeof t.$type=="string"}function xa(t){return typeof t=="object"&&t!==null&&typeof t.$refText=="string"}function ED(t){return typeof t=="object"&&t!==null&&typeof t.name=="string"&&typeof t.type=="string"&&typeof t.path=="string"}function Wd(t){return typeof t=="object"&&t!==null&&Xn(t.container)&&xa(t.reference)&&typeof t.message=="string"}function co(t){return typeof t=="object"&&t!==null&&Array.isArray(t.content)}function ef(t){return typeof t=="object"&&t!==null&&typeof t.tokenType=="object"}function zv(t){return co(t)&&typeof t.fullText=="string"}var Yd,Vo=R(()=>{"use strict";o(Xn,"isAstNode");o(xa,"isReference");o(ED,"isAstNodeDescription");o(Wd,"isLinkingError");Yd=class{static{o(this,"AbstractAstReflection")}constructor(){this.subtypes={},this.allSubtypes={}}isInstance(e,r){return Xn(e)&&this.isSubtype(e.$type,r)}isSubtype(e,r){if(e===r)return!0;let n=this.subtypes[e];n||(n=this.subtypes[e]={});let i=n[r];if(i!==void 0)return i;{let a=this.computeIsSubtype(e,r);return n[r]=a,a}}getAllSubTypes(e){let r=this.allSubtypes[e];if(r)return r;{let n=this.getAllTypes(),i=[];for(let a of n)this.isSubtype(a,e)&&i.push(a);return this.allSubtypes[e]=i,i}}};o(co,"isCompositeCstNode");o(ef,"isLeafCstNode");o(zv,"isRootCstNode")});function cMe(t){return typeof t=="string"?t:typeof t>"u"?"undefined":typeof t.toString=="function"?t.toString():Object.prototype.toString.call(t)}function hT(t){return!!t&&typeof t[Symbol.iterator]=="function"}function Kr(...t){if(t.length===1){let e=t[0];if(e instanceof uo)return e;if(hT(e))return new uo(()=>e[Symbol.iterator](),r=>r.next());if(typeof e.length=="number")return new uo(()=>({index:0}),r=>r.index1?new uo(()=>({collIndex:0,arrIndex:0}),e=>{do{if(e.iterator){let r=e.iterator.next();if(!r.done)return r;e.iterator=void 0}if(e.array){if(e.arrIndex{"use strict";uo=class t{static{o(this,"StreamImpl")}constructor(e,r){this.startFn=e,this.nextFn=r}iterator(){let e={state:this.startFn(),next:o(()=>this.nextFn(e.state),"next"),[Symbol.iterator]:()=>e};return e}[Symbol.iterator](){return this.iterator()}isEmpty(){return!!this.iterator().next().done}count(){let e=this.iterator(),r=0,n=e.next();for(;!n.done;)r++,n=e.next();return r}toArray(){let e=[],r=this.iterator(),n;do n=r.next(),n.value!==void 0&&e.push(n.value);while(!n.done);return e}toSet(){return new Set(this)}toMap(e,r){let n=this.map(i=>[e?e(i):i,r?r(i):i]);return new Map(n)}toString(){return this.join()}concat(e){let r=e[Symbol.iterator]();return new t(()=>({first:this.startFn(),firstDone:!1}),n=>{let i;if(!n.firstDone){do if(i=this.nextFn(n.first),!i.done)return i;while(!i.done);n.firstDone=!0}do if(i=r.next(),!i.done)return i;while(!i.done);return Ja})}join(e=","){let r=this.iterator(),n="",i,a=!1;do i=r.next(),i.done||(a&&(n+=e),n+=cMe(i.value)),a=!0;while(!i.done);return n}indexOf(e,r=0){let n=this.iterator(),i=0,a=n.next();for(;!a.done;){if(i>=r&&a.value===e)return i;a=n.next(),i++}return-1}every(e){let r=this.iterator(),n=r.next();for(;!n.done;){if(!e(n.value))return!1;n=r.next()}return!0}some(e){let r=this.iterator(),n=r.next();for(;!n.done;){if(e(n.value))return!0;n=r.next()}return!1}forEach(e){let r=this.iterator(),n=0,i=r.next();for(;!i.done;)e(i.value,n),i=r.next(),n++}map(e){return new t(this.startFn,r=>{let{done:n,value:i}=this.nextFn(r);return n?Ja:{done:!1,value:e(i)}})}filter(e){return new t(this.startFn,r=>{let n;do if(n=this.nextFn(r),!n.done&&e(n.value))return n;while(!n.done);return Ja})}nonNullable(){return this.filter(e=>e!=null)}reduce(e,r){let n=this.iterator(),i=r,a=n.next();for(;!a.done;)i===void 0?i=a.value:i=e(i,a.value),a=n.next();return i}reduceRight(e,r){return this.recursiveReduce(this.iterator(),e,r)}recursiveReduce(e,r,n){let i=e.next();if(i.done)return n;let a=this.recursiveReduce(e,r,n);return a===void 0?i.value:r(a,i.value)}find(e){let r=this.iterator(),n=r.next();for(;!n.done;){if(e(n.value))return n.value;n=r.next()}}findIndex(e){let r=this.iterator(),n=0,i=r.next();for(;!i.done;){if(e(i.value))return n;i=r.next(),n++}return-1}includes(e){let r=this.iterator(),n=r.next();for(;!n.done;){if(n.value===e)return!0;n=r.next()}return!1}flatMap(e){return new t(()=>({this:this.startFn()}),r=>{do{if(r.iterator){let a=r.iterator.next();if(a.done)r.iterator=void 0;else return a}let{done:n,value:i}=this.nextFn(r.this);if(!n){let a=e(i);if(hT(a))r.iterator=a[Symbol.iterator]();else return{done:!1,value:a}}}while(r.iterator);return Ja})}flat(e){if(e===void 0&&(e=1),e<=0)return this;let r=e>1?this.flat(e-1):this;return new t(()=>({this:r.startFn()}),n=>{do{if(n.iterator){let s=n.iterator.next();if(s.done)n.iterator=void 0;else return s}let{done:i,value:a}=r.nextFn(n.this);if(!i)if(hT(a))n.iterator=a[Symbol.iterator]();else return{done:!1,value:a}}while(n.iterator);return Ja})}head(){let r=this.iterator().next();if(!r.done)return r.value}tail(e=1){return new t(()=>{let r=this.startFn();for(let n=0;n({size:0,state:this.startFn()}),r=>(r.size++,r.size>e?Ja:this.nextFn(r.state)))}distinct(e){let r=new Set;return this.filter(n=>{let i=e?e(n):n;return r.has(i)?!1:(r.add(i),!0)})}exclude(e,r){let n=new Set;for(let i of e){let a=r?r(i):i;n.add(a)}return this.filter(i=>{let a=r?r(i):i;return!n.has(a)})}};o(cMe,"toString");o(hT,"isIterable");Gv=new uo(()=>{},()=>Ja),Ja=Object.freeze({done:!0,value:void 0});o(Kr,"stream");Cc=class extends uo{static{o(this,"TreeStreamImpl")}constructor(e,r,n){super(()=>({iterators:n?.includeRoot?[[e][Symbol.iterator]()]:[r(e)[Symbol.iterator]()],pruned:!1}),i=>{for(i.pruned&&(i.iterators.pop(),i.pruned=!1);i.iterators.length>0;){let s=i.iterators[i.iterators.length-1].next();if(s.done)i.iterators.pop();else return i.iterators.push(r(s.value)[Symbol.iterator]()),s}return Ja})}iterator(){let e={state:this.startFn(),next:o(()=>this.nextFn(e.state),"next"),prune:o(()=>{e.state.pruned=!0},"prune"),[Symbol.iterator]:()=>e};return e}};(function(t){function e(a){return a.reduce((s,l)=>s+l,0)}o(e,"sum"),t.sum=e;function r(a){return a.reduce((s,l)=>s*l,0)}o(r,"product"),t.product=r;function n(a){return a.reduce((s,l)=>Math.min(s,l))}o(n,"min"),t.min=n;function i(a){return a.reduce((s,l)=>Math.max(s,l))}o(i,"max"),t.max=i})(Fm||(Fm={}))});var dT={};hr(dT,{DefaultNameRegexp:()=>fT,RangeComparison:()=>Mu,compareRange:()=>Mne,findCommentNode:()=>_D,findDeclarationNodeAtOffset:()=>hMe,findLeafNodeAtOffset:()=>LD,findLeafNodeBeforeOffset:()=>Ine,flattenCst:()=>uMe,getInteriorNodes:()=>pMe,getNextNode:()=>fMe,getPreviousNode:()=>Pne,getStartlineNode:()=>dMe,inRange:()=>AD,isChildNode:()=>SD,isCommentNode:()=>CD,streamCst:()=>qd,toDocumentSegment:()=>Xd,tokenToRange:()=>zm});function qd(t){return new Cc(t,e=>co(e)?e.content:[],{includeRoot:!0})}function uMe(t){return qd(t).filter(ef)}function SD(t,e){for(;t.container;)if(t=t.container,t===e)return!0;return!1}function zm(t){return{start:{character:t.startColumn-1,line:t.startLine-1},end:{character:t.endColumn,line:t.endLine-1}}}function Xd(t){if(!t)return;let{offset:e,end:r,range:n}=t;return{range:n,offset:e,end:r,length:r-e}}function Mne(t,e){if(t.end.linee.end.line||t.start.line===e.end.line&&t.start.character>e.end.character)return Mu.After;let r=t.start.line>e.start.line||t.start.line===e.start.line&&t.start.character>=e.start.character,n=t.end.lineMu.After}function hMe(t,e,r=fT){if(t){if(e>0){let n=e-t.offset,i=t.text.charAt(n);r.test(i)||e--}return LD(t,e)}}function _D(t,e){if(t){let r=Pne(t,!0);if(r&&CD(r,e))return r;if(zv(t)){let n=t.content.findIndex(i=>!i.hidden);for(let i=n-1;i>=0;i--){let a=t.content[i];if(CD(a,e))return a}}}}function CD(t,e){return ef(t)&&e.includes(t.tokenType.name)}function LD(t,e){if(ef(t))return t;if(co(t)){let r=One(t,e,!1);if(r)return LD(r,e)}}function Ine(t,e){if(ef(t))return t;if(co(t)){let r=One(t,e,!0);if(r)return Ine(r,e)}}function One(t,e,r){let n=0,i=t.content.length-1,a;for(;n<=i;){let s=Math.floor((n+i)/2),l=t.content[s];if(l.offset<=e&&l.end>e)return l;l.end<=e?(a=r?l:void 0,n=s+1):i=s-1}return a}function Pne(t,e=!0){for(;t.container;){let r=t.container,n=r.content.indexOf(t);for(;n>0;){n--;let i=r.content[n];if(e||!i.hidden)return i}t=r}}function fMe(t,e=!0){for(;t.container;){let r=t.container,n=r.content.indexOf(t),i=r.content.length-1;for(;n{"use strict";Vo();Ds();o(qd,"streamCst");o(uMe,"flattenCst");o(SD,"isChildNode");o(zm,"tokenToRange");o(Xd,"toDocumentSegment");(function(t){t[t.Before=0]="Before",t[t.After=1]="After",t[t.OverlapFront=2]="OverlapFront",t[t.OverlapBack=3]="OverlapBack",t[t.Inside=4]="Inside"})(Mu||(Mu={}));o(Mne,"compareRange");o(AD,"inRange");fT=/^[\w\p{L}]$/u;o(hMe,"findDeclarationNodeAtOffset");o(_D,"findCommentNode");o(CD,"isCommentNode");o(LD,"findLeafNodeAtOffset");o(Ine,"findLeafNodeBeforeOffset");o(One,"binarySearch");o(Pne,"getPreviousNode");o(fMe,"getNextNode");o(dMe,"getStartlineNode");o(pMe,"getInteriorNodes");o(mMe,"getCommonParent");o(Nne,"getParentChain")});function tf(t){throw new Error("Error! The input value was not handled.")}var jd,pT=R(()=>{"use strict";jd=class extends Error{static{o(this,"ErrorWithLocation")}constructor(e,r){super(e?`${r} at ${e.range.start.line}:${e.range.start.character}`:r)}};o(tf,"assertUnreachable")});var Yv={};hr(Yv,{AbstractElement:()=>RD,AbstractRule:()=>$v,AbstractType:()=>Vv,Action:()=>aR,Alternatives:()=>sR,ArrayLiteral:()=>ND,ArrayType:()=>MD,Assignment:()=>oR,BooleanLiteral:()=>OD,CharacterRange:()=>lR,Condition:()=>mT,Conjunction:()=>BD,CrossReference:()=>uR,Disjunction:()=>zD,EndOfFile:()=>hR,Grammar:()=>$D,GrammarImport:()=>Fne,Group:()=>dR,InferredType:()=>VD,Interface:()=>UD,Keyword:()=>pR,LangiumGrammarAstReflection:()=>Gm,LangiumGrammarTerminals:()=>gMe,NamedArgument:()=>zne,NegatedToken:()=>mR,Negation:()=>HD,NumberLiteral:()=>WD,Parameter:()=>qD,ParameterReference:()=>XD,ParserRule:()=>KD,ReferenceType:()=>QD,RegexToken:()=>yR,ReturnType:()=>Gne,RuleCall:()=>xR,SimpleType:()=>eR,StringLiteral:()=>tR,TerminalAlternatives:()=>bR,TerminalGroup:()=>TR,TerminalRule:()=>yT,TerminalRuleCall:()=>ER,Type:()=>rR,TypeAttribute:()=>$ne,TypeDefinition:()=>DD,UnionType:()=>nR,UnorderedGroup:()=>CR,UntilToken:()=>SR,ValueLiteral:()=>gT,Wildcard:()=>_R,isAbstractElement:()=>Uv,isAbstractRule:()=>yMe,isAbstractType:()=>vMe,isAction:()=>Iu,isAlternatives:()=>wT,isArrayLiteral:()=>kMe,isArrayType:()=>ID,isAssignment:()=>Nl,isBooleanLiteral:()=>PD,isCharacterRange:()=>cR,isCondition:()=>xMe,isConjunction:()=>FD,isCrossReference:()=>Kd,isDisjunction:()=>GD,isEndOfFile:()=>fR,isFeatureName:()=>bMe,isGrammar:()=>EMe,isGrammarImport:()=>CMe,isGroup:()=>rf,isInferredType:()=>vT,isInterface:()=>xT,isKeyword:()=>Ho,isNamedArgument:()=>SMe,isNegatedToken:()=>gR,isNegation:()=>YD,isNumberLiteral:()=>AMe,isParameter:()=>_Me,isParameterReference:()=>jD,isParserRule:()=>Oa,isPrimitiveType:()=>Bne,isReferenceType:()=>ZD,isRegexToken:()=>vR,isReturnType:()=>JD,isRuleCall:()=>Ml,isSimpleType:()=>bT,isStringLiteral:()=>LMe,isTerminalAlternatives:()=>wR,isTerminalGroup:()=>kR,isTerminalRule:()=>Uo,isTerminalRuleCall:()=>TT,isType:()=>Hv,isTypeAttribute:()=>DMe,isTypeDefinition:()=>wMe,isUnionType:()=>iR,isUnorderedGroup:()=>kT,isUntilToken:()=>AR,isValueLiteral:()=>TMe,isWildcard:()=>LR,reflection:()=>Kt});function yMe(t){return Kt.isInstance(t,$v)}function vMe(t){return Kt.isInstance(t,Vv)}function xMe(t){return Kt.isInstance(t,mT)}function bMe(t){return Bne(t)||t==="current"||t==="entry"||t==="extends"||t==="false"||t==="fragment"||t==="grammar"||t==="hidden"||t==="import"||t==="interface"||t==="returns"||t==="terminal"||t==="true"||t==="type"||t==="infer"||t==="infers"||t==="with"||typeof t=="string"&&/\^?[_a-zA-Z][\w_]*/.test(t)}function Bne(t){return t==="string"||t==="number"||t==="boolean"||t==="Date"||t==="bigint"}function wMe(t){return Kt.isInstance(t,DD)}function TMe(t){return Kt.isInstance(t,gT)}function Uv(t){return Kt.isInstance(t,RD)}function kMe(t){return Kt.isInstance(t,ND)}function ID(t){return Kt.isInstance(t,MD)}function PD(t){return Kt.isInstance(t,OD)}function FD(t){return Kt.isInstance(t,BD)}function GD(t){return Kt.isInstance(t,zD)}function EMe(t){return Kt.isInstance(t,$D)}function CMe(t){return Kt.isInstance(t,Fne)}function vT(t){return Kt.isInstance(t,VD)}function xT(t){return Kt.isInstance(t,UD)}function SMe(t){return Kt.isInstance(t,zne)}function YD(t){return Kt.isInstance(t,HD)}function AMe(t){return Kt.isInstance(t,WD)}function _Me(t){return Kt.isInstance(t,qD)}function jD(t){return Kt.isInstance(t,XD)}function Oa(t){return Kt.isInstance(t,KD)}function ZD(t){return Kt.isInstance(t,QD)}function JD(t){return Kt.isInstance(t,Gne)}function bT(t){return Kt.isInstance(t,eR)}function LMe(t){return Kt.isInstance(t,tR)}function Uo(t){return Kt.isInstance(t,yT)}function Hv(t){return Kt.isInstance(t,rR)}function DMe(t){return Kt.isInstance(t,$ne)}function iR(t){return Kt.isInstance(t,nR)}function Iu(t){return Kt.isInstance(t,aR)}function wT(t){return Kt.isInstance(t,sR)}function Nl(t){return Kt.isInstance(t,oR)}function cR(t){return Kt.isInstance(t,lR)}function Kd(t){return Kt.isInstance(t,uR)}function fR(t){return Kt.isInstance(t,hR)}function rf(t){return Kt.isInstance(t,dR)}function Ho(t){return Kt.isInstance(t,pR)}function gR(t){return Kt.isInstance(t,mR)}function vR(t){return Kt.isInstance(t,yR)}function Ml(t){return Kt.isInstance(t,xR)}function wR(t){return Kt.isInstance(t,bR)}function kR(t){return Kt.isInstance(t,TR)}function TT(t){return Kt.isInstance(t,ER)}function kT(t){return Kt.isInstance(t,CR)}function AR(t){return Kt.isInstance(t,SR)}function LR(t){return Kt.isInstance(t,_R)}var gMe,$v,Vv,mT,DD,gT,RD,ND,MD,OD,BD,zD,$D,Fne,VD,UD,zne,HD,WD,qD,XD,KD,QD,Gne,eR,tR,yT,rR,$ne,nR,aR,sR,oR,lR,uR,hR,dR,pR,mR,yR,xR,bR,TR,ER,CR,SR,_R,Gm,Kt,Sc=R(()=>{"use strict";Vo();gMe={ID:/\^?[_a-zA-Z][\w_]*/,STRING:/"(\\.|[^"\\])*"|'(\\.|[^'\\])*'/,NUMBER:/NaN|-?((\d*\.\d+|\d+)([Ee][+-]?\d+)?|Infinity)/,RegexLiteral:/\/(?![*+?])(?:[^\r\n\[/\\]|\\.|\[(?:[^\r\n\]\\]|\\.)*\])+\/[a-z]*/,WS:/\s+/,ML_COMMENT:/\/\*[\s\S]*?\*\//,SL_COMMENT:/\/\/[^\n\r]*/},$v="AbstractRule";o(yMe,"isAbstractRule");Vv="AbstractType";o(vMe,"isAbstractType");mT="Condition";o(xMe,"isCondition");o(bMe,"isFeatureName");o(Bne,"isPrimitiveType");DD="TypeDefinition";o(wMe,"isTypeDefinition");gT="ValueLiteral";o(TMe,"isValueLiteral");RD="AbstractElement";o(Uv,"isAbstractElement");ND="ArrayLiteral";o(kMe,"isArrayLiteral");MD="ArrayType";o(ID,"isArrayType");OD="BooleanLiteral";o(PD,"isBooleanLiteral");BD="Conjunction";o(FD,"isConjunction");zD="Disjunction";o(GD,"isDisjunction");$D="Grammar";o(EMe,"isGrammar");Fne="GrammarImport";o(CMe,"isGrammarImport");VD="InferredType";o(vT,"isInferredType");UD="Interface";o(xT,"isInterface");zne="NamedArgument";o(SMe,"isNamedArgument");HD="Negation";o(YD,"isNegation");WD="NumberLiteral";o(AMe,"isNumberLiteral");qD="Parameter";o(_Me,"isParameter");XD="ParameterReference";o(jD,"isParameterReference");KD="ParserRule";o(Oa,"isParserRule");QD="ReferenceType";o(ZD,"isReferenceType");Gne="ReturnType";o(JD,"isReturnType");eR="SimpleType";o(bT,"isSimpleType");tR="StringLiteral";o(LMe,"isStringLiteral");yT="TerminalRule";o(Uo,"isTerminalRule");rR="Type";o(Hv,"isType");$ne="TypeAttribute";o(DMe,"isTypeAttribute");nR="UnionType";o(iR,"isUnionType");aR="Action";o(Iu,"isAction");sR="Alternatives";o(wT,"isAlternatives");oR="Assignment";o(Nl,"isAssignment");lR="CharacterRange";o(cR,"isCharacterRange");uR="CrossReference";o(Kd,"isCrossReference");hR="EndOfFile";o(fR,"isEndOfFile");dR="Group";o(rf,"isGroup");pR="Keyword";o(Ho,"isKeyword");mR="NegatedToken";o(gR,"isNegatedToken");yR="RegexToken";o(vR,"isRegexToken");xR="RuleCall";o(Ml,"isRuleCall");bR="TerminalAlternatives";o(wR,"isTerminalAlternatives");TR="TerminalGroup";o(kR,"isTerminalGroup");ER="TerminalRuleCall";o(TT,"isTerminalRuleCall");CR="UnorderedGroup";o(kT,"isUnorderedGroup");SR="UntilToken";o(AR,"isUntilToken");_R="Wildcard";o(LR,"isWildcard");Gm=class extends Yd{static{o(this,"LangiumGrammarAstReflection")}getAllTypes(){return["AbstractElement","AbstractRule","AbstractType","Action","Alternatives","ArrayLiteral","ArrayType","Assignment","BooleanLiteral","CharacterRange","Condition","Conjunction","CrossReference","Disjunction","EndOfFile","Grammar","GrammarImport","Group","InferredType","Interface","Keyword","NamedArgument","NegatedToken","Negation","NumberLiteral","Parameter","ParameterReference","ParserRule","ReferenceType","RegexToken","ReturnType","RuleCall","SimpleType","StringLiteral","TerminalAlternatives","TerminalGroup","TerminalRule","TerminalRuleCall","Type","TypeAttribute","TypeDefinition","UnionType","UnorderedGroup","UntilToken","ValueLiteral","Wildcard"]}computeIsSubtype(e,r){switch(e){case aR:case sR:case oR:case lR:case uR:case hR:case dR:case pR:case mR:case yR:case xR:case bR:case TR:case ER:case CR:case SR:case _R:return this.isSubtype(RD,r);case ND:case WD:case tR:return this.isSubtype(gT,r);case MD:case QD:case eR:case nR:return this.isSubtype(DD,r);case OD:return this.isSubtype(mT,r)||this.isSubtype(gT,r);case BD:case zD:case HD:case XD:return this.isSubtype(mT,r);case VD:case UD:case rR:return this.isSubtype(Vv,r);case KD:return this.isSubtype($v,r)||this.isSubtype(Vv,r);case yT:return this.isSubtype($v,r);default:return!1}}getReferenceType(e){let r=`${e.container.$type}:${e.property}`;switch(r){case"Action:type":case"CrossReference:type":case"Interface:superTypes":case"ParserRule:returnType":case"SimpleType:typeRef":return Vv;case"Grammar:hiddenTokens":case"ParserRule:hiddenTokens":case"RuleCall:rule":return $v;case"Grammar:usedGrammars":return $D;case"NamedArgument:parameter":case"ParameterReference:parameter":return qD;case"TerminalRuleCall:rule":return yT;default:throw new Error(`${r} is not a valid reference id.`)}}getTypeMetaData(e){switch(e){case"AbstractElement":return{name:"AbstractElement",properties:[{name:"cardinality"},{name:"lookahead"}]};case"ArrayLiteral":return{name:"ArrayLiteral",properties:[{name:"elements",defaultValue:[]}]};case"ArrayType":return{name:"ArrayType",properties:[{name:"elementType"}]};case"BooleanLiteral":return{name:"BooleanLiteral",properties:[{name:"true",defaultValue:!1}]};case"Conjunction":return{name:"Conjunction",properties:[{name:"left"},{name:"right"}]};case"Disjunction":return{name:"Disjunction",properties:[{name:"left"},{name:"right"}]};case"Grammar":return{name:"Grammar",properties:[{name:"definesHiddenTokens",defaultValue:!1},{name:"hiddenTokens",defaultValue:[]},{name:"imports",defaultValue:[]},{name:"interfaces",defaultValue:[]},{name:"isDeclared",defaultValue:!1},{name:"name"},{name:"rules",defaultValue:[]},{name:"types",defaultValue:[]},{name:"usedGrammars",defaultValue:[]}]};case"GrammarImport":return{name:"GrammarImport",properties:[{name:"path"}]};case"InferredType":return{name:"InferredType",properties:[{name:"name"}]};case"Interface":return{name:"Interface",properties:[{name:"attributes",defaultValue:[]},{name:"name"},{name:"superTypes",defaultValue:[]}]};case"NamedArgument":return{name:"NamedArgument",properties:[{name:"calledByName",defaultValue:!1},{name:"parameter"},{name:"value"}]};case"Negation":return{name:"Negation",properties:[{name:"value"}]};case"NumberLiteral":return{name:"NumberLiteral",properties:[{name:"value"}]};case"Parameter":return{name:"Parameter",properties:[{name:"name"}]};case"ParameterReference":return{name:"ParameterReference",properties:[{name:"parameter"}]};case"ParserRule":return{name:"ParserRule",properties:[{name:"dataType"},{name:"definesHiddenTokens",defaultValue:!1},{name:"definition"},{name:"entry",defaultValue:!1},{name:"fragment",defaultValue:!1},{name:"hiddenTokens",defaultValue:[]},{name:"inferredType"},{name:"name"},{name:"parameters",defaultValue:[]},{name:"returnType"},{name:"wildcard",defaultValue:!1}]};case"ReferenceType":return{name:"ReferenceType",properties:[{name:"referenceType"}]};case"ReturnType":return{name:"ReturnType",properties:[{name:"name"}]};case"SimpleType":return{name:"SimpleType",properties:[{name:"primitiveType"},{name:"stringType"},{name:"typeRef"}]};case"StringLiteral":return{name:"StringLiteral",properties:[{name:"value"}]};case"TerminalRule":return{name:"TerminalRule",properties:[{name:"definition"},{name:"fragment",defaultValue:!1},{name:"hidden",defaultValue:!1},{name:"name"},{name:"type"}]};case"Type":return{name:"Type",properties:[{name:"name"},{name:"type"}]};case"TypeAttribute":return{name:"TypeAttribute",properties:[{name:"defaultValue"},{name:"isOptional",defaultValue:!1},{name:"name"},{name:"type"}]};case"UnionType":return{name:"UnionType",properties:[{name:"types",defaultValue:[]}]};case"Action":return{name:"Action",properties:[{name:"cardinality"},{name:"feature"},{name:"inferredType"},{name:"lookahead"},{name:"operator"},{name:"type"}]};case"Alternatives":return{name:"Alternatives",properties:[{name:"cardinality"},{name:"elements",defaultValue:[]},{name:"lookahead"}]};case"Assignment":return{name:"Assignment",properties:[{name:"cardinality"},{name:"feature"},{name:"lookahead"},{name:"operator"},{name:"terminal"}]};case"CharacterRange":return{name:"CharacterRange",properties:[{name:"cardinality"},{name:"left"},{name:"lookahead"},{name:"right"}]};case"CrossReference":return{name:"CrossReference",properties:[{name:"cardinality"},{name:"deprecatedSyntax",defaultValue:!1},{name:"lookahead"},{name:"terminal"},{name:"type"}]};case"EndOfFile":return{name:"EndOfFile",properties:[{name:"cardinality"},{name:"lookahead"}]};case"Group":return{name:"Group",properties:[{name:"cardinality"},{name:"elements",defaultValue:[]},{name:"guardCondition"},{name:"lookahead"}]};case"Keyword":return{name:"Keyword",properties:[{name:"cardinality"},{name:"lookahead"},{name:"value"}]};case"NegatedToken":return{name:"NegatedToken",properties:[{name:"cardinality"},{name:"lookahead"},{name:"terminal"}]};case"RegexToken":return{name:"RegexToken",properties:[{name:"cardinality"},{name:"lookahead"},{name:"regex"}]};case"RuleCall":return{name:"RuleCall",properties:[{name:"arguments",defaultValue:[]},{name:"cardinality"},{name:"lookahead"},{name:"rule"}]};case"TerminalAlternatives":return{name:"TerminalAlternatives",properties:[{name:"cardinality"},{name:"elements",defaultValue:[]},{name:"lookahead"}]};case"TerminalGroup":return{name:"TerminalGroup",properties:[{name:"cardinality"},{name:"elements",defaultValue:[]},{name:"lookahead"}]};case"TerminalRuleCall":return{name:"TerminalRuleCall",properties:[{name:"cardinality"},{name:"lookahead"},{name:"rule"}]};case"UnorderedGroup":return{name:"UnorderedGroup",properties:[{name:"cardinality"},{name:"elements",defaultValue:[]},{name:"lookahead"}]};case"UntilToken":return{name:"UntilToken",properties:[{name:"cardinality"},{name:"lookahead"},{name:"terminal"}]};case"Wildcard":return{name:"Wildcard",properties:[{name:"cardinality"},{name:"lookahead"}]};default:return{name:e,properties:[]}}}},Kt=new Gm});var CT={};hr(CT,{assignMandatoryProperties:()=>NR,copyAstNode:()=>RR,findLocalReferences:()=>NMe,findRootNode:()=>Vne,getContainerOfType:()=>Qd,getDocument:()=>Oi,hasContainerOfType:()=>RMe,linkContentToContainer:()=>ET,streamAllContents:()=>Ac,streamAst:()=>Yo,streamContents:()=>Wv,streamReferences:()=>$m});function ET(t){for(let[e,r]of Object.entries(t))e.startsWith("$")||(Array.isArray(r)?r.forEach((n,i)=>{Xn(n)&&(n.$container=t,n.$containerProperty=e,n.$containerIndex=i)}):Xn(r)&&(r.$container=t,r.$containerProperty=e))}function Qd(t,e){let r=t;for(;r;){if(e(r))return r;r=r.$container}}function RMe(t,e){let r=t;for(;r;){if(e(r))return!0;r=r.$container}return!1}function Oi(t){let r=Vne(t).$document;if(!r)throw new Error("AST node has no document.");return r}function Vne(t){for(;t.$container;)t=t.$container;return t}function Wv(t,e){if(!t)throw new Error("Node must be an AstNode.");let r=e?.range;return new uo(()=>({keys:Object.keys(t),keyIndex:0,arrayIndex:0}),n=>{for(;n.keyIndexWv(r,e))}function Yo(t,e){if(t){if(e?.range&&!DR(t,e.range))return new Cc(t,()=>[])}else throw new Error("Root node must be an AstNode.");return new Cc(t,r=>Wv(r,e),{includeRoot:!0})}function DR(t,e){var r;if(!e)return!0;let n=(r=t.$cstNode)===null||r===void 0?void 0:r.range;return n?AD(n,e):!1}function $m(t){return new uo(()=>({keys:Object.keys(t),keyIndex:0,arrayIndex:0}),e=>{for(;e.keyIndex{$m(n).forEach(i=>{i.reference.ref===t&&r.push(i.reference)})}),Kr(r)}function NR(t,e){let r=t.getTypeMetaData(e.$type),n=e;for(let i of r.properties)i.defaultValue!==void 0&&n[i.name]===void 0&&(n[i.name]=Une(i.defaultValue))}function Une(t){return Array.isArray(t)?[...t.map(Une)]:t}function RR(t,e){let r={$type:t.$type};for(let[n,i]of Object.entries(t))if(!n.startsWith("$"))if(Xn(i))r[n]=RR(i,e);else if(xa(i))r[n]=e(r,n,i.$refNode,i.$refText);else if(Array.isArray(i)){let a=[];for(let s of i)Xn(s)?a.push(RR(s,e)):xa(s)?a.push(e(r,n,s.$refNode,s.$refText)):a.push(s);r[n]=a}else r[n]=i;return ET(r),r}var es=R(()=>{"use strict";Vo();Ds();Rl();o(ET,"linkContentToContainer");o(Qd,"getContainerOfType");o(RMe,"hasContainerOfType");o(Oi,"getDocument");o(Vne,"findRootNode");o(Wv,"streamContents");o(Ac,"streamAllContents");o(Yo,"streamAst");o(DR,"isAstNodeInRange");o($m,"streamReferences");o(NMe,"findLocalReferences");o(NR,"assignMandatoryProperties");o(Une,"copyDefaultValue");o(RR,"copyAstNode")});function qt(t){return t.charCodeAt(0)}function ST(t,e){Array.isArray(t)?t.forEach(function(r){e.push(r)}):e.push(t)}function Vm(t,e){if(t[e]===!0)throw"duplicate flag "+e;let r=t[e];t[e]=!0}function Zd(t){if(t===void 0)throw Error("Internal Error - Should never get here!");return!0}function qv(){throw Error("Internal Error - Should never get here!")}function MR(t){return t.type==="Character"}var IR=R(()=>{"use strict";o(qt,"cc");o(ST,"insertToSet");o(Vm,"addFlag");o(Zd,"ASSERT_EXISTS");o(qv,"ASSERT_NEVER_REACH_HERE");o(MR,"isCharacter")});var Xv,jv,OR,Hne=R(()=>{"use strict";IR();Xv=[];for(let t=qt("0");t<=qt("9");t++)Xv.push(t);jv=[qt("_")].concat(Xv);for(let t=qt("a");t<=qt("z");t++)jv.push(t);for(let t=qt("A");t<=qt("Z");t++)jv.push(t);OR=[qt(" "),qt("\f"),qt(` +`),qt("\r"),qt(" "),qt("\v"),qt(" "),qt("\xA0"),qt("\u1680"),qt("\u2000"),qt("\u2001"),qt("\u2002"),qt("\u2003"),qt("\u2004"),qt("\u2005"),qt("\u2006"),qt("\u2007"),qt("\u2008"),qt("\u2009"),qt("\u200A"),qt("\u2028"),qt("\u2029"),qt("\u202F"),qt("\u205F"),qt("\u3000"),qt("\uFEFF")]});var MMe,AT,IMe,Jd,Yne=R(()=>{"use strict";IR();Hne();MMe=/[0-9a-fA-F]/,AT=/[0-9]/,IMe=/[1-9]/,Jd=class{static{o(this,"RegExpParser")}constructor(){this.idx=0,this.input="",this.groupIdx=0}saveState(){return{idx:this.idx,input:this.input,groupIdx:this.groupIdx}}restoreState(e){this.idx=e.idx,this.input=e.input,this.groupIdx=e.groupIdx}pattern(e){this.idx=0,this.input=e,this.groupIdx=0,this.consumeChar("/");let r=this.disjunction();this.consumeChar("/");let n={type:"Flags",loc:{begin:this.idx,end:e.length},global:!1,ignoreCase:!1,multiLine:!1,unicode:!1,sticky:!1};for(;this.isRegExpFlag();)switch(this.popChar()){case"g":Vm(n,"global");break;case"i":Vm(n,"ignoreCase");break;case"m":Vm(n,"multiLine");break;case"u":Vm(n,"unicode");break;case"y":Vm(n,"sticky");break}if(this.idx!==this.input.length)throw Error("Redundant input: "+this.input.substring(this.idx));return{type:"Pattern",flags:n,value:r,loc:this.loc(0)}}disjunction(){let e=[],r=this.idx;for(e.push(this.alternative());this.peekChar()==="|";)this.consumeChar("|"),e.push(this.alternative());return{type:"Disjunction",value:e,loc:this.loc(r)}}alternative(){let e=[],r=this.idx;for(;this.isTerm();)e.push(this.term());return{type:"Alternative",value:e,loc:this.loc(r)}}term(){return this.isAssertion()?this.assertion():this.atom()}assertion(){let e=this.idx;switch(this.popChar()){case"^":return{type:"StartAnchor",loc:this.loc(e)};case"$":return{type:"EndAnchor",loc:this.loc(e)};case"\\":switch(this.popChar()){case"b":return{type:"WordBoundary",loc:this.loc(e)};case"B":return{type:"NonWordBoundary",loc:this.loc(e)}}throw Error("Invalid Assertion Escape");case"(":this.consumeChar("?");let r;switch(this.popChar()){case"=":r="Lookahead";break;case"!":r="NegativeLookahead";break}Zd(r);let n=this.disjunction();return this.consumeChar(")"),{type:r,value:n,loc:this.loc(e)}}return qv()}quantifier(e=!1){let r,n=this.idx;switch(this.popChar()){case"*":r={atLeast:0,atMost:1/0};break;case"+":r={atLeast:1,atMost:1/0};break;case"?":r={atLeast:0,atMost:1};break;case"{":let i=this.integerIncludingZero();switch(this.popChar()){case"}":r={atLeast:i,atMost:i};break;case",":let a;this.isDigit()?(a=this.integerIncludingZero(),r={atLeast:i,atMost:a}):r={atLeast:i,atMost:1/0},this.consumeChar("}");break}if(e===!0&&r===void 0)return;Zd(r);break}if(!(e===!0&&r===void 0)&&Zd(r))return this.peekChar(0)==="?"?(this.consumeChar("?"),r.greedy=!1):r.greedy=!0,r.type="Quantifier",r.loc=this.loc(n),r}atom(){let e,r=this.idx;switch(this.peekChar()){case".":e=this.dotAll();break;case"\\":e=this.atomEscape();break;case"[":e=this.characterClass();break;case"(":e=this.group();break}return e===void 0&&this.isPatternCharacter()&&(e=this.patternCharacter()),Zd(e)?(e.loc=this.loc(r),this.isQuantifier()&&(e.quantifier=this.quantifier()),e):qv()}dotAll(){return this.consumeChar("."),{type:"Set",complement:!0,value:[qt(` +`),qt("\r"),qt("\u2028"),qt("\u2029")]}}atomEscape(){switch(this.consumeChar("\\"),this.peekChar()){case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":return this.decimalEscapeAtom();case"d":case"D":case"s":case"S":case"w":case"W":return this.characterClassEscape();case"f":case"n":case"r":case"t":case"v":return this.controlEscapeAtom();case"c":return this.controlLetterEscapeAtom();case"0":return this.nulCharacterAtom();case"x":return this.hexEscapeSequenceAtom();case"u":return this.regExpUnicodeEscapeSequenceAtom();default:return this.identityEscapeAtom()}}decimalEscapeAtom(){return{type:"GroupBackReference",value:this.positiveInteger()}}characterClassEscape(){let e,r=!1;switch(this.popChar()){case"d":e=Xv;break;case"D":e=Xv,r=!0;break;case"s":e=OR;break;case"S":e=OR,r=!0;break;case"w":e=jv;break;case"W":e=jv,r=!0;break}return Zd(e)?{type:"Set",value:e,complement:r}:qv()}controlEscapeAtom(){let e;switch(this.popChar()){case"f":e=qt("\f");break;case"n":e=qt(` +`);break;case"r":e=qt("\r");break;case"t":e=qt(" ");break;case"v":e=qt("\v");break}return Zd(e)?{type:"Character",value:e}:qv()}controlLetterEscapeAtom(){this.consumeChar("c");let e=this.popChar();if(/[a-zA-Z]/.test(e)===!1)throw Error("Invalid ");return{type:"Character",value:e.toUpperCase().charCodeAt(0)-64}}nulCharacterAtom(){return this.consumeChar("0"),{type:"Character",value:qt("\0")}}hexEscapeSequenceAtom(){return this.consumeChar("x"),this.parseHexDigits(2)}regExpUnicodeEscapeSequenceAtom(){return this.consumeChar("u"),this.parseHexDigits(4)}identityEscapeAtom(){let e=this.popChar();return{type:"Character",value:qt(e)}}classPatternCharacterAtom(){switch(this.peekChar()){case` +`:case"\r":case"\u2028":case"\u2029":case"\\":case"]":throw Error("TBD");default:let e=this.popChar();return{type:"Character",value:qt(e)}}}characterClass(){let e=[],r=!1;for(this.consumeChar("["),this.peekChar(0)==="^"&&(this.consumeChar("^"),r=!0);this.isClassAtom();){let n=this.classAtom(),i=n.type==="Character";if(MR(n)&&this.isRangeDash()){this.consumeChar("-");let a=this.classAtom(),s=a.type==="Character";if(MR(a)){if(a.value=this.input.length)throw Error("Unexpected end of input");this.idx++}loc(e){return{begin:e,end:this.idx}}}});var _c,Wne=R(()=>{"use strict";_c=class{static{o(this,"BaseRegExpVisitor")}visitChildren(e){for(let r in e){let n=e[r];e.hasOwnProperty(r)&&(n.type!==void 0?this.visit(n):Array.isArray(n)&&n.forEach(i=>{this.visit(i)},this))}}visit(e){switch(e.type){case"Pattern":this.visitPattern(e);break;case"Flags":this.visitFlags(e);break;case"Disjunction":this.visitDisjunction(e);break;case"Alternative":this.visitAlternative(e);break;case"StartAnchor":this.visitStartAnchor(e);break;case"EndAnchor":this.visitEndAnchor(e);break;case"WordBoundary":this.visitWordBoundary(e);break;case"NonWordBoundary":this.visitNonWordBoundary(e);break;case"Lookahead":this.visitLookahead(e);break;case"NegativeLookahead":this.visitNegativeLookahead(e);break;case"Character":this.visitCharacter(e);break;case"Set":this.visitSet(e);break;case"Group":this.visitGroup(e);break;case"GroupBackReference":this.visitGroupBackReference(e);break;case"Quantifier":this.visitQuantifier(e);break}this.visitChildren(e)}visitPattern(e){}visitFlags(e){}visitDisjunction(e){}visitAlternative(e){}visitStartAnchor(e){}visitEndAnchor(e){}visitWordBoundary(e){}visitNonWordBoundary(e){}visitLookahead(e){}visitNegativeLookahead(e){}visitCharacter(e){}visitSet(e){}visitGroup(e){}visitGroupBackReference(e){}visitQuantifier(e){}}});var Kv=R(()=>{"use strict";Yne();Wne()});var LT={};hr(LT,{NEWLINE_REGEXP:()=>BR,escapeRegExp:()=>t0,getCaseInsensitivePattern:()=>zR,getTerminalParts:()=>OMe,isMultilineComment:()=>FR,isWhitespace:()=>_T,partialMatches:()=>GR,partialRegExp:()=>Xne});function OMe(t){try{typeof t!="string"&&(t=t.source),t=`/${t}/`;let e=qne.pattern(t),r=[];for(let n of e.value.value)e0.reset(t),e0.visit(n),r.push({start:e0.startRegexp,end:e0.endRegex});return r}catch{return[]}}function FR(t){try{return typeof t=="string"&&(t=new RegExp(t)),t=t.toString(),e0.reset(t),e0.visit(qne.pattern(t)),e0.multiline}catch{return!1}}function _T(t){return(typeof t=="string"?new RegExp(t):t).test(" ")}function t0(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function zR(t){return Array.prototype.map.call(t,e=>/\w/.test(e)?`[${e.toLowerCase()}${e.toUpperCase()}]`:t0(e)).join("")}function GR(t,e){let r=Xne(t),n=e.match(r);return!!n&&n[0].length>0}function Xne(t){typeof t=="string"&&(t=new RegExp(t));let e=t,r=t.source,n=0;function i(){let a="",s;function l(h){a+=r.substr(n,h),n+=h}o(l,"appendRaw");function u(h){a+="(?:"+r.substr(n,h)+"|$)",n+=h}for(o(u,"appendOptional");n",n)-n+1);break;default:u(2);break}break;case"[":s=/\[(?:\\.|.)*?\]/g,s.lastIndex=n,s=s.exec(r)||[],u(s[0].length);break;case"|":case"^":case"$":case"*":case"+":case"?":l(1);break;case"{":s=/\{\d+,?\d*\}/g,s.lastIndex=n,s=s.exec(r),s?l(s[0].length):u(1);break;case"(":if(r[n+1]==="?")switch(r[n+2]){case":":a+="(?:",n+=3,a+=i()+"|$)";break;case"=":a+="(?=",n+=3,a+=i()+")";break;case"!":s=n,n+=3,i(),a+=r.substr(s,n-s);break;case"<":switch(r[n+3]){case"=":case"!":s=n,n+=4,i(),a+=r.substr(s,n-s);break;default:l(r.indexOf(">",n)-n+1),a+=i()+"|$)";break}break}else l(1),a+=i()+"|$)";break;case")":return++n,a;default:u(1);break}return a}return o(i,"process"),new RegExp(i(),t.flags)}var BR,qne,PR,e0,Um=R(()=>{"use strict";Kv();BR=/\r?\n/gm,qne=new Jd,PR=class extends _c{static{o(this,"TerminalRegExpVisitor")}constructor(){super(...arguments),this.isStarting=!0,this.endRegexpStack=[],this.multiline=!1}get endRegex(){return this.endRegexpStack.join("")}reset(e){this.multiline=!1,this.regex=e,this.startRegexp="",this.isStarting=!0,this.endRegexpStack=[]}visitGroup(e){e.quantifier&&(this.isStarting=!1,this.endRegexpStack=[])}visitCharacter(e){let r=String.fromCharCode(e.value);if(!this.multiline&&r===` +`&&(this.multiline=!0),e.quantifier)this.isStarting=!1,this.endRegexpStack=[];else{let n=t0(r);this.endRegexpStack.push(n),this.isStarting&&(this.startRegexp+=n)}}visitSet(e){if(!this.multiline){let r=this.regex.substring(e.loc.begin,e.loc.end),n=new RegExp(r);this.multiline=!!` +`.match(n)}if(e.quantifier)this.isStarting=!1,this.endRegexpStack=[];else{let r=this.regex.substring(e.loc.begin,e.loc.end);this.endRegexpStack.push(r),this.isStarting&&(this.startRegexp+=r)}}visitChildren(e){e.type==="Group"&&e.quantifier||super.visitChildren(e)}},e0=new PR;o(OMe,"getTerminalParts");o(FR,"isMultilineComment");o(_T,"isWhitespace");o(t0,"escapeRegExp");o(zR,"getCaseInsensitivePattern");o(GR,"partialMatches");o(Xne,"partialRegExp")});var RT={};hr(RT,{findAssignment:()=>jR,findNameAssignment:()=>DT,findNodeForKeyword:()=>qR,findNodeForProperty:()=>Zv,findNodesForKeyword:()=>PMe,findNodesForKeywordInternal:()=>XR,findNodesForProperty:()=>YR,getActionAtElement:()=>Jne,getActionType:()=>tie,getAllReachableRules:()=>Qv,getCrossReferenceTerminal:()=>UR,getEntryRule:()=>jne,getExplicitRuleType:()=>KR,getHiddenRules:()=>Kne,getRuleType:()=>QR,getTypeName:()=>r0,isArrayCardinality:()=>FMe,isArrayOperator:()=>zMe,isCommentTerminal:()=>HR,isDataType:()=>GMe,isDataTypeRule:()=>Jv,isOptionalCardinality:()=>BMe,terminalRegex:()=>Hm});function jne(t){return t.rules.find(e=>Oa(e)&&e.entry)}function Kne(t){return t.rules.filter(e=>Uo(e)&&e.hidden)}function Qv(t,e){let r=new Set,n=jne(t);if(!n)return new Set(t.rules);let i=[n].concat(Kne(t));for(let s of i)Qne(s,r,e);let a=new Set;for(let s of t.rules)(r.has(s.name)||Uo(s)&&s.hidden)&&a.add(s);return a}function Qne(t,e,r){e.add(t.name),Ac(t).forEach(n=>{if(Ml(n)||r&&TT(n)){let i=n.rule.ref;i&&!e.has(i.name)&&Qne(i,e,r)}})}function UR(t){if(t.terminal)return t.terminal;if(t.type.ref){let e=DT(t.type.ref);return e?.terminal}}function HR(t){return t.hidden&&!Hm(t).test(" ")}function YR(t,e){return!t||!e?[]:WR(t,e,t.astNode,!0)}function Zv(t,e,r){if(!t||!e)return;let n=WR(t,e,t.astNode,!0);if(n.length!==0)return r!==void 0?r=Math.max(0,Math.min(r,n.length-1)):r=0,n[r]}function WR(t,e,r,n){if(!n){let i=Qd(t.grammarSource,Nl);if(i&&i.feature===e)return[t]}return co(t)&&t.astNode===r?t.content.flatMap(i=>WR(i,e,r,!1)):[]}function PMe(t,e){return t?XR(t,e,t?.astNode):[]}function qR(t,e,r){if(!t)return;let n=XR(t,e,t?.astNode);if(n.length!==0)return r!==void 0?r=Math.max(0,Math.min(r,n.length-1)):r=0,n[r]}function XR(t,e,r){if(t.astNode!==r)return[];if(Ho(t.grammarSource)&&t.grammarSource.value===e)return[t];let n=qd(t).iterator(),i,a=[];do if(i=n.next(),!i.done){let s=i.value;s.astNode===r?Ho(s.grammarSource)&&s.grammarSource.value===e&&a.push(s):n.prune()}while(!i.done);return a}function jR(t){var e;let r=t.astNode;for(;r===((e=t.container)===null||e===void 0?void 0:e.astNode);){let n=Qd(t.grammarSource,Nl);if(n)return n;t=t.container}}function DT(t){let e=t;return vT(e)&&(Iu(e.$container)?e=e.$container.$container:Oa(e.$container)?e=e.$container:tf(e.$container)),Zne(t,e,new Map)}function Zne(t,e,r){var n;function i(a,s){let l;return Qd(a,Nl)||(l=Zne(s,s,r)),r.set(t,l),l}if(o(i,"go"),r.has(t))return r.get(t);r.set(t,void 0);for(let a of Ac(e)){if(Nl(a)&&a.feature.toLowerCase()==="name")return r.set(t,a),a;if(Ml(a)&&Oa(a.rule.ref))return i(a,a.rule.ref);if(bT(a)&&(!((n=a.typeRef)===null||n===void 0)&&n.ref))return i(a,a.typeRef.ref)}}function Jne(t){let e=t.$container;if(rf(e)){let r=e.elements,n=r.indexOf(t);for(let i=n-1;i>=0;i--){let a=r[i];if(Iu(a))return a;{let s=Ac(r[i]).find(Iu);if(s)return s}}}if(Uv(e))return Jne(e)}function BMe(t,e){return t==="?"||t==="*"||rf(e)&&!!e.guardCondition}function FMe(t){return t==="*"||t==="+"}function zMe(t){return t==="+="}function Jv(t){return eie(t,new Set)}function eie(t,e){if(e.has(t))return!0;e.add(t);for(let r of Ac(t))if(Ml(r)){if(!r.rule.ref||Oa(r.rule.ref)&&!eie(r.rule.ref,e))return!1}else{if(Nl(r))return!1;if(Iu(r))return!1}return!!t.definition}function GMe(t){return VR(t.type,new Set)}function VR(t,e){if(e.has(t))return!0;if(e.add(t),ID(t))return!1;if(ZD(t))return!1;if(iR(t))return t.types.every(r=>VR(r,e));if(bT(t)){if(t.primitiveType!==void 0)return!0;if(t.stringType!==void 0)return!0;if(t.typeRef!==void 0){let r=t.typeRef.ref;return Hv(r)?VR(r.type,e):!1}else return!1}else return!1}function KR(t){if(t.inferredType)return t.inferredType.name;if(t.dataType)return t.dataType;if(t.returnType){let e=t.returnType.ref;if(e){if(Oa(e))return e.name;if(xT(e)||Hv(e))return e.name}}}function r0(t){var e;if(Oa(t))return Jv(t)?t.name:(e=KR(t))!==null&&e!==void 0?e:t.name;if(xT(t)||Hv(t)||JD(t))return t.name;if(Iu(t)){let r=tie(t);if(r)return r}else if(vT(t))return t.name;throw new Error("Cannot get name of Unknown Type")}function tie(t){var e;if(t.inferredType)return t.inferredType.name;if(!((e=t.type)===null||e===void 0)&&e.ref)return r0(t.type.ref)}function QR(t){var e,r,n;return Uo(t)?(r=(e=t.type)===null||e===void 0?void 0:e.name)!==null&&r!==void 0?r:"string":Jv(t)?t.name:(n=KR(t))!==null&&n!==void 0?n:t.name}function Hm(t){let e={s:!1,i:!1,u:!1},r=Ym(t.definition,e),n=Object.entries(e).filter(([,i])=>i).map(([i])=>i).join("");return new RegExp(r,n)}function Ym(t,e){if(wR(t))return $Me(t);if(kR(t))return VMe(t);if(cR(t))return YMe(t);if(TT(t)){let r=t.rule.ref;if(!r)throw new Error("Missing rule reference.");return Ou(Ym(r.definition),{cardinality:t.cardinality,lookahead:t.lookahead})}else{if(gR(t))return HMe(t);if(AR(t))return UMe(t);if(vR(t)){let r=t.regex.lastIndexOf("/"),n=t.regex.substring(1,r),i=t.regex.substring(r+1);return e&&(e.i=i.includes("i"),e.s=i.includes("s"),e.u=i.includes("u")),Ou(n,{cardinality:t.cardinality,lookahead:t.lookahead,wrap:!1})}else{if(LR(t))return Ou(ZR,{cardinality:t.cardinality,lookahead:t.lookahead});throw new Error(`Invalid terminal element: ${t?.$type}`)}}}function $Me(t){return Ou(t.elements.map(e=>Ym(e)).join("|"),{cardinality:t.cardinality,lookahead:t.lookahead})}function VMe(t){return Ou(t.elements.map(e=>Ym(e)).join(""),{cardinality:t.cardinality,lookahead:t.lookahead})}function UMe(t){return Ou(`${ZR}*?${Ym(t.terminal)}`,{cardinality:t.cardinality,lookahead:t.lookahead})}function HMe(t){return Ou(`(?!${Ym(t.terminal)})${ZR}*?`,{cardinality:t.cardinality,lookahead:t.lookahead})}function YMe(t){return t.right?Ou(`[${$R(t.left)}-${$R(t.right)}]`,{cardinality:t.cardinality,lookahead:t.lookahead,wrap:!1}):Ou($R(t.left),{cardinality:t.cardinality,lookahead:t.lookahead,wrap:!1})}function $R(t){return t0(t.value)}function Ou(t,e){var r;return(e.wrap!==!1||e.lookahead)&&(t=`(${(r=e.lookahead)!==null&&r!==void 0?r:""}${t})`),e.cardinality?`${t}${e.cardinality}`:t}var ZR,Il=R(()=>{"use strict";pT();Sc();Vo();es();Rl();Um();o(jne,"getEntryRule");o(Kne,"getHiddenRules");o(Qv,"getAllReachableRules");o(Qne,"ruleDfs");o(UR,"getCrossReferenceTerminal");o(HR,"isCommentTerminal");o(YR,"findNodesForProperty");o(Zv,"findNodeForProperty");o(WR,"findNodesForPropertyInternal");o(PMe,"findNodesForKeyword");o(qR,"findNodeForKeyword");o(XR,"findNodesForKeywordInternal");o(jR,"findAssignment");o(DT,"findNameAssignment");o(Zne,"findNameAssignmentInternal");o(Jne,"getActionAtElement");o(BMe,"isOptionalCardinality");o(FMe,"isArrayCardinality");o(zMe,"isArrayOperator");o(Jv,"isDataTypeRule");o(eie,"isDataTypeRuleInternal");o(GMe,"isDataType");o(VR,"isDataTypeInternal");o(KR,"getExplicitRuleType");o(r0,"getTypeName");o(tie,"getActionType");o(QR,"getRuleType");o(Hm,"terminalRegex");ZR=/[\s\S]/.source;o(Ym,"abstractElementToRegex");o($Me,"terminalAlternativesToRegex");o(VMe,"terminalGroupToRegex");o(UMe,"untilTokenToRegex");o(HMe,"negateTokenToRegex");o(YMe,"characterRangeToRegex");o($R,"keywordToRegex");o(Ou,"withCardinality")});function JR(t){let e=[],r=t.Grammar;for(let n of r.rules)Uo(n)&&HR(n)&&FR(Hm(n))&&e.push(n.name);return{multilineCommentRules:e,nameRegexp:fT}}var eN=R(()=>{"use strict";Rl();Il();Um();Sc();o(JR,"createGrammarConfig")});var tN=R(()=>{"use strict"});function Wm(t){console&&console.error&&console.error(`Error: ${t}`)}function e2(t){console&&console.warn&&console.warn(`Warning: ${t}`)}var rie=R(()=>{"use strict";o(Wm,"PRINT_ERROR");o(e2,"PRINT_WARNING")});function t2(t){let e=new Date().getTime(),r=t();return{time:new Date().getTime()-e,value:r}}var nie=R(()=>{"use strict";o(t2,"timer")});function r2(t){function e(){}o(e,"FakeConstructor"),e.prototype=t;let r=new e;function n(){return typeof r.bar}return o(n,"fakeAccess"),n(),n(),t;(0,eval)(t)}var iie=R(()=>{"use strict";o(r2,"toFastProperties")});var qm=R(()=>{"use strict";rie();nie();iie()});function WMe(t){return qMe(t)?t.LABEL:t.name}function qMe(t){return di(t.LABEL)&&t.LABEL!==""}function NT(t){return qe(t,Xm)}function Xm(t){function e(r){return qe(r,Xm)}if(o(e,"convertDefinition"),t instanceof Zr){let r={type:"NonTerminal",name:t.nonTerminalName,idx:t.idx};return di(t.label)&&(r.label=t.label),r}else{if(t instanceof Sn)return{type:"Alternative",definition:e(t.definition)};if(t instanceof Jr)return{type:"Option",idx:t.idx,definition:e(t.definition)};if(t instanceof An)return{type:"RepetitionMandatory",idx:t.idx,definition:e(t.definition)};if(t instanceof _n)return{type:"RepetitionMandatoryWithSeparator",idx:t.idx,separator:Xm(new fr({terminalType:t.separator})),definition:e(t.definition)};if(t instanceof mn)return{type:"RepetitionWithSeparator",idx:t.idx,separator:Xm(new fr({terminalType:t.separator})),definition:e(t.definition)};if(t instanceof br)return{type:"Repetition",idx:t.idx,definition:e(t.definition)};if(t instanceof gn)return{type:"Alternation",idx:t.idx,definition:e(t.definition)};if(t instanceof fr){let r={type:"Terminal",name:t.terminalType.name,label:WMe(t.terminalType),idx:t.idx};di(t.label)&&(r.terminalLabel=t.label);let n=t.terminalType.PATTERN;return t.terminalType.PATTERN&&(r.pattern=zo(n)?n.source:n),r}else{if(t instanceof ts)return{type:"Rule",name:t.name,orgText:t.orgText,definition:e(t.definition)};throw Error("non exhaustive match")}}}var ho,Zr,ts,Sn,Jr,An,_n,br,mn,gn,fr,MT=R(()=>{"use strict";Pt();o(WMe,"tokenLabel");o(qMe,"hasTokenLabel");ho=class{static{o(this,"AbstractProduction")}get definition(){return this._definition}set definition(e){this._definition=e}constructor(e){this._definition=e}accept(e){e.visit(this),Ee(this.definition,r=>{r.accept(e)})}},Zr=class extends ho{static{o(this,"NonTerminal")}constructor(e){super([]),this.idx=1,pa(this,Ls(e,r=>r!==void 0))}set definition(e){}get definition(){return this.referencedRule!==void 0?this.referencedRule.definition:[]}accept(e){e.visit(this)}},ts=class extends ho{static{o(this,"Rule")}constructor(e){super(e.definition),this.orgText="",pa(this,Ls(e,r=>r!==void 0))}},Sn=class extends ho{static{o(this,"Alternative")}constructor(e){super(e.definition),this.ignoreAmbiguities=!1,pa(this,Ls(e,r=>r!==void 0))}},Jr=class extends ho{static{o(this,"Option")}constructor(e){super(e.definition),this.idx=1,pa(this,Ls(e,r=>r!==void 0))}},An=class extends ho{static{o(this,"RepetitionMandatory")}constructor(e){super(e.definition),this.idx=1,pa(this,Ls(e,r=>r!==void 0))}},_n=class extends ho{static{o(this,"RepetitionMandatoryWithSeparator")}constructor(e){super(e.definition),this.idx=1,pa(this,Ls(e,r=>r!==void 0))}},br=class extends ho{static{o(this,"Repetition")}constructor(e){super(e.definition),this.idx=1,pa(this,Ls(e,r=>r!==void 0))}},mn=class extends ho{static{o(this,"RepetitionWithSeparator")}constructor(e){super(e.definition),this.idx=1,pa(this,Ls(e,r=>r!==void 0))}},gn=class extends ho{static{o(this,"Alternation")}get definition(){return this._definition}set definition(e){this._definition=e}constructor(e){super(e.definition),this.idx=1,this.ignoreAmbiguities=!1,this.hasPredicates=!1,pa(this,Ls(e,r=>r!==void 0))}},fr=class{static{o(this,"Terminal")}constructor(e){this.idx=1,pa(this,Ls(e,r=>r!==void 0))}accept(e){e.visit(this)}};o(NT,"serializeGrammar");o(Xm,"serializeProduction")});var rs,aie=R(()=>{"use strict";MT();rs=class{static{o(this,"GAstVisitor")}visit(e){let r=e;switch(r.constructor){case Zr:return this.visitNonTerminal(r);case Sn:return this.visitAlternative(r);case Jr:return this.visitOption(r);case An:return this.visitRepetitionMandatory(r);case _n:return this.visitRepetitionMandatoryWithSeparator(r);case mn:return this.visitRepetitionWithSeparator(r);case br:return this.visitRepetition(r);case gn:return this.visitAlternation(r);case fr:return this.visitTerminal(r);case ts:return this.visitRule(r);default:throw Error("non exhaustive match")}}visitNonTerminal(e){}visitAlternative(e){}visitOption(e){}visitRepetition(e){}visitRepetitionMandatory(e){}visitRepetitionMandatoryWithSeparator(e){}visitRepetitionWithSeparator(e){}visitAlternation(e){}visitTerminal(e){}visitRule(e){}}});function rN(t){return t instanceof Sn||t instanceof Jr||t instanceof br||t instanceof An||t instanceof _n||t instanceof mn||t instanceof fr||t instanceof ts}function n0(t,e=[]){return t instanceof Jr||t instanceof br||t instanceof mn?!0:t instanceof gn?Nv(t.definition,n=>n0(n,e)):t instanceof Zr&&Fn(e,t)?!1:t instanceof ho?(t instanceof Zr&&e.push(t),Ia(t.definition,n=>n0(n,e))):!1}function nN(t){return t instanceof gn}function Rs(t){if(t instanceof Zr)return"SUBRULE";if(t instanceof Jr)return"OPTION";if(t instanceof gn)return"OR";if(t instanceof An)return"AT_LEAST_ONE";if(t instanceof _n)return"AT_LEAST_ONE_SEP";if(t instanceof mn)return"MANY_SEP";if(t instanceof br)return"MANY";if(t instanceof fr)return"CONSUME";throw Error("non exhaustive match")}var sie=R(()=>{"use strict";Pt();MT();o(rN,"isSequenceProd");o(n0,"isOptionalProd");o(nN,"isBranchingProd");o(Rs,"getProductionDslName")});var ns=R(()=>{"use strict";MT();aie();sie()});function oie(t,e,r){return[new Jr({definition:[new fr({terminalType:t.separator})].concat(t.definition)})].concat(e,r)}var Pu,IT=R(()=>{"use strict";Pt();ns();Pu=class{static{o(this,"RestWalker")}walk(e,r=[]){Ee(e.definition,(n,i)=>{let a=fi(e.definition,i+1);if(n instanceof Zr)this.walkProdRef(n,a,r);else if(n instanceof fr)this.walkTerminal(n,a,r);else if(n instanceof Sn)this.walkFlat(n,a,r);else if(n instanceof Jr)this.walkOption(n,a,r);else if(n instanceof An)this.walkAtLeastOne(n,a,r);else if(n instanceof _n)this.walkAtLeastOneSep(n,a,r);else if(n instanceof mn)this.walkManySep(n,a,r);else if(n instanceof br)this.walkMany(n,a,r);else if(n instanceof gn)this.walkOr(n,a,r);else throw Error("non exhaustive match")})}walkTerminal(e,r,n){}walkProdRef(e,r,n){}walkFlat(e,r,n){let i=r.concat(n);this.walk(e,i)}walkOption(e,r,n){let i=r.concat(n);this.walk(e,i)}walkAtLeastOne(e,r,n){let i=[new Jr({definition:e.definition})].concat(r,n);this.walk(e,i)}walkAtLeastOneSep(e,r,n){let i=oie(e,r,n);this.walk(e,i)}walkMany(e,r,n){let i=[new Jr({definition:e.definition})].concat(r,n);this.walk(e,i)}walkManySep(e,r,n){let i=oie(e,r,n);this.walk(e,i)}walkOr(e,r,n){let i=r.concat(n);Ee(e.definition,a=>{let s=new Sn({definition:[a]});this.walk(s,i)})}};o(oie,"restForRepetitionWithSeparator")});function i0(t){if(t instanceof Zr)return i0(t.referencedRule);if(t instanceof fr)return KMe(t);if(rN(t))return XMe(t);if(nN(t))return jMe(t);throw Error("non exhaustive match")}function XMe(t){let e=[],r=t.definition,n=0,i=r.length>n,a,s=!0;for(;i&&s;)a=r[n],s=n0(a),e=e.concat(i0(a)),n=n+1,i=r.length>n;return Pm(e)}function jMe(t){let e=qe(t.definition,r=>i0(r));return Pm(Gr(e))}function KMe(t){return[t.terminalType]}var iN=R(()=>{"use strict";Pt();ns();o(i0,"first");o(XMe,"firstForSequence");o(jMe,"firstForBranching");o(KMe,"firstForTerminal")});var OT,aN=R(()=>{"use strict";OT="_~IN~_"});function lie(t){let e={};return Ee(t,r=>{let n=new sN(r).startWalking();pa(e,n)}),e}function QMe(t,e){return t.name+e+OT}var sN,cie=R(()=>{"use strict";IT();iN();Pt();aN();ns();sN=class extends Pu{static{o(this,"ResyncFollowsWalker")}constructor(e){super(),this.topProd=e,this.follows={}}startWalking(){return this.walk(this.topProd),this.follows}walkTerminal(e,r,n){}walkProdRef(e,r,n){let i=QMe(e.referencedRule,e.idx)+this.topProd.name,a=r.concat(n),s=new Sn({definition:a}),l=i0(s);this.follows[i]=l}};o(lie,"computeAllProdsFollows");o(QMe,"buildBetweenProdsFollowPrefix")});function jm(t){let e=t.toString();if(PT.hasOwnProperty(e))return PT[e];{let r=ZMe.pattern(e);return PT[e]=r,r}}function uie(){PT={}}var PT,ZMe,BT=R(()=>{"use strict";Kv();PT={},ZMe=new Jd;o(jm,"getRegExpAst");o(uie,"clearRegExpParserCache")});function die(t,e=!1){try{let r=jm(t);return oN(r.value,{},r.flags.ignoreCase)}catch(r){if(r.message===fie)e&&e2(`${n2} Unable to optimize: < ${t.toString()} > + Complement Sets cannot be automatically optimized. + This will disable the lexer's first char optimizations. + See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#COMPLEMENT for details.`);else{let n="";e&&(n=` + This will disable the lexer's first char optimizations. + See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#REGEXP_PARSING for details.`),Wm(`${n2} + Failed parsing: < ${t.toString()} > + Using the @chevrotain/regexp-to-ast library + Please open an issue at: https://github.com/chevrotain/chevrotain/issues`+n)}}return[]}function oN(t,e,r){switch(t.type){case"Disjunction":for(let i=0;i{if(typeof u=="number")FT(u,e,r);else{let h=u;if(r===!0)for(let f=h.from;f<=h.to;f++)FT(f,e,r);else{for(let f=h.from;f<=h.to&&f=Km){let f=h.from>=Km?h.from:Km,d=h.to,p=Lc(f),m=Lc(d);for(let g=p;g<=m;g++)e[g]=g}}}});break;case"Group":oN(s.value,e,r);break;default:throw Error("Non Exhaustive Match")}let l=s.quantifier!==void 0&&s.quantifier.atLeast===0;if(s.type==="Group"&&lN(s)===!1||s.type!=="Group"&&l===!1)break}break;default:throw Error("non exhaustive match!")}return or(e)}function FT(t,e,r){let n=Lc(t);e[n]=n,r===!0&&JMe(t,e)}function JMe(t,e){let r=String.fromCharCode(t),n=r.toUpperCase();if(n!==r){let i=Lc(n.charCodeAt(0));e[i]=i}else{let i=r.toLowerCase();if(i!==r){let a=Lc(i.charCodeAt(0));e[a]=a}}}function hie(t,e){return Za(t.value,r=>{if(typeof r=="number")return Fn(e,r);{let n=r;return Za(e,i=>n.from<=i&&i<=n.to)!==void 0}})}function lN(t){let e=t.quantifier;return e&&e.atLeast===0?!0:t.value?wt(t.value)?Ia(t.value,lN):lN(t.value):!1}function zT(t,e){if(e instanceof RegExp){let r=jm(e),n=new cN(t);return n.visit(r),n.found}else return Za(e,r=>Fn(t,r.charCodeAt(0)))!==void 0}var fie,n2,cN,pie=R(()=>{"use strict";Kv();Pt();qm();BT();uN();fie="Complement Sets are not supported for first char optimization",n2=`Unable to use "first char" lexer optimizations: +`;o(die,"getOptimizedStartCodesIndices");o(oN,"firstCharOptimizedIndices");o(FT,"addOptimizedIdxToResult");o(JMe,"handleIgnoreCase");o(hie,"findCode");o(lN,"isWholeOptional");cN=class extends _c{static{o(this,"CharCodeFinder")}constructor(e){super(),this.targetCharCodes=e,this.found=!1}visitChildren(e){if(this.found!==!0){switch(e.type){case"Lookahead":this.visitLookahead(e);return;case"NegativeLookahead":this.visitNegativeLookahead(e);return}super.visitChildren(e)}}visitCharacter(e){Fn(this.targetCharCodes,e.value)&&(this.found=!0)}visitSet(e){e.complement?hie(e,this.targetCharCodes)===void 0&&(this.found=!0):hie(e,this.targetCharCodes)!==void 0&&(this.found=!0)}};o(zT,"canMatchCharCode")});function yie(t,e){e=Xh(e,{useSticky:fN,debug:!1,safeMode:!1,positionTracking:"full",lineTerminatorCharacters:["\r",` +`],tracer:o((b,w)=>w(),"tracer")});let r=e.tracer;r("initCharCodeToOptimizedIndexMap",()=>{yIe()});let n;r("Reject Lexer.NA",()=>{n=Kh(t,b=>b[a0]===ni.NA)});let i=!1,a;r("Transform Patterns",()=>{i=!1,a=qe(n,b=>{let w=b[a0];if(zo(w)){let S=w.source;return S.length===1&&S!=="^"&&S!=="$"&&S!=="."&&!w.ignoreCase?S:S.length===2&&S[0]==="\\"&&!Fn(["d","D","s","S","t","r","n","t","0","c","b","B","f","v","w","W"],S[1])?S[1]:e.useSticky?gie(w):mie(w)}else{if(wi(w))return i=!0,{exec:w};if(typeof w=="object")return i=!0,w;if(typeof w=="string"){if(w.length===1)return w;{let S=w.replace(/[\\^$.*+?()[\]{}|]/g,"\\$&"),T=new RegExp(S);return e.useSticky?gie(T):mie(T)}}else throw Error("non exhaustive match")}})});let s,l,u,h,f;r("misc mapping",()=>{s=qe(n,b=>b.tokenTypeIdx),l=qe(n,b=>{let w=b.GROUP;if(w!==ni.SKIPPED){if(di(w))return w;if(er(w))return!1;throw Error("non exhaustive match")}}),u=qe(n,b=>{let w=b.LONGER_ALT;if(w)return wt(w)?qe(w,T=>Yw(n,T)):[Yw(n,w)]}),h=qe(n,b=>b.PUSH_MODE),f=qe(n,b=>Xe(b,"POP_MODE"))});let d;r("Line Terminator Handling",()=>{let b=Cie(e.lineTerminatorCharacters);d=qe(n,w=>!1),e.positionTracking!=="onlyOffset"&&(d=qe(n,w=>Xe(w,"LINE_BREAKS")?!!w.LINE_BREAKS:Eie(w,b)===!1&&zT(b,w.PATTERN)))});let p,m,g,y;r("Misc Mapping #2",()=>{p=qe(n,Tie),m=qe(a,mIe),g=Vr(n,(b,w)=>{let S=w.GROUP;return di(S)&&S!==ni.SKIPPED&&(b[S]=[]),b},{}),y=qe(a,(b,w)=>({pattern:a[w],longerAlt:u[w],canLineTerminator:d[w],isCustom:p[w],short:m[w],group:l[w],push:h[w],pop:f[w],tokenTypeIdx:s[w],tokenType:n[w]}))});let v=!0,x=[];return e.safeMode||r("First Char Optimization",()=>{x=Vr(n,(b,w,S)=>{if(typeof w.PATTERN=="string"){let T=w.PATTERN.charCodeAt(0),E=Lc(T);hN(b,E,y[S])}else if(wt(w.START_CHARS_HINT)){let T;Ee(w.START_CHARS_HINT,E=>{let _=typeof E=="string"?E.charCodeAt(0):E,A=Lc(_);T!==A&&(T=A,hN(b,A,y[S]))})}else if(zo(w.PATTERN))if(w.PATTERN.unicode)v=!1,e.ensureOptimizations&&Wm(`${n2} Unable to analyze < ${w.PATTERN.toString()} > pattern. + The regexp unicode flag is not currently supported by the regexp-to-ast library. + This will disable the lexer's first char optimizations. + For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#UNICODE_OPTIMIZE`);else{let T=die(w.PATTERN,e.ensureOptimizations);Qt(T)&&(v=!1),Ee(T,E=>{hN(b,E,y[S])})}else e.ensureOptimizations&&Wm(`${n2} TokenType: <${w.name}> is using a custom token pattern without providing parameter. + This will disable the lexer's first char optimizations. + For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#CUSTOM_OPTIMIZE`),v=!1;return b},[])}),{emptyGroups:g,patternIdxToConfig:y,charCodeToPatternIdxToConfig:x,hasCustom:i,canBeOptimized:v}}function vie(t,e){let r=[],n=tIe(t);r=r.concat(n.errors);let i=rIe(n.valid),a=i.valid;return r=r.concat(i.errors),r=r.concat(eIe(a)),r=r.concat(uIe(a)),r=r.concat(hIe(a,e)),r=r.concat(fIe(a)),r}function eIe(t){let e=[],r=$r(t,n=>zo(n[a0]));return e=e.concat(iIe(r)),e=e.concat(oIe(r)),e=e.concat(lIe(r)),e=e.concat(cIe(r)),e=e.concat(aIe(r)),e}function tIe(t){let e=$r(t,i=>!Xe(i,a0)),r=qe(e,i=>({message:"Token Type: ->"+i.name+"<- missing static 'PATTERN' property",type:Gn.MISSING_PATTERN,tokenTypes:[i]})),n=jh(t,e);return{errors:r,valid:n}}function rIe(t){let e=$r(t,i=>{let a=i[a0];return!zo(a)&&!wi(a)&&!Xe(a,"exec")&&!di(a)}),r=qe(e,i=>({message:"Token Type: ->"+i.name+"<- static 'PATTERN' can only be a RegExp, a Function matching the {CustomPatternMatcherFunc} type or an Object matching the {ICustomPattern} interface.",type:Gn.INVALID_PATTERN,tokenTypes:[i]})),n=jh(t,e);return{errors:r,valid:n}}function iIe(t){class e extends _c{static{o(this,"EndAnchorFinder")}constructor(){super(...arguments),this.found=!1}visitEndAnchor(a){this.found=!0}}let r=$r(t,i=>{let a=i.PATTERN;try{let s=jm(a),l=new e;return l.visit(s),l.found}catch{return nIe.test(a.source)}});return qe(r,i=>({message:`Unexpected RegExp Anchor Error: + Token Type: ->`+i.name+`<- static 'PATTERN' cannot contain end of input anchor '$' + See chevrotain.io/docs/guide/resolving_lexer_errors.html#ANCHORS for details.`,type:Gn.EOI_ANCHOR_FOUND,tokenTypes:[i]}))}function aIe(t){let e=$r(t,n=>n.PATTERN.test(""));return qe(e,n=>({message:"Token Type: ->"+n.name+"<- static 'PATTERN' must not match an empty string",type:Gn.EMPTY_MATCH_PATTERN,tokenTypes:[n]}))}function oIe(t){class e extends _c{static{o(this,"StartAnchorFinder")}constructor(){super(...arguments),this.found=!1}visitStartAnchor(a){this.found=!0}}let r=$r(t,i=>{let a=i.PATTERN;try{let s=jm(a),l=new e;return l.visit(s),l.found}catch{return sIe.test(a.source)}});return qe(r,i=>({message:`Unexpected RegExp Anchor Error: + Token Type: ->`+i.name+`<- static 'PATTERN' cannot contain start of input anchor '^' + See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#ANCHORS for details.`,type:Gn.SOI_ANCHOR_FOUND,tokenTypes:[i]}))}function lIe(t){let e=$r(t,n=>{let i=n[a0];return i instanceof RegExp&&(i.multiline||i.global)});return qe(e,n=>({message:"Token Type: ->"+n.name+"<- static 'PATTERN' may NOT contain global('g') or multiline('m')",type:Gn.UNSUPPORTED_FLAGS_FOUND,tokenTypes:[n]}))}function cIe(t){let e=[],r=qe(t,a=>Vr(t,(s,l)=>(a.PATTERN.source===l.PATTERN.source&&!Fn(e,l)&&l.PATTERN!==ni.NA&&(e.push(l),s.push(l)),s),[]));r=wc(r);let n=$r(r,a=>a.length>1);return qe(n,a=>{let s=qe(a,u=>u.name);return{message:`The same RegExp pattern ->${na(a).PATTERN}<-has been used in all of the following Token Types: ${s.join(", ")} <-`,type:Gn.DUPLICATE_PATTERNS_FOUND,tokenTypes:a}})}function uIe(t){let e=$r(t,n=>{if(!Xe(n,"GROUP"))return!1;let i=n.GROUP;return i!==ni.SKIPPED&&i!==ni.NA&&!di(i)});return qe(e,n=>({message:"Token Type: ->"+n.name+"<- static 'GROUP' can only be Lexer.SKIPPED/Lexer.NA/A String",type:Gn.INVALID_GROUP_TYPE_FOUND,tokenTypes:[n]}))}function hIe(t,e){let r=$r(t,i=>i.PUSH_MODE!==void 0&&!Fn(e,i.PUSH_MODE));return qe(r,i=>({message:`Token Type: ->${i.name}<- static 'PUSH_MODE' value cannot refer to a Lexer Mode ->${i.PUSH_MODE}<-which does not exist`,type:Gn.PUSH_MODE_DOES_NOT_EXIST,tokenTypes:[i]}))}function fIe(t){let e=[],r=Vr(t,(n,i,a)=>{let s=i.PATTERN;return s===ni.NA||(di(s)?n.push({str:s,idx:a,tokenType:i}):zo(s)&&pIe(s)&&n.push({str:s.source,idx:a,tokenType:i})),n},[]);return Ee(t,(n,i)=>{Ee(r,({str:a,idx:s,tokenType:l})=>{if(i${l.name}<- can never be matched. +Because it appears AFTER the Token Type ->${n.name}<-in the lexer's definition. +See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#UNREACHABLE`;e.push({message:u,type:Gn.UNREACHABLE_PATTERN,tokenTypes:[n,l]})}})}),e}function dIe(t,e){if(zo(e)){let r=e.exec(t);return r!==null&&r.index===0}else{if(wi(e))return e(t,0,[],{});if(Xe(e,"exec"))return e.exec(t,0,[],{});if(typeof e=="string")return e===t;throw Error("non exhaustive match")}}function pIe(t){return Za([".","\\","[","]","|","^","$","(",")","?","*","+","{"],r=>t.source.indexOf(r)!==-1)===void 0}function mie(t){let e=t.ignoreCase?"i":"";return new RegExp(`^(?:${t.source})`,e)}function gie(t){let e=t.ignoreCase?"iy":"y";return new RegExp(`${t.source}`,e)}function xie(t,e,r){let n=[];return Xe(t,Qm)||n.push({message:"A MultiMode Lexer cannot be initialized without a <"+Qm+`> property in its definition +`,type:Gn.MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE}),Xe(t,GT)||n.push({message:"A MultiMode Lexer cannot be initialized without a <"+GT+`> property in its definition +`,type:Gn.MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY}),Xe(t,GT)&&Xe(t,Qm)&&!Xe(t.modes,t.defaultMode)&&n.push({message:`A MultiMode Lexer cannot be initialized with a ${Qm}: <${t.defaultMode}>which does not exist +`,type:Gn.MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST}),Xe(t,GT)&&Ee(t.modes,(i,a)=>{Ee(i,(s,l)=>{if(er(s))n.push({message:`A Lexer cannot be initialized using an undefined Token Type. Mode:<${a}> at index: <${l}> +`,type:Gn.LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED});else if(Xe(s,"LONGER_ALT")){let u=wt(s.LONGER_ALT)?s.LONGER_ALT:[s.LONGER_ALT];Ee(u,h=>{!er(h)&&!Fn(i,h)&&n.push({message:`A MultiMode Lexer cannot be initialized with a longer_alt <${h.name}> on token <${s.name}> outside of mode <${a}> +`,type:Gn.MULTI_MODE_LEXER_LONGER_ALT_NOT_IN_CURRENT_MODE})})}})}),n}function bie(t,e,r){let n=[],i=!1,a=wc(Gr(or(t.modes))),s=Kh(a,u=>u[a0]===ni.NA),l=Cie(r);return e&&Ee(s,u=>{let h=Eie(u,l);if(h!==!1){let d={message:gIe(u,h),type:h.issue,tokenType:u};n.push(d)}else Xe(u,"LINE_BREAKS")?u.LINE_BREAKS===!0&&(i=!0):zT(l,u.PATTERN)&&(i=!0)}),e&&!i&&n.push({message:`Warning: No LINE_BREAKS Found. + This Lexer has been defined to track line and column information, + But none of the Token Types can be identified as matching a line terminator. + See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#LINE_BREAKS + for details.`,type:Gn.NO_LINE_BREAKS_FLAGS}),n}function wie(t){let e={},r=Dr(t);return Ee(r,n=>{let i=t[n];if(wt(i))e[n]=[];else throw Error("non exhaustive match")}),e}function Tie(t){let e=t.PATTERN;if(zo(e))return!1;if(wi(e))return!0;if(Xe(e,"exec"))return!0;if(di(e))return!1;throw Error("non exhaustive match")}function mIe(t){return di(t)&&t.length===1?t.charCodeAt(0):!1}function Eie(t,e){if(Xe(t,"LINE_BREAKS"))return!1;if(zo(t.PATTERN)){try{zT(e,t.PATTERN)}catch(r){return{issue:Gn.IDENTIFY_TERMINATOR,errMsg:r.message}}return!1}else{if(di(t.PATTERN))return!1;if(Tie(t))return{issue:Gn.CUSTOM_LINE_BREAK};throw Error("non exhaustive match")}}function gIe(t,e){if(e.issue===Gn.IDENTIFY_TERMINATOR)return`Warning: unable to identify line terminator usage in pattern. + The problem is in the <${t.name}> Token Type + Root cause: ${e.errMsg}. + For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#IDENTIFY_TERMINATOR`;if(e.issue===Gn.CUSTOM_LINE_BREAK)return`Warning: A Custom Token Pattern should specify the option. + The problem is in the <${t.name}> Token Type + For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#CUSTOM_LINE_BREAK`;throw Error("non exhaustive match")}function Cie(t){return qe(t,r=>di(r)?r.charCodeAt(0):r)}function hN(t,e,r){t[e]===void 0?t[e]=[r]:t[e].push(r)}function Lc(t){return t255?255+~~(t/255):t}}var a0,Qm,GT,fN,nIe,sIe,kie,Km,$T,uN=R(()=>{"use strict";Kv();i2();Pt();qm();pie();BT();a0="PATTERN",Qm="defaultMode",GT="modes",fN=typeof new RegExp("(?:)").sticky=="boolean";o(yie,"analyzeTokenTypes");o(vie,"validatePatterns");o(eIe,"validateRegExpPattern");o(tIe,"findMissingPatterns");o(rIe,"findInvalidPatterns");nIe=/[^\\][$]/;o(iIe,"findEndOfInputAnchor");o(aIe,"findEmptyMatchRegExps");sIe=/[^\\[][\^]|^\^/;o(oIe,"findStartOfInputAnchor");o(lIe,"findUnsupportedFlags");o(cIe,"findDuplicatePatterns");o(uIe,"findInvalidGroupType");o(hIe,"findModesThatDoNotExist");o(fIe,"findUnreachablePatterns");o(dIe,"testTokenType");o(pIe,"noMetaChar");o(mie,"addStartOfInput");o(gie,"addStickyFlag");o(xie,"performRuntimeChecks");o(bie,"performWarningRuntimeChecks");o(wie,"cloneEmptyGroups");o(Tie,"isCustomPattern");o(mIe,"isShortPattern");kie={test:o(function(t){let e=t.length;for(let r=this.lastIndex;r{r.isParent=r.categoryMatches.length>0})}function vIe(t){let e=Qr(t),r=t,n=!0;for(;n;){r=wc(Gr(qe(r,a=>a.CATEGORIES)));let i=jh(r,e);e=e.concat(i),Qt(i)?n=!1:r=i}return e}function xIe(t){Ee(t,e=>{dN(e)||(_ie[Sie]=e,e.tokenTypeIdx=Sie++),Aie(e)&&!wt(e.CATEGORIES)&&(e.CATEGORIES=[e.CATEGORIES]),Aie(e)||(e.CATEGORIES=[]),TIe(e)||(e.categoryMatches=[]),kIe(e)||(e.categoryMatchesMap={})})}function bIe(t){Ee(t,e=>{e.categoryMatches=[],Ee(e.categoryMatchesMap,(r,n)=>{e.categoryMatches.push(_ie[n].tokenTypeIdx)})})}function wIe(t){Ee(t,e=>{Lie([],e)})}function Lie(t,e){Ee(t,r=>{e.categoryMatchesMap[r.tokenTypeIdx]=!0}),Ee(e.CATEGORIES,r=>{let n=t.concat(e);Fn(n,r)||Lie(n,r)})}function dN(t){return Xe(t,"tokenTypeIdx")}function Aie(t){return Xe(t,"CATEGORIES")}function TIe(t){return Xe(t,"categoryMatches")}function kIe(t){return Xe(t,"categoryMatchesMap")}function Die(t){return Xe(t,"tokenTypeIdx")}var Sie,_ie,s0=R(()=>{"use strict";Pt();o(Bu,"tokenStructuredMatcher");o(Zm,"tokenStructuredMatcherNoCategories");Sie=1,_ie={};o(Fu,"augmentTokenTypes");o(vIe,"expandCategories");o(xIe,"assignTokenDefaultProps");o(bIe,"assignCategoriesTokensProp");o(wIe,"assignCategoriesMapProp");o(Lie,"singleAssignCategoriesToksMap");o(dN,"hasShortKeyProperty");o(Aie,"hasCategoriesProperty");o(TIe,"hasExtendingTokensTypesProperty");o(kIe,"hasExtendingTokensTypesMapProperty");o(Die,"isTokenType")});var pN,mN=R(()=>{"use strict";pN={buildUnableToPopLexerModeMessage(t){return`Unable to pop Lexer Mode after encountering Token ->${t.image}<- The Mode Stack is empty`},buildUnexpectedCharactersMessage(t,e,r,n,i){return`unexpected character: ->${t.charAt(e)}<- at offset: ${e}, skipped ${r} characters.`}}});var Gn,a2,ni,i2=R(()=>{"use strict";uN();Pt();qm();s0();mN();BT();(function(t){t[t.MISSING_PATTERN=0]="MISSING_PATTERN",t[t.INVALID_PATTERN=1]="INVALID_PATTERN",t[t.EOI_ANCHOR_FOUND=2]="EOI_ANCHOR_FOUND",t[t.UNSUPPORTED_FLAGS_FOUND=3]="UNSUPPORTED_FLAGS_FOUND",t[t.DUPLICATE_PATTERNS_FOUND=4]="DUPLICATE_PATTERNS_FOUND",t[t.INVALID_GROUP_TYPE_FOUND=5]="INVALID_GROUP_TYPE_FOUND",t[t.PUSH_MODE_DOES_NOT_EXIST=6]="PUSH_MODE_DOES_NOT_EXIST",t[t.MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE=7]="MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE",t[t.MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY=8]="MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY",t[t.MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST=9]="MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST",t[t.LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED=10]="LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED",t[t.SOI_ANCHOR_FOUND=11]="SOI_ANCHOR_FOUND",t[t.EMPTY_MATCH_PATTERN=12]="EMPTY_MATCH_PATTERN",t[t.NO_LINE_BREAKS_FLAGS=13]="NO_LINE_BREAKS_FLAGS",t[t.UNREACHABLE_PATTERN=14]="UNREACHABLE_PATTERN",t[t.IDENTIFY_TERMINATOR=15]="IDENTIFY_TERMINATOR",t[t.CUSTOM_LINE_BREAK=16]="CUSTOM_LINE_BREAK",t[t.MULTI_MODE_LEXER_LONGER_ALT_NOT_IN_CURRENT_MODE=17]="MULTI_MODE_LEXER_LONGER_ALT_NOT_IN_CURRENT_MODE"})(Gn||(Gn={}));a2={deferDefinitionErrorsHandling:!1,positionTracking:"full",lineTerminatorsPattern:/\n|\r\n?/g,lineTerminatorCharacters:[` +`,"\r"],ensureOptimizations:!1,safeMode:!1,errorMessageProvider:pN,traceInitPerf:!1,skipValidations:!1,recoveryEnabled:!0};Object.freeze(a2);ni=class{static{o(this,"Lexer")}constructor(e,r=a2){if(this.lexerDefinition=e,this.lexerDefinitionErrors=[],this.lexerDefinitionWarning=[],this.patternIdxToConfig={},this.charCodeToPatternIdxToConfig={},this.modes=[],this.emptyGroups={},this.trackStartLines=!0,this.trackEndLines=!0,this.hasCustom=!1,this.canModeBeOptimized={},this.TRACE_INIT=(i,a)=>{if(this.traceInitPerf===!0){this.traceInitIndent++;let s=new Array(this.traceInitIndent+1).join(" ");this.traceInitIndent <${i}>`);let{time:l,value:u}=t2(a),h=l>10?console.warn:console.log;return this.traceInitIndent time: ${l}ms`),this.traceInitIndent--,u}else return a()},typeof r=="boolean")throw Error(`The second argument to the Lexer constructor is now an ILexerConfig Object. +a boolean 2nd argument is no longer supported`);this.config=pa({},a2,r);let n=this.config.traceInitPerf;n===!0?(this.traceInitMaxIdent=1/0,this.traceInitPerf=!0):typeof n=="number"&&(this.traceInitMaxIdent=n,this.traceInitPerf=!0),this.traceInitIndent=-1,this.TRACE_INIT("Lexer Constructor",()=>{let i,a=!0;this.TRACE_INIT("Lexer Config handling",()=>{if(this.config.lineTerminatorsPattern===a2.lineTerminatorsPattern)this.config.lineTerminatorsPattern=kie;else if(this.config.lineTerminatorCharacters===a2.lineTerminatorCharacters)throw Error(`Error: Missing property on the Lexer config. + For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#MISSING_LINE_TERM_CHARS`);if(r.safeMode&&r.ensureOptimizations)throw Error('"safeMode" and "ensureOptimizations" flags are mutually exclusive.');this.trackStartLines=/full|onlyStart/i.test(this.config.positionTracking),this.trackEndLines=/full/i.test(this.config.positionTracking),wt(e)?i={modes:{defaultMode:Qr(e)},defaultMode:Qm}:(a=!1,i=Qr(e))}),this.config.skipValidations===!1&&(this.TRACE_INIT("performRuntimeChecks",()=>{this.lexerDefinitionErrors=this.lexerDefinitionErrors.concat(xie(i,this.trackStartLines,this.config.lineTerminatorCharacters))}),this.TRACE_INIT("performWarningRuntimeChecks",()=>{this.lexerDefinitionWarning=this.lexerDefinitionWarning.concat(bie(i,this.trackStartLines,this.config.lineTerminatorCharacters))})),i.modes=i.modes?i.modes:{},Ee(i.modes,(l,u)=>{i.modes[u]=Kh(l,h=>er(h))});let s=Dr(i.modes);if(Ee(i.modes,(l,u)=>{this.TRACE_INIT(`Mode: <${u}> processing`,()=>{if(this.modes.push(u),this.config.skipValidations===!1&&this.TRACE_INIT("validatePatterns",()=>{this.lexerDefinitionErrors=this.lexerDefinitionErrors.concat(vie(l,s))}),Qt(this.lexerDefinitionErrors)){Fu(l);let h;this.TRACE_INIT("analyzeTokenTypes",()=>{h=yie(l,{lineTerminatorCharacters:this.config.lineTerminatorCharacters,positionTracking:r.positionTracking,ensureOptimizations:r.ensureOptimizations,safeMode:r.safeMode,tracer:this.TRACE_INIT})}),this.patternIdxToConfig[u]=h.patternIdxToConfig,this.charCodeToPatternIdxToConfig[u]=h.charCodeToPatternIdxToConfig,this.emptyGroups=pa({},this.emptyGroups,h.emptyGroups),this.hasCustom=h.hasCustom||this.hasCustom,this.canModeBeOptimized[u]=h.canBeOptimized}})}),this.defaultMode=i.defaultMode,!Qt(this.lexerDefinitionErrors)&&!this.config.deferDefinitionErrorsHandling){let u=qe(this.lexerDefinitionErrors,h=>h.message).join(`----------------------- +`);throw new Error(`Errors detected in definition of Lexer: +`+u)}Ee(this.lexerDefinitionWarning,l=>{e2(l.message)}),this.TRACE_INIT("Choosing sub-methods implementations",()=>{if(fN?(this.chopInput=ea,this.match=this.matchWithTest):(this.updateLastIndex=qn,this.match=this.matchWithExec),a&&(this.handleModes=qn),this.trackStartLines===!1&&(this.computeNewColumn=ea),this.trackEndLines===!1&&(this.updateTokenEndLineColumnLocation=qn),/full/i.test(this.config.positionTracking))this.createTokenInstance=this.createFullToken;else if(/onlyStart/i.test(this.config.positionTracking))this.createTokenInstance=this.createStartOnlyToken;else if(/onlyOffset/i.test(this.config.positionTracking))this.createTokenInstance=this.createOffsetOnlyToken;else throw Error(`Invalid config option: "${this.config.positionTracking}"`);this.hasCustom?(this.addToken=this.addTokenUsingPush,this.handlePayload=this.handlePayloadWithCustom):(this.addToken=this.addTokenUsingMemberAccess,this.handlePayload=this.handlePayloadNoCustom)}),this.TRACE_INIT("Failed Optimization Warnings",()=>{let l=Vr(this.canModeBeOptimized,(u,h,f)=>(h===!1&&u.push(f),u),[]);if(r.ensureOptimizations&&!Qt(l))throw Error(`Lexer Modes: < ${l.join(", ")} > cannot be optimized. + Disable the "ensureOptimizations" lexer config flag to silently ignore this and run the lexer in an un-optimized mode. + Or inspect the console log for details on how to resolve these issues.`)}),this.TRACE_INIT("clearRegExpParserCache",()=>{uie()}),this.TRACE_INIT("toFastProperties",()=>{r2(this)})})}tokenize(e,r=this.defaultMode){if(!Qt(this.lexerDefinitionErrors)){let i=qe(this.lexerDefinitionErrors,a=>a.message).join(`----------------------- +`);throw new Error(`Unable to Tokenize because Errors detected in definition of Lexer: +`+i)}return this.tokenizeInternal(e,r)}tokenizeInternal(e,r){let n,i,a,s,l,u,h,f,d,p,m,g,y,v,x,b,w=e,S=w.length,T=0,E=0,_=this.hasCustom?0:Math.floor(e.length/10),A=new Array(_),L=[],M=this.trackStartLines?1:void 0,N=this.trackStartLines?1:void 0,k=wie(this.emptyGroups),I=this.trackStartLines,C=this.config.lineTerminatorsPattern,O=0,D=[],P=[],F=[],B=[];Object.freeze(B);let $;function z(){return D}o(z,"getPossiblePatternsSlow");function Y(J){let Z=Lc(J),H=P[Z];return H===void 0?B:H}o(Y,"getPossiblePatternsOptimized");let Q=o(J=>{if(F.length===1&&J.tokenType.PUSH_MODE===void 0){let Z=this.config.errorMessageProvider.buildUnableToPopLexerModeMessage(J);L.push({offset:J.startOffset,line:J.startLine,column:J.startColumn,length:J.image.length,message:Z})}else{F.pop();let Z=ma(F);D=this.patternIdxToConfig[Z],P=this.charCodeToPatternIdxToConfig[Z],O=D.length;let H=this.canModeBeOptimized[Z]&&this.config.safeMode===!1;P&&H?$=Y:$=z}},"pop_mode");function X(J){F.push(J),P=this.charCodeToPatternIdxToConfig[J],D=this.patternIdxToConfig[J],O=D.length,O=D.length;let Z=this.canModeBeOptimized[J]&&this.config.safeMode===!1;P&&Z?$=Y:$=z}o(X,"push_mode"),X.call(this,r);let ie,j=this.config.recoveryEnabled;for(;Tu.length){u=s,h=f,ie=ce;break}}}break}}if(u!==null){if(d=u.length,p=ie.group,p!==void 0&&(m=ie.tokenTypeIdx,g=this.createTokenInstance(u,T,m,ie.tokenType,M,N,d),this.handlePayload(g,h),p===!1?E=this.addToken(A,E,g):k[p].push(g)),e=this.chopInput(e,d),T=T+d,N=this.computeNewColumn(N,d),I===!0&&ie.canLineTerminator===!0){let q=0,K,se;C.lastIndex=0;do K=C.test(u),K===!0&&(se=C.lastIndex-1,q++);while(K===!0);q!==0&&(M=M+q,N=d-se,this.updateTokenEndLineColumnLocation(g,p,se,q,M,N,d))}this.handleModes(ie,Q,X,g)}else{let q=T,K=M,se=N,ce=j===!1;for(;ce===!1&&T{"use strict";Pt();i2();s0();o(zu,"tokenLabel");o(gN,"hasTokenLabel");EIe="parent",Rie="categories",Nie="label",Mie="group",Iie="push_mode",Oie="pop_mode",Pie="longer_alt",Bie="line_breaks",Fie="start_chars_hint";o(VT,"createToken");o(CIe,"createTokenInternal");fo=VT({name:"EOF",pattern:ni.NA});Fu([fo]);o(o0,"createTokenInstance");o(s2,"tokenMatcher")});var Gu,zie,Ol,Jm=R(()=>{"use strict";l0();Pt();ns();Gu={buildMismatchTokenMessage({expected:t,actual:e,previous:r,ruleName:n}){return`Expecting ${gN(t)?`--> ${zu(t)} <--`:`token of type --> ${t.name} <--`} but found --> '${e.image}' <--`},buildNotAllInputParsedMessage({firstRedundant:t,ruleName:e}){return"Redundant input, expecting EOF but found: "+t.image},buildNoViableAltMessage({expectedPathsPerAlt:t,actual:e,previous:r,customUserDescription:n,ruleName:i}){let a="Expecting: ",l=` +but found: '`+na(e).image+"'";if(n)return a+n+l;{let u=Vr(t,(p,m)=>p.concat(m),[]),h=qe(u,p=>`[${qe(p,m=>zu(m)).join(", ")}]`),d=`one of these possible Token sequences: +${qe(h,(p,m)=>` ${m+1}. ${p}`).join(` +`)}`;return a+d+l}},buildEarlyExitMessage({expectedIterationPaths:t,actual:e,customUserDescription:r,ruleName:n}){let i="Expecting: ",s=` +but found: '`+na(e).image+"'";if(r)return i+r+s;{let u=`expecting at least one iteration which starts with one of these possible Token sequences:: + <${qe(t,h=>`[${qe(h,f=>zu(f)).join(",")}]`).join(" ,")}>`;return i+u+s}}};Object.freeze(Gu);zie={buildRuleNotFoundError(t,e){return"Invalid grammar, reference to a rule which is not defined: ->"+e.nonTerminalName+`<- +inside top level rule: ->`+t.name+"<-"}},Ol={buildDuplicateFoundError(t,e){function r(f){return f instanceof fr?f.terminalType.name:f instanceof Zr?f.nonTerminalName:""}o(r,"getExtraProductionArgument");let n=t.name,i=na(e),a=i.idx,s=Rs(i),l=r(i),u=a>0,h=`->${s}${u?a:""}<- ${l?`with argument: ->${l}<-`:""} + appears more than once (${e.length} times) in the top level rule: ->${n}<-. + For further details see: https://chevrotain.io/docs/FAQ.html#NUMERICAL_SUFFIXES + `;return h=h.replace(/[ \t]+/g," "),h=h.replace(/\s\s+/g,` +`),h},buildNamespaceConflictError(t){return`Namespace conflict found in grammar. +The grammar has both a Terminal(Token) and a Non-Terminal(Rule) named: <${t.name}>. +To resolve this make sure each Terminal and Non-Terminal names are unique +This is easy to accomplish by using the convention that Terminal names start with an uppercase letter +and Non-Terminal names start with a lower case letter.`},buildAlternationPrefixAmbiguityError(t){let e=qe(t.prefixPath,i=>zu(i)).join(", "),r=t.alternation.idx===0?"":t.alternation.idx;return`Ambiguous alternatives: <${t.ambiguityIndices.join(" ,")}> due to common lookahead prefix +in inside <${t.topLevelRule.name}> Rule, +<${e}> may appears as a prefix path in all these alternatives. +See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#COMMON_PREFIX +For Further details.`},buildAlternationAmbiguityError(t){let e=qe(t.prefixPath,i=>zu(i)).join(", "),r=t.alternation.idx===0?"":t.alternation.idx,n=`Ambiguous Alternatives Detected: <${t.ambiguityIndices.join(" ,")}> in inside <${t.topLevelRule.name}> Rule, +<${e}> may appears as a prefix path in all these alternatives. +`;return n=n+`See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#AMBIGUOUS_ALTERNATIVES +For Further details.`,n},buildEmptyRepetitionError(t){let e=Rs(t.repetition);return t.repetition.idx!==0&&(e+=t.repetition.idx),`The repetition <${e}> within Rule <${t.topLevelRule.name}> can never consume any tokens. +This could lead to an infinite loop.`},buildTokenNameError(t){return"deprecated"},buildEmptyAlternationError(t){return`Ambiguous empty alternative: <${t.emptyChoiceIdx+1}> in inside <${t.topLevelRule.name}> Rule. +Only the last alternative may be an empty alternative.`},buildTooManyAlternativesError(t){return`An Alternation cannot have more than 256 alternatives: + inside <${t.topLevelRule.name}> Rule. + has ${t.alternation.definition.length+1} alternatives.`},buildLeftRecursionError(t){let e=t.topLevelRule.name,r=qe(t.leftRecursionPath,a=>a.name),n=`${e} --> ${r.concat([e]).join(" --> ")}`;return`Left Recursion found in grammar. +rule: <${e}> can be invoked from itself (directly or indirectly) +without consuming any Tokens. The grammar path that causes this is: + ${n} + To fix this refactor your grammar to remove the left recursion. +see: https://en.wikipedia.org/wiki/LL_parser#Left_factoring.`},buildInvalidRuleNameError(t){return"deprecated"},buildDuplicateRuleNameError(t){let e;return t.topLevelRule instanceof ts?e=t.topLevelRule.name:e=t.topLevelRule,`Duplicate definition, rule: ->${e}<- is already defined in the grammar: ->${t.grammarName}<-`}}});function Gie(t,e){let r=new yN(t,e);return r.resolveRefs(),r.errors}var yN,$ie=R(()=>{"use strict";Ns();Pt();ns();o(Gie,"resolveGrammar");yN=class extends rs{static{o(this,"GastRefResolverVisitor")}constructor(e,r){super(),this.nameToTopRule=e,this.errMsgProvider=r,this.errors=[]}resolveRefs(){Ee(or(this.nameToTopRule),e=>{this.currTopLevel=e,e.accept(this)})}visitNonTerminal(e){let r=this.nameToTopRule[e.nonTerminalName];if(r)e.referencedRule=r;else{let n=this.errMsgProvider.buildRuleNotFoundError(this.currTopLevel,e);this.errors.push({message:n,type:Pi.UNRESOLVED_SUBRULE_REF,ruleName:this.currTopLevel.name,unresolvedRefName:e.nonTerminalName})}}}});function WT(t,e,r=[]){r=Qr(r);let n=[],i=0;function a(l){return l.concat(fi(t,i+1))}o(a,"remainingPathWith");function s(l){let u=WT(a(l),e,r);return n.concat(u)}for(o(s,"getAlternativesForProd");r.length{Qt(u.definition)===!1&&(n=s(u.definition))}),n;if(l instanceof fr)r.push(l.terminalType);else throw Error("non exhaustive match")}i++}return n.push({partialPath:r,suffixDef:fi(t,i)}),n}function qT(t,e,r,n){let i="EXIT_NONE_TERMINAL",a=[i],s="EXIT_ALTERNATIVE",l=!1,u=e.length,h=u-n-1,f=[],d=[];for(d.push({idx:-1,def:t,ruleStack:[],occurrenceStack:[]});!Qt(d);){let p=d.pop();if(p===s){l&&ma(d).idx<=h&&d.pop();continue}let m=p.def,g=p.idx,y=p.ruleStack,v=p.occurrenceStack;if(Qt(m))continue;let x=m[0];if(x===i){let b={idx:g,def:fi(m),ruleStack:Ru(y),occurrenceStack:Ru(v)};d.push(b)}else if(x instanceof fr)if(g=0;b--){let w=x.definition[b],S={idx:g,def:w.definition.concat(fi(m)),ruleStack:y,occurrenceStack:v};d.push(S),d.push(s)}else if(x instanceof Sn)d.push({idx:g,def:x.definition.concat(fi(m)),ruleStack:y,occurrenceStack:v});else if(x instanceof ts)d.push(SIe(x,g,y,v));else throw Error("non exhaustive match")}return f}function SIe(t,e,r,n){let i=Qr(r);i.push(t.name);let a=Qr(n);return a.push(1),{idx:e,def:t.definition,ruleStack:i,occurrenceStack:a}}var vN,UT,eg,HT,o2,YT,l2,c2=R(()=>{"use strict";Pt();iN();IT();ns();vN=class extends Pu{static{o(this,"AbstractNextPossibleTokensWalker")}constructor(e,r){super(),this.topProd=e,this.path=r,this.possibleTokTypes=[],this.nextProductionName="",this.nextProductionOccurrence=0,this.found=!1,this.isAtEndOfPath=!1}startWalking(){if(this.found=!1,this.path.ruleStack[0]!==this.topProd.name)throw Error("The path does not start with the walker's top Rule!");return this.ruleStack=Qr(this.path.ruleStack).reverse(),this.occurrenceStack=Qr(this.path.occurrenceStack).reverse(),this.ruleStack.pop(),this.occurrenceStack.pop(),this.updateExpectedNext(),this.walk(this.topProd),this.possibleTokTypes}walk(e,r=[]){this.found||super.walk(e,r)}walkProdRef(e,r,n){if(e.referencedRule.name===this.nextProductionName&&e.idx===this.nextProductionOccurrence){let i=r.concat(n);this.updateExpectedNext(),this.walk(e.referencedRule,i)}}updateExpectedNext(){Qt(this.ruleStack)?(this.nextProductionName="",this.nextProductionOccurrence=0,this.isAtEndOfPath=!0):(this.nextProductionName=this.ruleStack.pop(),this.nextProductionOccurrence=this.occurrenceStack.pop())}},UT=class extends vN{static{o(this,"NextAfterTokenWalker")}constructor(e,r){super(e,r),this.path=r,this.nextTerminalName="",this.nextTerminalOccurrence=0,this.nextTerminalName=this.path.lastTok.name,this.nextTerminalOccurrence=this.path.lastTokOccurrence}walkTerminal(e,r,n){if(this.isAtEndOfPath&&e.terminalType.name===this.nextTerminalName&&e.idx===this.nextTerminalOccurrence&&!this.found){let i=r.concat(n),a=new Sn({definition:i});this.possibleTokTypes=i0(a),this.found=!0}}},eg=class extends Pu{static{o(this,"AbstractNextTerminalAfterProductionWalker")}constructor(e,r){super(),this.topRule=e,this.occurrence=r,this.result={token:void 0,occurrence:void 0,isEndOfRule:void 0}}startWalking(){return this.walk(this.topRule),this.result}},HT=class extends eg{static{o(this,"NextTerminalAfterManyWalker")}walkMany(e,r,n){if(e.idx===this.occurrence){let i=na(r.concat(n));this.result.isEndOfRule=i===void 0,i instanceof fr&&(this.result.token=i.terminalType,this.result.occurrence=i.idx)}else super.walkMany(e,r,n)}},o2=class extends eg{static{o(this,"NextTerminalAfterManySepWalker")}walkManySep(e,r,n){if(e.idx===this.occurrence){let i=na(r.concat(n));this.result.isEndOfRule=i===void 0,i instanceof fr&&(this.result.token=i.terminalType,this.result.occurrence=i.idx)}else super.walkManySep(e,r,n)}},YT=class extends eg{static{o(this,"NextTerminalAfterAtLeastOneWalker")}walkAtLeastOne(e,r,n){if(e.idx===this.occurrence){let i=na(r.concat(n));this.result.isEndOfRule=i===void 0,i instanceof fr&&(this.result.token=i.terminalType,this.result.occurrence=i.idx)}else super.walkAtLeastOne(e,r,n)}},l2=class extends eg{static{o(this,"NextTerminalAfterAtLeastOneSepWalker")}walkAtLeastOneSep(e,r,n){if(e.idx===this.occurrence){let i=na(r.concat(n));this.result.isEndOfRule=i===void 0,i instanceof fr&&(this.result.token=i.terminalType,this.result.occurrence=i.idx)}else super.walkAtLeastOneSep(e,r,n)}};o(WT,"possiblePathsFrom");o(qT,"nextPossibleTokensAfter");o(SIe,"expandTopLevelRule")});function u2(t){if(t instanceof Jr||t==="Option")return $n.OPTION;if(t instanceof br||t==="Repetition")return $n.REPETITION;if(t instanceof An||t==="RepetitionMandatory")return $n.REPETITION_MANDATORY;if(t instanceof _n||t==="RepetitionMandatoryWithSeparator")return $n.REPETITION_MANDATORY_WITH_SEPARATOR;if(t instanceof mn||t==="RepetitionWithSeparator")return $n.REPETITION_WITH_SEPARATOR;if(t instanceof gn||t==="Alternation")return $n.ALTERNATION;throw Error("non exhaustive match")}function jT(t){let{occurrence:e,rule:r,prodType:n,maxLookahead:i}=t,a=u2(n);return a===$n.ALTERNATION?tg(e,r,i):rg(e,r,a,i)}function Uie(t,e,r,n,i,a){let s=tg(t,e,r),l=jie(s)?Zm:Bu;return a(s,n,l,i)}function Hie(t,e,r,n,i,a){let s=rg(t,e,i,r),l=jie(s)?Zm:Bu;return a(s[0],l,n)}function Yie(t,e,r,n){let i=t.length,a=Ia(t,s=>Ia(s,l=>l.length===1));if(e)return function(s){let l=qe(s,u=>u.GATE);for(let u=0;uGr(u)),l=Vr(s,(u,h,f)=>(Ee(h,d=>{Xe(u,d.tokenTypeIdx)||(u[d.tokenTypeIdx]=f),Ee(d.categoryMatches,p=>{Xe(u,p)||(u[p]=f)})}),u),{});return function(){let u=this.LA(1);return l[u.tokenTypeIdx]}}else return function(){for(let s=0;sa.length===1),i=t.length;if(n&&!r){let a=Gr(t);if(a.length===1&&Qt(a[0].categoryMatches)){let l=a[0].tokenTypeIdx;return function(){return this.LA(1).tokenTypeIdx===l}}else{let s=Vr(a,(l,u,h)=>(l[u.tokenTypeIdx]=!0,Ee(u.categoryMatches,f=>{l[f]=!0}),l),[]);return function(){let l=this.LA(1);return s[l.tokenTypeIdx]===!0}}}else return function(){e:for(let a=0;aWT([s],1)),n=Vie(r.length),i=qe(r,s=>{let l={};return Ee(s,u=>{let h=xN(u.partialPath);Ee(h,f=>{l[f]=!0})}),l}),a=r;for(let s=1;s<=e;s++){let l=a;a=Vie(l.length);for(let u=0;u{let x=xN(v.partialPath);Ee(x,b=>{i[u][b]=!0})})}}}}return n}function tg(t,e,r,n){let i=new XT(t,$n.ALTERNATION,n);return e.accept(i),qie(i.result,r)}function rg(t,e,r,n){let i=new XT(t,r);e.accept(i);let a=i.result,l=new bN(e,t,r).startWalking(),u=new Sn({definition:a}),h=new Sn({definition:l});return qie([u,h],n)}function KT(t,e){e:for(let r=0;r{let i=e[n];return r===i||i.categoryMatchesMap[r.tokenTypeIdx]})}function jie(t){return Ia(t,e=>Ia(e,r=>Ia(r,n=>Qt(n.categoryMatches))))}var $n,bN,XT,ng=R(()=>{"use strict";Pt();c2();IT();s0();ns();(function(t){t[t.OPTION=0]="OPTION",t[t.REPETITION=1]="REPETITION",t[t.REPETITION_MANDATORY=2]="REPETITION_MANDATORY",t[t.REPETITION_MANDATORY_WITH_SEPARATOR=3]="REPETITION_MANDATORY_WITH_SEPARATOR",t[t.REPETITION_WITH_SEPARATOR=4]="REPETITION_WITH_SEPARATOR",t[t.ALTERNATION=5]="ALTERNATION"})($n||($n={}));o(u2,"getProdType");o(jT,"getLookaheadPaths");o(Uie,"buildLookaheadFuncForOr");o(Hie,"buildLookaheadFuncForOptionalProd");o(Yie,"buildAlternativesLookAheadFunc");o(Wie,"buildSingleAlternativeLookaheadFunction");bN=class extends Pu{static{o(this,"RestDefinitionFinderWalker")}constructor(e,r,n){super(),this.topProd=e,this.targetOccurrence=r,this.targetProdType=n}startWalking(){return this.walk(this.topProd),this.restDef}checkIsTarget(e,r,n,i){return e.idx===this.targetOccurrence&&this.targetProdType===r?(this.restDef=n.concat(i),!0):!1}walkOption(e,r,n){this.checkIsTarget(e,$n.OPTION,r,n)||super.walkOption(e,r,n)}walkAtLeastOne(e,r,n){this.checkIsTarget(e,$n.REPETITION_MANDATORY,r,n)||super.walkOption(e,r,n)}walkAtLeastOneSep(e,r,n){this.checkIsTarget(e,$n.REPETITION_MANDATORY_WITH_SEPARATOR,r,n)||super.walkOption(e,r,n)}walkMany(e,r,n){this.checkIsTarget(e,$n.REPETITION,r,n)||super.walkOption(e,r,n)}walkManySep(e,r,n){this.checkIsTarget(e,$n.REPETITION_WITH_SEPARATOR,r,n)||super.walkOption(e,r,n)}},XT=class extends rs{static{o(this,"InsideDefinitionFinderVisitor")}constructor(e,r,n){super(),this.targetOccurrence=e,this.targetProdType=r,this.targetRef=n,this.result=[]}checkIsTarget(e,r){e.idx===this.targetOccurrence&&this.targetProdType===r&&(this.targetRef===void 0||e===this.targetRef)&&(this.result=e.definition)}visitOption(e){this.checkIsTarget(e,$n.OPTION)}visitRepetition(e){this.checkIsTarget(e,$n.REPETITION)}visitRepetitionMandatory(e){this.checkIsTarget(e,$n.REPETITION_MANDATORY)}visitRepetitionMandatoryWithSeparator(e){this.checkIsTarget(e,$n.REPETITION_MANDATORY_WITH_SEPARATOR)}visitRepetitionWithSeparator(e){this.checkIsTarget(e,$n.REPETITION_WITH_SEPARATOR)}visitAlternation(e){this.checkIsTarget(e,$n.ALTERNATION)}};o(Vie,"initializeArrayOfArrays");o(xN,"pathToHashKeys");o(AIe,"isUniquePrefixHash");o(qie,"lookAheadSequenceFromAlternatives");o(tg,"getLookaheadPathsForOr");o(rg,"getLookaheadPathsForOptionalProd");o(KT,"containsPath");o(Xie,"isStrictPrefixOfPath");o(jie,"areTokenCategoriesNotUsed")});function Kie(t){let e=t.lookaheadStrategy.validate({rules:t.rules,tokenTypes:t.tokenTypes,grammarName:t.grammarName});return qe(e,r=>Object.assign({type:Pi.CUSTOM_LOOKAHEAD_VALIDATION},r))}function Qie(t,e,r,n){let i=ga(t,u=>_Ie(u,r)),a=IIe(t,e,r),s=ga(t,u=>RIe(u,r)),l=ga(t,u=>DIe(u,t,n,r));return i.concat(a,s,l)}function _Ie(t,e){let r=new wN;t.accept(r);let n=r.allProductions,i=IL(n,LIe),a=Ls(i,l=>l.length>1);return qe(or(a),l=>{let u=na(l),h=e.buildDuplicateFoundError(t,l),f=Rs(u),d={message:h,type:Pi.DUPLICATE_PRODUCTIONS,ruleName:t.name,dslName:f,occurrence:u.idx},p=Zie(u);return p&&(d.parameter=p),d})}function LIe(t){return`${Rs(t)}_#_${t.idx}_#_${Zie(t)}`}function Zie(t){return t instanceof fr?t.terminalType.name:t instanceof Zr?t.nonTerminalName:""}function DIe(t,e,r,n){let i=[];if(Vr(e,(s,l)=>l.name===t.name?s+1:s,0)>1){let s=n.buildDuplicateRuleNameError({topLevelRule:t,grammarName:r});i.push({message:s,type:Pi.DUPLICATE_RULE_NAME,ruleName:t.name})}return i}function Jie(t,e,r){let n=[],i;return Fn(e,t)||(i=`Invalid rule override, rule: ->${t}<- cannot be overridden in the grammar: ->${r}<-as it is not defined in any of the super grammars `,n.push({message:i,type:Pi.INVALID_RULE_OVERRIDE,ruleName:t})),n}function kN(t,e,r,n=[]){let i=[],a=QT(e.definition);if(Qt(a))return[];{let s=t.name;Fn(a,t)&&i.push({message:r.buildLeftRecursionError({topLevelRule:t,leftRecursionPath:n}),type:Pi.LEFT_RECURSION,ruleName:s});let u=jh(a,n.concat([t])),h=ga(u,f=>{let d=Qr(n);return d.push(f),kN(t,f,r,d)});return i.concat(h)}}function QT(t){let e=[];if(Qt(t))return e;let r=na(t);if(r instanceof Zr)e.push(r.referencedRule);else if(r instanceof Sn||r instanceof Jr||r instanceof An||r instanceof _n||r instanceof mn||r instanceof br)e=e.concat(QT(r.definition));else if(r instanceof gn)e=Gr(qe(r.definition,a=>QT(a.definition)));else if(!(r instanceof fr))throw Error("non exhaustive match");let n=n0(r),i=t.length>1;if(n&&i){let a=fi(t);return e.concat(QT(a))}else return e}function eae(t,e){let r=new h2;t.accept(r);let n=r.alternations;return ga(n,a=>{let s=Ru(a.definition);return ga(s,(l,u)=>{let h=qT([l],[],Bu,1);return Qt(h)?[{message:e.buildEmptyAlternationError({topLevelRule:t,alternation:a,emptyChoiceIdx:u}),type:Pi.NONE_LAST_EMPTY_ALT,ruleName:t.name,occurrence:a.idx,alternative:u+1}]:[]})})}function tae(t,e,r){let n=new h2;t.accept(n);let i=n.alternations;return i=Kh(i,s=>s.ignoreAmbiguities===!0),ga(i,s=>{let l=s.idx,u=s.maxLookahead||e,h=tg(l,t,u,s),f=NIe(h,s,t,r),d=MIe(h,s,t,r);return f.concat(d)})}function RIe(t,e){let r=new h2;t.accept(r);let n=r.alternations;return ga(n,a=>a.definition.length>255?[{message:e.buildTooManyAlternativesError({topLevelRule:t,alternation:a}),type:Pi.TOO_MANY_ALTS,ruleName:t.name,occurrence:a.idx}]:[])}function rae(t,e,r){let n=[];return Ee(t,i=>{let a=new TN;i.accept(a);let s=a.allProductions;Ee(s,l=>{let u=u2(l),h=l.maxLookahead||e,f=l.idx,p=rg(f,i,u,h)[0];if(Qt(Gr(p))){let m=r.buildEmptyRepetitionError({topLevelRule:i,repetition:l});n.push({message:m,type:Pi.NO_NON_EMPTY_LOOKAHEAD,ruleName:i.name})}})}),n}function NIe(t,e,r,n){let i=[],a=Vr(t,(l,u,h)=>(e.definition[h].ignoreAmbiguities===!0||Ee(u,f=>{let d=[h];Ee(t,(p,m)=>{h!==m&&KT(p,f)&&e.definition[m].ignoreAmbiguities!==!0&&d.push(m)}),d.length>1&&!KT(i,f)&&(i.push(f),l.push({alts:d,path:f}))}),l),[]);return qe(a,l=>{let u=qe(l.alts,f=>f+1);return{message:n.buildAlternationAmbiguityError({topLevelRule:r,alternation:e,ambiguityIndices:u,prefixPath:l.path}),type:Pi.AMBIGUOUS_ALTS,ruleName:r.name,occurrence:e.idx,alternatives:l.alts}})}function MIe(t,e,r,n){let i=Vr(t,(s,l,u)=>{let h=qe(l,f=>({idx:u,path:f}));return s.concat(h)},[]);return wc(ga(i,s=>{if(e.definition[s.idx].ignoreAmbiguities===!0)return[];let u=s.idx,h=s.path,f=$r(i,p=>e.definition[p.idx].ignoreAmbiguities!==!0&&p.idx{let m=[p.idx+1,u+1],g=e.idx===0?"":e.idx;return{message:n.buildAlternationPrefixAmbiguityError({topLevelRule:r,alternation:e,ambiguityIndices:m,prefixPath:p.path}),type:Pi.AMBIGUOUS_PREFIX_ALTS,ruleName:r.name,occurrence:g,alternatives:m}})}))}function IIe(t,e,r){let n=[],i=qe(e,a=>a.name);return Ee(t,a=>{let s=a.name;if(Fn(i,s)){let l=r.buildNamespaceConflictError(a);n.push({message:l,type:Pi.CONFLICT_TOKENS_RULES_NAMESPACE,ruleName:s})}}),n}var wN,h2,TN,f2=R(()=>{"use strict";Pt();Ns();ns();ng();c2();s0();o(Kie,"validateLookahead");o(Qie,"validateGrammar");o(_Ie,"validateDuplicateProductions");o(LIe,"identifyProductionForDuplicates");o(Zie,"getExtraProductionArgument");wN=class extends rs{static{o(this,"OccurrenceValidationCollector")}constructor(){super(...arguments),this.allProductions=[]}visitNonTerminal(e){this.allProductions.push(e)}visitOption(e){this.allProductions.push(e)}visitRepetitionWithSeparator(e){this.allProductions.push(e)}visitRepetitionMandatory(e){this.allProductions.push(e)}visitRepetitionMandatoryWithSeparator(e){this.allProductions.push(e)}visitRepetition(e){this.allProductions.push(e)}visitAlternation(e){this.allProductions.push(e)}visitTerminal(e){this.allProductions.push(e)}};o(DIe,"validateRuleDoesNotAlreadyExist");o(Jie,"validateRuleIsOverridden");o(kN,"validateNoLeftRecursion");o(QT,"getFirstNoneTerminal");h2=class extends rs{static{o(this,"OrCollector")}constructor(){super(...arguments),this.alternations=[]}visitAlternation(e){this.alternations.push(e)}};o(eae,"validateEmptyOrAlternative");o(tae,"validateAmbiguousAlternationAlternatives");TN=class extends rs{static{o(this,"RepetitionCollector")}constructor(){super(...arguments),this.allProductions=[]}visitRepetitionWithSeparator(e){this.allProductions.push(e)}visitRepetitionMandatory(e){this.allProductions.push(e)}visitRepetitionMandatoryWithSeparator(e){this.allProductions.push(e)}visitRepetition(e){this.allProductions.push(e)}};o(RIe,"validateTooManyAlts");o(rae,"validateSomeNonEmptyLookaheadPath");o(NIe,"checkAlternativesAmbiguities");o(MIe,"checkPrefixAlternativesAmbiguities");o(IIe,"checkTerminalAndNoneTerminalsNameSpace")});function nae(t){let e=Xh(t,{errMsgProvider:zie}),r={};return Ee(t.rules,n=>{r[n.name]=n}),Gie(r,e.errMsgProvider)}function iae(t){return t=Xh(t,{errMsgProvider:Ol}),Qie(t.rules,t.tokenTypes,t.errMsgProvider,t.grammarName)}var aae=R(()=>{"use strict";Pt();$ie();f2();Jm();o(nae,"resolveGrammar");o(iae,"validateGrammar")});function nf(t){return Fn(uae,t.name)}var sae,oae,lae,cae,uae,ig,c0,d2,p2,m2,ag=R(()=>{"use strict";Pt();sae="MismatchedTokenException",oae="NoViableAltException",lae="EarlyExitException",cae="NotAllInputParsedException",uae=[sae,oae,lae,cae];Object.freeze(uae);o(nf,"isRecognitionException");ig=class extends Error{static{o(this,"RecognitionException")}constructor(e,r){super(e),this.token=r,this.resyncedTokens=[],Object.setPrototypeOf(this,new.target.prototype),Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}},c0=class extends ig{static{o(this,"MismatchedTokenException")}constructor(e,r,n){super(e,r),this.previousToken=n,this.name=sae}},d2=class extends ig{static{o(this,"NoViableAltException")}constructor(e,r,n){super(e,r),this.previousToken=n,this.name=oae}},p2=class extends ig{static{o(this,"NotAllInputParsedException")}constructor(e,r){super(e,r),this.name=cae}},m2=class extends ig{static{o(this,"EarlyExitException")}constructor(e,r,n){super(e,r),this.previousToken=n,this.name=lae}}});function OIe(t,e,r,n,i,a,s){let l=this.getKeyForAutomaticLookahead(n,i),u=this.firstAfterRepMap[l];if(u===void 0){let p=this.getCurrRuleFullName(),m=this.getGAstProductions()[p];u=new a(m,i).startWalking(),this.firstAfterRepMap[l]=u}let h=u.token,f=u.occurrence,d=u.isEndOfRule;this.RULE_STACK.length===1&&d&&h===void 0&&(h=fo,f=1),!(h===void 0||f===void 0)&&this.shouldInRepetitionRecoveryBeTried(h,f,s)&&this.tryInRepetitionRecovery(t,e,r,h)}var EN,SN,CN,ZT,AN=R(()=>{"use strict";l0();Pt();ag();aN();Ns();EN={},SN="InRuleRecoveryException",CN=class extends Error{static{o(this,"InRuleRecoveryException")}constructor(e){super(e),this.name=SN}},ZT=class{static{o(this,"Recoverable")}initRecoverable(e){this.firstAfterRepMap={},this.resyncFollows={},this.recoveryEnabled=Xe(e,"recoveryEnabled")?e.recoveryEnabled:is.recoveryEnabled,this.recoveryEnabled&&(this.attemptInRepetitionRecovery=OIe)}getTokenToInsert(e){let r=o0(e,"",NaN,NaN,NaN,NaN,NaN,NaN);return r.isInsertedInRecovery=!0,r}canTokenTypeBeInsertedInRecovery(e){return!0}canTokenTypeBeDeletedInRecovery(e){return!0}tryInRepetitionRecovery(e,r,n,i){let a=this.findReSyncTokenType(),s=this.exportLexerState(),l=[],u=!1,h=this.LA(1),f=this.LA(1),d=o(()=>{let p=this.LA(0),m=this.errorMessageProvider.buildMismatchTokenMessage({expected:i,actual:h,previous:p,ruleName:this.getCurrRuleFullName()}),g=new c0(m,h,this.LA(0));g.resyncedTokens=Ru(l),this.SAVE_ERROR(g)},"generateErrorMessage");for(;!u;)if(this.tokenMatcher(f,i)){d();return}else if(n.call(this)){d(),e.apply(this,r);return}else this.tokenMatcher(f,a)?u=!0:(f=this.SKIP_TOKEN(),this.addToResyncTokens(f,l));this.importLexerState(s)}shouldInRepetitionRecoveryBeTried(e,r,n){return!(n===!1||this.tokenMatcher(this.LA(1),e)||this.isBackTracking()||this.canPerformInRuleRecovery(e,this.getFollowsForInRuleRecovery(e,r)))}getFollowsForInRuleRecovery(e,r){let n=this.getCurrentGrammarPath(e,r);return this.getNextPossibleTokenTypes(n)}tryInRuleRecovery(e,r){if(this.canRecoverWithSingleTokenInsertion(e,r))return this.getTokenToInsert(e);if(this.canRecoverWithSingleTokenDeletion(e)){let n=this.SKIP_TOKEN();return this.consumeToken(),n}throw new CN("sad sad panda")}canPerformInRuleRecovery(e,r){return this.canRecoverWithSingleTokenInsertion(e,r)||this.canRecoverWithSingleTokenDeletion(e)}canRecoverWithSingleTokenInsertion(e,r){if(!this.canTokenTypeBeInsertedInRecovery(e)||Qt(r))return!1;let n=this.LA(1);return Za(r,a=>this.tokenMatcher(n,a))!==void 0}canRecoverWithSingleTokenDeletion(e){return this.canTokenTypeBeDeletedInRecovery(e)?this.tokenMatcher(this.LA(2),e):!1}isInCurrentRuleReSyncSet(e){let r=this.getCurrFollowKey(),n=this.getFollowSetFromFollowKey(r);return Fn(n,e)}findReSyncTokenType(){let e=this.flattenFollowSet(),r=this.LA(1),n=2;for(;;){let i=Za(e,a=>s2(r,a));if(i!==void 0)return i;r=this.LA(n),n++}}getCurrFollowKey(){if(this.RULE_STACK.length===1)return EN;let e=this.getLastExplicitRuleShortName(),r=this.getLastExplicitRuleOccurrenceIndex(),n=this.getPreviousExplicitRuleShortName();return{ruleName:this.shortRuleNameToFullName(e),idxInCallingRule:r,inRule:this.shortRuleNameToFullName(n)}}buildFullFollowKeyStack(){let e=this.RULE_STACK,r=this.RULE_OCCURRENCE_STACK;return qe(e,(n,i)=>i===0?EN:{ruleName:this.shortRuleNameToFullName(n),idxInCallingRule:r[i],inRule:this.shortRuleNameToFullName(e[i-1])})}flattenFollowSet(){let e=qe(this.buildFullFollowKeyStack(),r=>this.getFollowSetFromFollowKey(r));return Gr(e)}getFollowSetFromFollowKey(e){if(e===EN)return[fo];let r=e.ruleName+e.idxInCallingRule+OT+e.inRule;return this.resyncFollows[r]}addToResyncTokens(e,r){return this.tokenMatcher(e,fo)||r.push(e),r}reSyncTo(e){let r=[],n=this.LA(1);for(;this.tokenMatcher(n,e)===!1;)n=this.SKIP_TOKEN(),this.addToResyncTokens(n,r);return Ru(r)}attemptInRepetitionRecovery(e,r,n,i,a,s,l){}getCurrentGrammarPath(e,r){let n=this.getHumanReadableRuleStack(),i=Qr(this.RULE_OCCURRENCE_STACK);return{ruleStack:n,occurrenceStack:i,lastTok:e,lastTokOccurrence:r}}getHumanReadableRuleStack(){return qe(this.RULE_STACK,e=>this.shortRuleNameToFullName(e))}};o(OIe,"attemptInRepetitionRecovery")});function JT(t,e,r){return r|e|t}var ek=R(()=>{"use strict";o(JT,"getKeyForAutomaticLookahead")});var $u,_N=R(()=>{"use strict";Pt();Jm();Ns();f2();ng();$u=class{static{o(this,"LLkLookaheadStrategy")}constructor(e){var r;this.maxLookahead=(r=e?.maxLookahead)!==null&&r!==void 0?r:is.maxLookahead}validate(e){let r=this.validateNoLeftRecursion(e.rules);if(Qt(r)){let n=this.validateEmptyOrAlternatives(e.rules),i=this.validateAmbiguousAlternationAlternatives(e.rules,this.maxLookahead),a=this.validateSomeNonEmptyLookaheadPath(e.rules,this.maxLookahead);return[...r,...n,...i,...a]}return r}validateNoLeftRecursion(e){return ga(e,r=>kN(r,r,Ol))}validateEmptyOrAlternatives(e){return ga(e,r=>eae(r,Ol))}validateAmbiguousAlternationAlternatives(e,r){return ga(e,n=>tae(n,r,Ol))}validateSomeNonEmptyLookaheadPath(e,r){return rae(e,r,Ol)}buildLookaheadForAlternation(e){return Uie(e.prodOccurrence,e.rule,e.maxLookahead,e.hasPredicates,e.dynamicTokensEnabled,Yie)}buildLookaheadForOptional(e){return Hie(e.prodOccurrence,e.rule,e.maxLookahead,e.dynamicTokensEnabled,u2(e.prodType),Wie)}}});function PIe(t){tk.reset(),t.accept(tk);let e=tk.dslMethods;return tk.reset(),e}var rk,LN,tk,hae=R(()=>{"use strict";Pt();Ns();ek();ns();_N();rk=class{static{o(this,"LooksAhead")}initLooksAhead(e){this.dynamicTokensEnabled=Xe(e,"dynamicTokensEnabled")?e.dynamicTokensEnabled:is.dynamicTokensEnabled,this.maxLookahead=Xe(e,"maxLookahead")?e.maxLookahead:is.maxLookahead,this.lookaheadStrategy=Xe(e,"lookaheadStrategy")?e.lookaheadStrategy:new $u({maxLookahead:this.maxLookahead}),this.lookAheadFuncsCache=new Map}preComputeLookaheadFunctions(e){Ee(e,r=>{this.TRACE_INIT(`${r.name} Rule Lookahead`,()=>{let{alternation:n,repetition:i,option:a,repetitionMandatory:s,repetitionMandatoryWithSeparator:l,repetitionWithSeparator:u}=PIe(r);Ee(n,h=>{let f=h.idx===0?"":h.idx;this.TRACE_INIT(`${Rs(h)}${f}`,()=>{let d=this.lookaheadStrategy.buildLookaheadForAlternation({prodOccurrence:h.idx,rule:r,maxLookahead:h.maxLookahead||this.maxLookahead,hasPredicates:h.hasPredicates,dynamicTokensEnabled:this.dynamicTokensEnabled}),p=JT(this.fullRuleNameToShort[r.name],256,h.idx);this.setLaFuncCache(p,d)})}),Ee(i,h=>{this.computeLookaheadFunc(r,h.idx,768,"Repetition",h.maxLookahead,Rs(h))}),Ee(a,h=>{this.computeLookaheadFunc(r,h.idx,512,"Option",h.maxLookahead,Rs(h))}),Ee(s,h=>{this.computeLookaheadFunc(r,h.idx,1024,"RepetitionMandatory",h.maxLookahead,Rs(h))}),Ee(l,h=>{this.computeLookaheadFunc(r,h.idx,1536,"RepetitionMandatoryWithSeparator",h.maxLookahead,Rs(h))}),Ee(u,h=>{this.computeLookaheadFunc(r,h.idx,1280,"RepetitionWithSeparator",h.maxLookahead,Rs(h))})})})}computeLookaheadFunc(e,r,n,i,a,s){this.TRACE_INIT(`${s}${r===0?"":r}`,()=>{let l=this.lookaheadStrategy.buildLookaheadForOptional({prodOccurrence:r,rule:e,maxLookahead:a||this.maxLookahead,dynamicTokensEnabled:this.dynamicTokensEnabled,prodType:i}),u=JT(this.fullRuleNameToShort[e.name],n,r);this.setLaFuncCache(u,l)})}getKeyForAutomaticLookahead(e,r){let n=this.getLastExplicitRuleShortName();return JT(n,e,r)}getLaFuncFromCache(e){return this.lookAheadFuncsCache.get(e)}setLaFuncCache(e,r){this.lookAheadFuncsCache.set(e,r)}},LN=class extends rs{static{o(this,"DslMethodsCollectorVisitor")}constructor(){super(...arguments),this.dslMethods={option:[],alternation:[],repetition:[],repetitionWithSeparator:[],repetitionMandatory:[],repetitionMandatoryWithSeparator:[]}}reset(){this.dslMethods={option:[],alternation:[],repetition:[],repetitionWithSeparator:[],repetitionMandatory:[],repetitionMandatoryWithSeparator:[]}}visitOption(e){this.dslMethods.option.push(e)}visitRepetitionWithSeparator(e){this.dslMethods.repetitionWithSeparator.push(e)}visitRepetitionMandatory(e){this.dslMethods.repetitionMandatory.push(e)}visitRepetitionMandatoryWithSeparator(e){this.dslMethods.repetitionMandatoryWithSeparator.push(e)}visitRepetition(e){this.dslMethods.repetition.push(e)}visitAlternation(e){this.dslMethods.alternation.push(e)}},tk=new LN;o(PIe,"collectMethods")});function NN(t,e){isNaN(t.startOffset)===!0?(t.startOffset=e.startOffset,t.endOffset=e.endOffset):t.endOffset{"use strict";o(NN,"setNodeLocationOnlyOffset");o(MN,"setNodeLocationFull");o(fae,"addTerminalToCst");o(dae,"addNoneTerminalToCst")});function IN(t,e){Object.defineProperty(t,BIe,{enumerable:!1,configurable:!0,writable:!1,value:e})}var BIe,mae=R(()=>{"use strict";BIe="name";o(IN,"defineNameProp")});function FIe(t,e){let r=Dr(t),n=r.length;for(let i=0;is.msg);throw Error(`Errors Detected in CST Visitor <${this.constructor.name}>: + ${a.join(` + +`).replace(/\n/g,` + `)}`)}},"validateVisitor")};return r.prototype=n,r.prototype.constructor=r,r._RULE_NAMES=e,r}function yae(t,e,r){let n=o(function(){},"derivedConstructor");IN(n,t+"BaseSemanticsWithDefaults");let i=Object.create(r.prototype);return Ee(e,a=>{i[a]=FIe}),n.prototype=i,n.prototype.constructor=n,n}function zIe(t,e){return GIe(t,e)}function GIe(t,e){let r=$r(e,i=>wi(t[i])===!1),n=qe(r,i=>({msg:`Missing visitor method: <${i}> on ${t.constructor.name} CST Visitor.`,type:ON.MISSING_METHOD,methodName:i}));return wc(n)}var ON,vae=R(()=>{"use strict";Pt();mae();o(FIe,"defaultVisit");o(gae,"createBaseSemanticVisitorConstructor");o(yae,"createBaseVisitorConstructorWithDefaults");(function(t){t[t.REDUNDANT_METHOD=0]="REDUNDANT_METHOD",t[t.MISSING_METHOD=1]="MISSING_METHOD"})(ON||(ON={}));o(zIe,"validateVisitor");o(GIe,"validateMissingCstMethods")});var sk,xae=R(()=>{"use strict";pae();Pt();vae();Ns();sk=class{static{o(this,"TreeBuilder")}initTreeBuilder(e){if(this.CST_STACK=[],this.outputCst=e.outputCst,this.nodeLocationTracking=Xe(e,"nodeLocationTracking")?e.nodeLocationTracking:is.nodeLocationTracking,!this.outputCst)this.cstInvocationStateUpdate=qn,this.cstFinallyStateUpdate=qn,this.cstPostTerminal=qn,this.cstPostNonTerminal=qn,this.cstPostRule=qn;else if(/full/i.test(this.nodeLocationTracking))this.recoveryEnabled?(this.setNodeLocationFromToken=MN,this.setNodeLocationFromNode=MN,this.cstPostRule=qn,this.setInitialNodeLocation=this.setInitialNodeLocationFullRecovery):(this.setNodeLocationFromToken=qn,this.setNodeLocationFromNode=qn,this.cstPostRule=this.cstPostRuleFull,this.setInitialNodeLocation=this.setInitialNodeLocationFullRegular);else if(/onlyOffset/i.test(this.nodeLocationTracking))this.recoveryEnabled?(this.setNodeLocationFromToken=NN,this.setNodeLocationFromNode=NN,this.cstPostRule=qn,this.setInitialNodeLocation=this.setInitialNodeLocationOnlyOffsetRecovery):(this.setNodeLocationFromToken=qn,this.setNodeLocationFromNode=qn,this.cstPostRule=this.cstPostRuleOnlyOffset,this.setInitialNodeLocation=this.setInitialNodeLocationOnlyOffsetRegular);else if(/none/i.test(this.nodeLocationTracking))this.setNodeLocationFromToken=qn,this.setNodeLocationFromNode=qn,this.cstPostRule=qn,this.setInitialNodeLocation=qn;else throw Error(`Invalid config option: "${e.nodeLocationTracking}"`)}setInitialNodeLocationOnlyOffsetRecovery(e){e.location={startOffset:NaN,endOffset:NaN}}setInitialNodeLocationOnlyOffsetRegular(e){e.location={startOffset:this.LA(1).startOffset,endOffset:NaN}}setInitialNodeLocationFullRecovery(e){e.location={startOffset:NaN,startLine:NaN,startColumn:NaN,endOffset:NaN,endLine:NaN,endColumn:NaN}}setInitialNodeLocationFullRegular(e){let r=this.LA(1);e.location={startOffset:r.startOffset,startLine:r.startLine,startColumn:r.startColumn,endOffset:NaN,endLine:NaN,endColumn:NaN}}cstInvocationStateUpdate(e){let r={name:e,children:Object.create(null)};this.setInitialNodeLocation(r),this.CST_STACK.push(r)}cstFinallyStateUpdate(){this.CST_STACK.pop()}cstPostRuleFull(e){let r=this.LA(0),n=e.location;n.startOffset<=r.startOffset?(n.endOffset=r.endOffset,n.endLine=r.endLine,n.endColumn=r.endColumn):(n.startOffset=NaN,n.startLine=NaN,n.startColumn=NaN)}cstPostRuleOnlyOffset(e){let r=this.LA(0),n=e.location;n.startOffset<=r.startOffset?n.endOffset=r.endOffset:n.startOffset=NaN}cstPostTerminal(e,r){let n=this.CST_STACK[this.CST_STACK.length-1];fae(n,r,e),this.setNodeLocationFromToken(n.location,r)}cstPostNonTerminal(e,r){let n=this.CST_STACK[this.CST_STACK.length-1];dae(n,r,e),this.setNodeLocationFromNode(n.location,e.location)}getBaseCstVisitorConstructor(){if(er(this.baseCstVisitorConstructor)){let e=gae(this.className,Dr(this.gastProductionsCache));return this.baseCstVisitorConstructor=e,e}return this.baseCstVisitorConstructor}getBaseCstVisitorConstructorWithDefaults(){if(er(this.baseCstVisitorWithDefaultsConstructor)){let e=yae(this.className,Dr(this.gastProductionsCache),this.getBaseCstVisitorConstructor());return this.baseCstVisitorWithDefaultsConstructor=e,e}return this.baseCstVisitorWithDefaultsConstructor}getLastExplicitRuleShortName(){let e=this.RULE_STACK;return e[e.length-1]}getPreviousExplicitRuleShortName(){let e=this.RULE_STACK;return e[e.length-2]}getLastExplicitRuleOccurrenceIndex(){let e=this.RULE_OCCURRENCE_STACK;return e[e.length-1]}}});var ok,bae=R(()=>{"use strict";Ns();ok=class{static{o(this,"LexerAdapter")}initLexerAdapter(){this.tokVector=[],this.tokVectorLength=0,this.currIdx=-1}set input(e){if(this.selfAnalysisDone!==!0)throw Error("Missing invocation at the end of the Parser's constructor.");this.reset(),this.tokVector=e,this.tokVectorLength=e.length}get input(){return this.tokVector}SKIP_TOKEN(){return this.currIdx<=this.tokVector.length-2?(this.consumeToken(),this.LA(1)):sg}LA(e){let r=this.currIdx+e;return r<0||this.tokVectorLength<=r?sg:this.tokVector[r]}consumeToken(){this.currIdx++}exportLexerState(){return this.currIdx}importLexerState(e){this.currIdx=e}resetLexerState(){this.currIdx=-1}moveToTerminatedState(){this.currIdx=this.tokVector.length-1}getLexerPosition(){return this.exportLexerState()}}});var lk,wae=R(()=>{"use strict";Pt();ag();Ns();Jm();f2();ns();lk=class{static{o(this,"RecognizerApi")}ACTION(e){return e.call(this)}consume(e,r,n){return this.consumeInternal(r,e,n)}subrule(e,r,n){return this.subruleInternal(r,e,n)}option(e,r){return this.optionInternal(r,e)}or(e,r){return this.orInternal(r,e)}many(e,r){return this.manyInternal(e,r)}atLeastOne(e,r){return this.atLeastOneInternal(e,r)}CONSUME(e,r){return this.consumeInternal(e,0,r)}CONSUME1(e,r){return this.consumeInternal(e,1,r)}CONSUME2(e,r){return this.consumeInternal(e,2,r)}CONSUME3(e,r){return this.consumeInternal(e,3,r)}CONSUME4(e,r){return this.consumeInternal(e,4,r)}CONSUME5(e,r){return this.consumeInternal(e,5,r)}CONSUME6(e,r){return this.consumeInternal(e,6,r)}CONSUME7(e,r){return this.consumeInternal(e,7,r)}CONSUME8(e,r){return this.consumeInternal(e,8,r)}CONSUME9(e,r){return this.consumeInternal(e,9,r)}SUBRULE(e,r){return this.subruleInternal(e,0,r)}SUBRULE1(e,r){return this.subruleInternal(e,1,r)}SUBRULE2(e,r){return this.subruleInternal(e,2,r)}SUBRULE3(e,r){return this.subruleInternal(e,3,r)}SUBRULE4(e,r){return this.subruleInternal(e,4,r)}SUBRULE5(e,r){return this.subruleInternal(e,5,r)}SUBRULE6(e,r){return this.subruleInternal(e,6,r)}SUBRULE7(e,r){return this.subruleInternal(e,7,r)}SUBRULE8(e,r){return this.subruleInternal(e,8,r)}SUBRULE9(e,r){return this.subruleInternal(e,9,r)}OPTION(e){return this.optionInternal(e,0)}OPTION1(e){return this.optionInternal(e,1)}OPTION2(e){return this.optionInternal(e,2)}OPTION3(e){return this.optionInternal(e,3)}OPTION4(e){return this.optionInternal(e,4)}OPTION5(e){return this.optionInternal(e,5)}OPTION6(e){return this.optionInternal(e,6)}OPTION7(e){return this.optionInternal(e,7)}OPTION8(e){return this.optionInternal(e,8)}OPTION9(e){return this.optionInternal(e,9)}OR(e){return this.orInternal(e,0)}OR1(e){return this.orInternal(e,1)}OR2(e){return this.orInternal(e,2)}OR3(e){return this.orInternal(e,3)}OR4(e){return this.orInternal(e,4)}OR5(e){return this.orInternal(e,5)}OR6(e){return this.orInternal(e,6)}OR7(e){return this.orInternal(e,7)}OR8(e){return this.orInternal(e,8)}OR9(e){return this.orInternal(e,9)}MANY(e){this.manyInternal(0,e)}MANY1(e){this.manyInternal(1,e)}MANY2(e){this.manyInternal(2,e)}MANY3(e){this.manyInternal(3,e)}MANY4(e){this.manyInternal(4,e)}MANY5(e){this.manyInternal(5,e)}MANY6(e){this.manyInternal(6,e)}MANY7(e){this.manyInternal(7,e)}MANY8(e){this.manyInternal(8,e)}MANY9(e){this.manyInternal(9,e)}MANY_SEP(e){this.manySepFirstInternal(0,e)}MANY_SEP1(e){this.manySepFirstInternal(1,e)}MANY_SEP2(e){this.manySepFirstInternal(2,e)}MANY_SEP3(e){this.manySepFirstInternal(3,e)}MANY_SEP4(e){this.manySepFirstInternal(4,e)}MANY_SEP5(e){this.manySepFirstInternal(5,e)}MANY_SEP6(e){this.manySepFirstInternal(6,e)}MANY_SEP7(e){this.manySepFirstInternal(7,e)}MANY_SEP8(e){this.manySepFirstInternal(8,e)}MANY_SEP9(e){this.manySepFirstInternal(9,e)}AT_LEAST_ONE(e){this.atLeastOneInternal(0,e)}AT_LEAST_ONE1(e){return this.atLeastOneInternal(1,e)}AT_LEAST_ONE2(e){this.atLeastOneInternal(2,e)}AT_LEAST_ONE3(e){this.atLeastOneInternal(3,e)}AT_LEAST_ONE4(e){this.atLeastOneInternal(4,e)}AT_LEAST_ONE5(e){this.atLeastOneInternal(5,e)}AT_LEAST_ONE6(e){this.atLeastOneInternal(6,e)}AT_LEAST_ONE7(e){this.atLeastOneInternal(7,e)}AT_LEAST_ONE8(e){this.atLeastOneInternal(8,e)}AT_LEAST_ONE9(e){this.atLeastOneInternal(9,e)}AT_LEAST_ONE_SEP(e){this.atLeastOneSepFirstInternal(0,e)}AT_LEAST_ONE_SEP1(e){this.atLeastOneSepFirstInternal(1,e)}AT_LEAST_ONE_SEP2(e){this.atLeastOneSepFirstInternal(2,e)}AT_LEAST_ONE_SEP3(e){this.atLeastOneSepFirstInternal(3,e)}AT_LEAST_ONE_SEP4(e){this.atLeastOneSepFirstInternal(4,e)}AT_LEAST_ONE_SEP5(e){this.atLeastOneSepFirstInternal(5,e)}AT_LEAST_ONE_SEP6(e){this.atLeastOneSepFirstInternal(6,e)}AT_LEAST_ONE_SEP7(e){this.atLeastOneSepFirstInternal(7,e)}AT_LEAST_ONE_SEP8(e){this.atLeastOneSepFirstInternal(8,e)}AT_LEAST_ONE_SEP9(e){this.atLeastOneSepFirstInternal(9,e)}RULE(e,r,n=og){if(Fn(this.definedRulesNames,e)){let s={message:Ol.buildDuplicateRuleNameError({topLevelRule:e,grammarName:this.className}),type:Pi.DUPLICATE_RULE_NAME,ruleName:e};this.definitionErrors.push(s)}this.definedRulesNames.push(e);let i=this.defineRule(e,r,n);return this[e]=i,i}OVERRIDE_RULE(e,r,n=og){let i=Jie(e,this.definedRulesNames,this.className);this.definitionErrors=this.definitionErrors.concat(i);let a=this.defineRule(e,r,n);return this[e]=a,a}BACKTRACK(e,r){return function(){this.isBackTrackingStack.push(1);let n=this.saveRecogState();try{return e.apply(this,r),!0}catch(i){if(nf(i))return!1;throw i}finally{this.reloadRecogState(n),this.isBackTrackingStack.pop()}}}getGAstProductions(){return this.gastProductionsCache}getSerializedGastProductions(){return NT(or(this.gastProductionsCache))}}});var ck,Tae=R(()=>{"use strict";Pt();ek();ag();ng();c2();Ns();AN();l0();s0();ck=class{static{o(this,"RecognizerEngine")}initRecognizerEngine(e,r){if(this.className=this.constructor.name,this.shortRuleNameToFull={},this.fullRuleNameToShort={},this.ruleShortNameIdx=256,this.tokenMatcher=Zm,this.subruleIdx=0,this.definedRulesNames=[],this.tokensMap={},this.isBackTrackingStack=[],this.RULE_STACK=[],this.RULE_OCCURRENCE_STACK=[],this.gastProductionsCache={},Xe(r,"serializedGrammar"))throw Error(`The Parser's configuration can no longer contain a property. + See: https://chevrotain.io/docs/changes/BREAKING_CHANGES.html#_6-0-0 + For Further details.`);if(wt(e)){if(Qt(e))throw Error(`A Token Vocabulary cannot be empty. + Note that the first argument for the parser constructor + is no longer a Token vector (since v4.0).`);if(typeof e[0].startOffset=="number")throw Error(`The Parser constructor no longer accepts a token vector as the first argument. + See: https://chevrotain.io/docs/changes/BREAKING_CHANGES.html#_4-0-0 + For Further details.`)}if(wt(e))this.tokensMap=Vr(e,(a,s)=>(a[s.name]=s,a),{});else if(Xe(e,"modes")&&Ia(Gr(or(e.modes)),Die)){let a=Gr(or(e.modes)),s=Pm(a);this.tokensMap=Vr(s,(l,u)=>(l[u.name]=u,l),{})}else if(pn(e))this.tokensMap=Qr(e);else throw new Error(" argument must be An Array of Token constructors, A dictionary of Token constructors or an IMultiModeLexerDefinition");this.tokensMap.EOF=fo;let n=Xe(e,"modes")?Gr(or(e.modes)):or(e),i=Ia(n,a=>Qt(a.categoryMatches));this.tokenMatcher=i?Zm:Bu,Fu(or(this.tokensMap))}defineRule(e,r,n){if(this.selfAnalysisDone)throw Error(`Grammar rule <${e}> may not be defined after the 'performSelfAnalysis' method has been called' +Make sure that all grammar rule definitions are done before 'performSelfAnalysis' is called.`);let i=Xe(n,"resyncEnabled")?n.resyncEnabled:og.resyncEnabled,a=Xe(n,"recoveryValueFunc")?n.recoveryValueFunc:og.recoveryValueFunc,s=this.ruleShortNameIdx<<12;this.ruleShortNameIdx++,this.shortRuleNameToFull[s]=e,this.fullRuleNameToShort[e]=s;let l;return this.outputCst===!0?l=o(function(...f){try{this.ruleInvocationStateUpdate(s,e,this.subruleIdx),r.apply(this,f);let d=this.CST_STACK[this.CST_STACK.length-1];return this.cstPostRule(d),d}catch(d){return this.invokeRuleCatch(d,i,a)}finally{this.ruleFinallyStateUpdate()}},"invokeRuleWithTry"):l=o(function(...f){try{return this.ruleInvocationStateUpdate(s,e,this.subruleIdx),r.apply(this,f)}catch(d){return this.invokeRuleCatch(d,i,a)}finally{this.ruleFinallyStateUpdate()}},"invokeRuleWithTryCst"),Object.assign(l,{ruleName:e,originalGrammarAction:r})}invokeRuleCatch(e,r,n){let i=this.RULE_STACK.length===1,a=r&&!this.isBackTracking()&&this.recoveryEnabled;if(nf(e)){let s=e;if(a){let l=this.findReSyncTokenType();if(this.isInCurrentRuleReSyncSet(l))if(s.resyncedTokens=this.reSyncTo(l),this.outputCst){let u=this.CST_STACK[this.CST_STACK.length-1];return u.recoveredNode=!0,u}else return n(e);else{if(this.outputCst){let u=this.CST_STACK[this.CST_STACK.length-1];u.recoveredNode=!0,s.partialCstResult=u}throw s}}else{if(i)return this.moveToTerminatedState(),n(e);throw s}}else throw e}optionInternal(e,r){let n=this.getKeyForAutomaticLookahead(512,r);return this.optionInternalLogic(e,r,n)}optionInternalLogic(e,r,n){let i=this.getLaFuncFromCache(n),a;if(typeof e!="function"){a=e.DEF;let s=e.GATE;if(s!==void 0){let l=i;i=o(()=>s.call(this)&&l.call(this),"lookAheadFunc")}}else a=e;if(i.call(this)===!0)return a.call(this)}atLeastOneInternal(e,r){let n=this.getKeyForAutomaticLookahead(1024,e);return this.atLeastOneInternalLogic(e,r,n)}atLeastOneInternalLogic(e,r,n){let i=this.getLaFuncFromCache(n),a;if(typeof r!="function"){a=r.DEF;let s=r.GATE;if(s!==void 0){let l=i;i=o(()=>s.call(this)&&l.call(this),"lookAheadFunc")}}else a=r;if(i.call(this)===!0){let s=this.doSingleRepetition(a);for(;i.call(this)===!0&&s===!0;)s=this.doSingleRepetition(a)}else throw this.raiseEarlyExitException(e,$n.REPETITION_MANDATORY,r.ERR_MSG);this.attemptInRepetitionRecovery(this.atLeastOneInternal,[e,r],i,1024,e,YT)}atLeastOneSepFirstInternal(e,r){let n=this.getKeyForAutomaticLookahead(1536,e);this.atLeastOneSepFirstInternalLogic(e,r,n)}atLeastOneSepFirstInternalLogic(e,r,n){let i=r.DEF,a=r.SEP;if(this.getLaFuncFromCache(n).call(this)===!0){i.call(this);let l=o(()=>this.tokenMatcher(this.LA(1),a),"separatorLookAheadFunc");for(;this.tokenMatcher(this.LA(1),a)===!0;)this.CONSUME(a),i.call(this);this.attemptInRepetitionRecovery(this.repetitionSepSecondInternal,[e,a,l,i,l2],l,1536,e,l2)}else throw this.raiseEarlyExitException(e,$n.REPETITION_MANDATORY_WITH_SEPARATOR,r.ERR_MSG)}manyInternal(e,r){let n=this.getKeyForAutomaticLookahead(768,e);return this.manyInternalLogic(e,r,n)}manyInternalLogic(e,r,n){let i=this.getLaFuncFromCache(n),a;if(typeof r!="function"){a=r.DEF;let l=r.GATE;if(l!==void 0){let u=i;i=o(()=>l.call(this)&&u.call(this),"lookaheadFunction")}}else a=r;let s=!0;for(;i.call(this)===!0&&s===!0;)s=this.doSingleRepetition(a);this.attemptInRepetitionRecovery(this.manyInternal,[e,r],i,768,e,HT,s)}manySepFirstInternal(e,r){let n=this.getKeyForAutomaticLookahead(1280,e);this.manySepFirstInternalLogic(e,r,n)}manySepFirstInternalLogic(e,r,n){let i=r.DEF,a=r.SEP;if(this.getLaFuncFromCache(n).call(this)===!0){i.call(this);let l=o(()=>this.tokenMatcher(this.LA(1),a),"separatorLookAheadFunc");for(;this.tokenMatcher(this.LA(1),a)===!0;)this.CONSUME(a),i.call(this);this.attemptInRepetitionRecovery(this.repetitionSepSecondInternal,[e,a,l,i,o2],l,1280,e,o2)}}repetitionSepSecondInternal(e,r,n,i,a){for(;n();)this.CONSUME(r),i.call(this);this.attemptInRepetitionRecovery(this.repetitionSepSecondInternal,[e,r,n,i,a],n,1536,e,a)}doSingleRepetition(e){let r=this.getLexerPosition();return e.call(this),this.getLexerPosition()>r}orInternal(e,r){let n=this.getKeyForAutomaticLookahead(256,r),i=wt(e)?e:e.DEF,s=this.getLaFuncFromCache(n).call(this,i);if(s!==void 0)return i[s].ALT.call(this);this.raiseNoAltException(r,e.ERR_MSG)}ruleFinallyStateUpdate(){if(this.RULE_STACK.pop(),this.RULE_OCCURRENCE_STACK.pop(),this.cstFinallyStateUpdate(),this.RULE_STACK.length===0&&this.isAtEndOfInput()===!1){let e=this.LA(1),r=this.errorMessageProvider.buildNotAllInputParsedMessage({firstRedundant:e,ruleName:this.getCurrRuleFullName()});this.SAVE_ERROR(new p2(r,e))}}subruleInternal(e,r,n){let i;try{let a=n!==void 0?n.ARGS:void 0;return this.subruleIdx=r,i=e.apply(this,a),this.cstPostNonTerminal(i,n!==void 0&&n.LABEL!==void 0?n.LABEL:e.ruleName),i}catch(a){throw this.subruleInternalError(a,n,e.ruleName)}}subruleInternalError(e,r,n){throw nf(e)&&e.partialCstResult!==void 0&&(this.cstPostNonTerminal(e.partialCstResult,r!==void 0&&r.LABEL!==void 0?r.LABEL:n),delete e.partialCstResult),e}consumeInternal(e,r,n){let i;try{let a=this.LA(1);this.tokenMatcher(a,e)===!0?(this.consumeToken(),i=a):this.consumeInternalError(e,a,n)}catch(a){i=this.consumeInternalRecovery(e,r,a)}return this.cstPostTerminal(n!==void 0&&n.LABEL!==void 0?n.LABEL:e.name,i),i}consumeInternalError(e,r,n){let i,a=this.LA(0);throw n!==void 0&&n.ERR_MSG?i=n.ERR_MSG:i=this.errorMessageProvider.buildMismatchTokenMessage({expected:e,actual:r,previous:a,ruleName:this.getCurrRuleFullName()}),this.SAVE_ERROR(new c0(i,r,a))}consumeInternalRecovery(e,r,n){if(this.recoveryEnabled&&n.name==="MismatchedTokenException"&&!this.isBackTracking()){let i=this.getFollowsForInRuleRecovery(e,r);try{return this.tryInRuleRecovery(e,i)}catch(a){throw a.name===SN?n:a}}else throw n}saveRecogState(){let e=this.errors,r=Qr(this.RULE_STACK);return{errors:e,lexerState:this.exportLexerState(),RULE_STACK:r,CST_STACK:this.CST_STACK}}reloadRecogState(e){this.errors=e.errors,this.importLexerState(e.lexerState),this.RULE_STACK=e.RULE_STACK}ruleInvocationStateUpdate(e,r,n){this.RULE_OCCURRENCE_STACK.push(n),this.RULE_STACK.push(e),this.cstInvocationStateUpdate(r)}isBackTracking(){return this.isBackTrackingStack.length!==0}getCurrRuleFullName(){let e=this.getLastExplicitRuleShortName();return this.shortRuleNameToFull[e]}shortRuleNameToFullName(e){return this.shortRuleNameToFull[e]}isAtEndOfInput(){return this.tokenMatcher(this.LA(1),fo)}reset(){this.resetLexerState(),this.subruleIdx=0,this.isBackTrackingStack=[],this.errors=[],this.RULE_STACK=[],this.CST_STACK=[],this.RULE_OCCURRENCE_STACK=[]}}});var uk,kae=R(()=>{"use strict";ag();Pt();ng();Ns();uk=class{static{o(this,"ErrorHandler")}initErrorHandler(e){this._errors=[],this.errorMessageProvider=Xe(e,"errorMessageProvider")?e.errorMessageProvider:is.errorMessageProvider}SAVE_ERROR(e){if(nf(e))return e.context={ruleStack:this.getHumanReadableRuleStack(),ruleOccurrenceStack:Qr(this.RULE_OCCURRENCE_STACK)},this._errors.push(e),e;throw Error("Trying to save an Error which is not a RecognitionException")}get errors(){return Qr(this._errors)}set errors(e){this._errors=e}raiseEarlyExitException(e,r,n){let i=this.getCurrRuleFullName(),a=this.getGAstProductions()[i],l=rg(e,a,r,this.maxLookahead)[0],u=[];for(let f=1;f<=this.maxLookahead;f++)u.push(this.LA(f));let h=this.errorMessageProvider.buildEarlyExitMessage({expectedIterationPaths:l,actual:u,previous:this.LA(0),customUserDescription:n,ruleName:i});throw this.SAVE_ERROR(new m2(h,this.LA(1),this.LA(0)))}raiseNoAltException(e,r){let n=this.getCurrRuleFullName(),i=this.getGAstProductions()[n],a=tg(e,i,this.maxLookahead),s=[];for(let h=1;h<=this.maxLookahead;h++)s.push(this.LA(h));let l=this.LA(0),u=this.errorMessageProvider.buildNoViableAltMessage({expectedPathsPerAlt:a,actual:s,previous:l,customUserDescription:r,ruleName:this.getCurrRuleFullName()});throw this.SAVE_ERROR(new d2(u,this.LA(1),l))}}});var hk,Eae=R(()=>{"use strict";c2();Pt();hk=class{static{o(this,"ContentAssist")}initContentAssist(){}computeContentAssist(e,r){let n=this.gastProductionsCache[e];if(er(n))throw Error(`Rule ->${e}<- does not exist in this grammar.`);return qT([n],r,this.tokenMatcher,this.maxLookahead)}getNextPossibleTokenTypes(e){let r=na(e.ruleStack),i=this.getGAstProductions()[r];return new UT(i,e).startWalking()}}});function y2(t,e,r,n=!1){dk(r);let i=ma(this.recordingProdStack),a=wi(e)?e:e.DEF,s=new t({definition:[],idx:r});return n&&(s.separator=e.SEP),Xe(e,"MAX_LOOKAHEAD")&&(s.maxLookahead=e.MAX_LOOKAHEAD),this.recordingProdStack.push(s),a.call(this),i.definition.push(s),this.recordingProdStack.pop(),pk}function UIe(t,e){dk(e);let r=ma(this.recordingProdStack),n=wt(t)===!1,i=n===!1?t:t.DEF,a=new gn({definition:[],idx:e,ignoreAmbiguities:n&&t.IGNORE_AMBIGUITIES===!0});Xe(t,"MAX_LOOKAHEAD")&&(a.maxLookahead=t.MAX_LOOKAHEAD);let s=Nv(i,l=>wi(l.GATE));return a.hasPredicates=s,r.definition.push(a),Ee(i,l=>{let u=new Sn({definition:[]});a.definition.push(u),Xe(l,"IGNORE_AMBIGUITIES")?u.ignoreAmbiguities=l.IGNORE_AMBIGUITIES:Xe(l,"GATE")&&(u.ignoreAmbiguities=!0),this.recordingProdStack.push(u),l.ALT.call(this),this.recordingProdStack.pop()}),pk}function Aae(t){return t===0?"":`${t}`}function dk(t){if(t<0||t>Sae){let e=new Error(`Invalid DSL Method idx value: <${t}> + Idx value must be a none negative value smaller than ${Sae+1}`);throw e.KNOWN_RECORDER_ERROR=!0,e}}var pk,Cae,Sae,_ae,Lae,VIe,fk,Dae=R(()=>{"use strict";Pt();ns();i2();s0();l0();Ns();ek();pk={description:"This Object indicates the Parser is during Recording Phase"};Object.freeze(pk);Cae=!0,Sae=Math.pow(2,8)-1,_ae=VT({name:"RECORDING_PHASE_TOKEN",pattern:ni.NA});Fu([_ae]);Lae=o0(_ae,`This IToken indicates the Parser is in Recording Phase + See: https://chevrotain.io/docs/guide/internals.html#grammar-recording for details`,-1,-1,-1,-1,-1,-1);Object.freeze(Lae);VIe={name:`This CSTNode indicates the Parser is in Recording Phase + See: https://chevrotain.io/docs/guide/internals.html#grammar-recording for details`,children:{}},fk=class{static{o(this,"GastRecorder")}initGastRecorder(e){this.recordingProdStack=[],this.RECORDING_PHASE=!1}enableRecording(){this.RECORDING_PHASE=!0,this.TRACE_INIT("Enable Recording",()=>{for(let e=0;e<10;e++){let r=e>0?e:"";this[`CONSUME${r}`]=function(n,i){return this.consumeInternalRecord(n,e,i)},this[`SUBRULE${r}`]=function(n,i){return this.subruleInternalRecord(n,e,i)},this[`OPTION${r}`]=function(n){return this.optionInternalRecord(n,e)},this[`OR${r}`]=function(n){return this.orInternalRecord(n,e)},this[`MANY${r}`]=function(n){this.manyInternalRecord(e,n)},this[`MANY_SEP${r}`]=function(n){this.manySepFirstInternalRecord(e,n)},this[`AT_LEAST_ONE${r}`]=function(n){this.atLeastOneInternalRecord(e,n)},this[`AT_LEAST_ONE_SEP${r}`]=function(n){this.atLeastOneSepFirstInternalRecord(e,n)}}this.consume=function(e,r,n){return this.consumeInternalRecord(r,e,n)},this.subrule=function(e,r,n){return this.subruleInternalRecord(r,e,n)},this.option=function(e,r){return this.optionInternalRecord(r,e)},this.or=function(e,r){return this.orInternalRecord(r,e)},this.many=function(e,r){this.manyInternalRecord(e,r)},this.atLeastOne=function(e,r){this.atLeastOneInternalRecord(e,r)},this.ACTION=this.ACTION_RECORD,this.BACKTRACK=this.BACKTRACK_RECORD,this.LA=this.LA_RECORD})}disableRecording(){this.RECORDING_PHASE=!1,this.TRACE_INIT("Deleting Recording methods",()=>{let e=this;for(let r=0;r<10;r++){let n=r>0?r:"";delete e[`CONSUME${n}`],delete e[`SUBRULE${n}`],delete e[`OPTION${n}`],delete e[`OR${n}`],delete e[`MANY${n}`],delete e[`MANY_SEP${n}`],delete e[`AT_LEAST_ONE${n}`],delete e[`AT_LEAST_ONE_SEP${n}`]}delete e.consume,delete e.subrule,delete e.option,delete e.or,delete e.many,delete e.atLeastOne,delete e.ACTION,delete e.BACKTRACK,delete e.LA})}ACTION_RECORD(e){}BACKTRACK_RECORD(e,r){return()=>!0}LA_RECORD(e){return sg}topLevelRuleRecord(e,r){try{let n=new ts({definition:[],name:e});return n.name=e,this.recordingProdStack.push(n),r.call(this),this.recordingProdStack.pop(),n}catch(n){if(n.KNOWN_RECORDER_ERROR!==!0)try{n.message=n.message+` + This error was thrown during the "grammar recording phase" For more info see: + https://chevrotain.io/docs/guide/internals.html#grammar-recording`}catch{throw n}throw n}}optionInternalRecord(e,r){return y2.call(this,Jr,e,r)}atLeastOneInternalRecord(e,r){y2.call(this,An,r,e)}atLeastOneSepFirstInternalRecord(e,r){y2.call(this,_n,r,e,Cae)}manyInternalRecord(e,r){y2.call(this,br,r,e)}manySepFirstInternalRecord(e,r){y2.call(this,mn,r,e,Cae)}orInternalRecord(e,r){return UIe.call(this,e,r)}subruleInternalRecord(e,r,n){if(dk(r),!e||Xe(e,"ruleName")===!1){let l=new Error(` argument is invalid expecting a Parser method reference but got: <${JSON.stringify(e)}> + inside top level rule: <${this.recordingProdStack[0].name}>`);throw l.KNOWN_RECORDER_ERROR=!0,l}let i=ma(this.recordingProdStack),a=e.ruleName,s=new Zr({idx:r,nonTerminalName:a,label:n?.LABEL,referencedRule:void 0});return i.definition.push(s),this.outputCst?VIe:pk}consumeInternalRecord(e,r,n){if(dk(r),!dN(e)){let s=new Error(` argument is invalid expecting a TokenType reference but got: <${JSON.stringify(e)}> + inside top level rule: <${this.recordingProdStack[0].name}>`);throw s.KNOWN_RECORDER_ERROR=!0,s}let i=ma(this.recordingProdStack),a=new fr({idx:r,terminalType:e,label:n?.LABEL});return i.definition.push(a),Lae}};o(y2,"recordProd");o(UIe,"recordOrProd");o(Aae,"getIdxSuffix");o(dk,"assertMethodIdxIsValid")});var mk,Rae=R(()=>{"use strict";Pt();qm();Ns();mk=class{static{o(this,"PerformanceTracer")}initPerformanceTracer(e){if(Xe(e,"traceInitPerf")){let r=e.traceInitPerf,n=typeof r=="number";this.traceInitMaxIdent=n?r:1/0,this.traceInitPerf=n?r>0:r}else this.traceInitMaxIdent=0,this.traceInitPerf=is.traceInitPerf;this.traceInitIndent=-1}TRACE_INIT(e,r){if(this.traceInitPerf===!0){this.traceInitIndent++;let n=new Array(this.traceInitIndent+1).join(" ");this.traceInitIndent <${e}>`);let{time:i,value:a}=t2(r),s=i>10?console.warn:console.log;return this.traceInitIndent time: ${i}ms`),this.traceInitIndent--,a}else return r()}}});function Nae(t,e){e.forEach(r=>{let n=r.prototype;Object.getOwnPropertyNames(n).forEach(i=>{if(i==="constructor")return;let a=Object.getOwnPropertyDescriptor(n,i);a&&(a.get||a.set)?Object.defineProperty(t.prototype,i,a):t.prototype[i]=r.prototype[i]})})}var Mae=R(()=>{"use strict";o(Nae,"applyMixins")});function gk(t=void 0){return function(){return t}}var sg,is,og,Pi,v2,x2,Ns=R(()=>{"use strict";Pt();qm();cie();l0();Jm();aae();AN();hae();xae();bae();wae();Tae();kae();Eae();Dae();Rae();Mae();f2();sg=o0(fo,"",NaN,NaN,NaN,NaN,NaN,NaN);Object.freeze(sg);is=Object.freeze({recoveryEnabled:!1,maxLookahead:3,dynamicTokensEnabled:!1,outputCst:!0,errorMessageProvider:Gu,nodeLocationTracking:"none",traceInitPerf:!1,skipValidations:!1}),og=Object.freeze({recoveryValueFunc:o(()=>{},"recoveryValueFunc"),resyncEnabled:!0});(function(t){t[t.INVALID_RULE_NAME=0]="INVALID_RULE_NAME",t[t.DUPLICATE_RULE_NAME=1]="DUPLICATE_RULE_NAME",t[t.INVALID_RULE_OVERRIDE=2]="INVALID_RULE_OVERRIDE",t[t.DUPLICATE_PRODUCTIONS=3]="DUPLICATE_PRODUCTIONS",t[t.UNRESOLVED_SUBRULE_REF=4]="UNRESOLVED_SUBRULE_REF",t[t.LEFT_RECURSION=5]="LEFT_RECURSION",t[t.NONE_LAST_EMPTY_ALT=6]="NONE_LAST_EMPTY_ALT",t[t.AMBIGUOUS_ALTS=7]="AMBIGUOUS_ALTS",t[t.CONFLICT_TOKENS_RULES_NAMESPACE=8]="CONFLICT_TOKENS_RULES_NAMESPACE",t[t.INVALID_TOKEN_NAME=9]="INVALID_TOKEN_NAME",t[t.NO_NON_EMPTY_LOOKAHEAD=10]="NO_NON_EMPTY_LOOKAHEAD",t[t.AMBIGUOUS_PREFIX_ALTS=11]="AMBIGUOUS_PREFIX_ALTS",t[t.TOO_MANY_ALTS=12]="TOO_MANY_ALTS",t[t.CUSTOM_LOOKAHEAD_VALIDATION=13]="CUSTOM_LOOKAHEAD_VALIDATION"})(Pi||(Pi={}));o(gk,"EMPTY_ALT");v2=class t{static{o(this,"Parser")}static performSelfAnalysis(e){throw Error("The **static** `performSelfAnalysis` method has been deprecated. \nUse the **instance** method with the same name instead.")}performSelfAnalysis(){this.TRACE_INIT("performSelfAnalysis",()=>{let e;this.selfAnalysisDone=!0;let r=this.className;this.TRACE_INIT("toFastProps",()=>{r2(this)}),this.TRACE_INIT("Grammar Recording",()=>{try{this.enableRecording(),Ee(this.definedRulesNames,i=>{let s=this[i].originalGrammarAction,l;this.TRACE_INIT(`${i} Rule`,()=>{l=this.topLevelRuleRecord(i,s)}),this.gastProductionsCache[i]=l})}finally{this.disableRecording()}});let n=[];if(this.TRACE_INIT("Grammar Resolving",()=>{n=nae({rules:or(this.gastProductionsCache)}),this.definitionErrors=this.definitionErrors.concat(n)}),this.TRACE_INIT("Grammar Validations",()=>{if(Qt(n)&&this.skipValidations===!1){let i=iae({rules:or(this.gastProductionsCache),tokenTypes:or(this.tokensMap),errMsgProvider:Ol,grammarName:r}),a=Kie({lookaheadStrategy:this.lookaheadStrategy,rules:or(this.gastProductionsCache),tokenTypes:or(this.tokensMap),grammarName:r});this.definitionErrors=this.definitionErrors.concat(i,a)}}),Qt(this.definitionErrors)&&(this.recoveryEnabled&&this.TRACE_INIT("computeAllProdsFollows",()=>{let i=lie(or(this.gastProductionsCache));this.resyncFollows=i}),this.TRACE_INIT("ComputeLookaheadFunctions",()=>{var i,a;(a=(i=this.lookaheadStrategy).initialize)===null||a===void 0||a.call(i,{rules:or(this.gastProductionsCache)}),this.preComputeLookaheadFunctions(or(this.gastProductionsCache))})),!t.DEFER_DEFINITION_ERRORS_HANDLING&&!Qt(this.definitionErrors))throw e=qe(this.definitionErrors,i=>i.message),new Error(`Parser Definition Errors detected: + ${e.join(` +------------------------------- +`)}`)})}constructor(e,r){this.definitionErrors=[],this.selfAnalysisDone=!1;let n=this;if(n.initErrorHandler(r),n.initLexerAdapter(),n.initLooksAhead(r),n.initRecognizerEngine(e,r),n.initRecoverable(r),n.initTreeBuilder(r),n.initContentAssist(),n.initGastRecorder(r),n.initPerformanceTracer(r),Xe(r,"ignoredIssues"))throw new Error(`The IParserConfig property has been deprecated. + Please use the flag on the relevant DSL method instead. + See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#IGNORING_AMBIGUITIES + For further details.`);this.skipValidations=Xe(r,"skipValidations")?r.skipValidations:is.skipValidations}};v2.DEFER_DEFINITION_ERRORS_HANDLING=!1;Nae(v2,[ZT,rk,sk,ok,ck,lk,uk,hk,fk,mk]);x2=class extends v2{static{o(this,"EmbeddedActionsParser")}constructor(e,r=is){let n=Qr(r);n.outputCst=!1,super(e,n)}}});var Iae=R(()=>{"use strict";ns()});var Oae=R(()=>{"use strict"});var Pae=R(()=>{"use strict";Iae();Oae()});var Bae=R(()=>{"use strict";tN()});var u0=R(()=>{"use strict";tN();Ns();i2();l0();ng();_N();Jm();ag();mN();ns();ns();Pae();Bae()});function h0(t,e,r){return`${t.name}_${e}_${r}`}function $ae(t){let e={decisionMap:{},decisionStates:[],ruleToStartState:new Map,ruleToStopState:new Map,states:[]};KIe(e,t);let r=t.length;for(let n=0;nVae(t,e,s));return hg(t,e,n,r,...i)}function rOe(t,e,r){let n=ia(t,e,r,{type:af});sf(t,n);let i=hg(t,e,n,r,f0(t,e,r));return nOe(t,e,r,i)}function f0(t,e,r){let n=$r(qe(r.definition,i=>Vae(t,e,i)),i=>i!==void 0);return n.length===1?n[0]:n.length===0?void 0:aOe(t,n)}function Uae(t,e,r,n,i){let a=n.left,s=n.right,l=ia(t,e,r,{type:jIe});sf(t,l);let u=ia(t,e,r,{type:Gae});return a.loopback=l,u.loopback=l,t.decisionMap[h0(e,i?"RepetitionMandatoryWithSeparator":"RepetitionMandatory",r.idx)]=l,Ei(s,l),i===void 0?(Ei(l,a),Ei(l,u)):(Ei(l,u),Ei(l,i.left),Ei(i.right,a)),{left:a,right:u}}function Hae(t,e,r,n,i){let a=n.left,s=n.right,l=ia(t,e,r,{type:XIe});sf(t,l);let u=ia(t,e,r,{type:Gae}),h=ia(t,e,r,{type:qIe});return l.loopback=h,u.loopback=h,Ei(l,a),Ei(l,u),Ei(s,h),i!==void 0?(Ei(h,u),Ei(h,i.left),Ei(i.right,a)):Ei(h,l),t.decisionMap[h0(e,i?"RepetitionWithSeparator":"Repetition",r.idx)]=l,{left:l,right:u}}function nOe(t,e,r,n){let i=n.left,a=n.right;return Ei(i,a),t.decisionMap[h0(e,"Option",r.idx)]=i,n}function sf(t,e){return t.decisionStates.push(e),e.decision=t.decisionStates.length-1,e.decision}function hg(t,e,r,n,...i){let a=ia(t,e,n,{type:WIe,start:r});r.end=a;for(let l of i)l!==void 0?(Ei(r,l.left),Ei(l.right,a)):Ei(r,a);let s={left:r,right:a};return t.decisionMap[h0(e,iOe(n),n.idx)]=r,s}function iOe(t){if(t instanceof gn)return"Alternation";if(t instanceof Jr)return"Option";if(t instanceof br)return"Repetition";if(t instanceof mn)return"RepetitionWithSeparator";if(t instanceof An)return"RepetitionMandatory";if(t instanceof _n)return"RepetitionMandatoryWithSeparator";throw new Error("Invalid production type encountered")}function aOe(t,e){let r=e.length;for(let a=0;a{"use strict";Mm();LL();u0();o(h0,"buildATNKey");af=1,YIe=2,Fae=4,zae=5,ug=7,WIe=8,qIe=9,XIe=10,jIe=11,Gae=12,b2=class{static{o(this,"AbstractTransition")}constructor(e){this.target=e}isEpsilon(){return!1}},lg=class extends b2{static{o(this,"AtomTransition")}constructor(e,r){super(e),this.tokenType=r}},w2=class extends b2{static{o(this,"EpsilonTransition")}constructor(e){super(e)}isEpsilon(){return!0}},cg=class extends b2{static{o(this,"RuleTransition")}constructor(e,r,n){super(e),this.rule=r,this.followState=n}isEpsilon(){return!0}};o($ae,"createATN");o(KIe,"createRuleStartAndStopATNStates");o(Vae,"atom");o(QIe,"repetition");o(ZIe,"repetitionSep");o(JIe,"repetitionMandatory");o(eOe,"repetitionMandatorySep");o(tOe,"alternation");o(rOe,"option");o(f0,"block");o(Uae,"plus");o(Hae,"star");o(nOe,"optional");o(sf,"defineDecisionState");o(hg,"makeAlts");o(iOe,"getProdType");o(aOe,"makeBlock");o(BN,"tokenRef");o(sOe,"ruleRef");o(oOe,"buildRuleHandle");o(Ei,"epsilon");o(ia,"newState");o(FN,"addTransition");o(lOe,"removeState")});function zN(t,e=!0){return`${e?`a${t.alt}`:""}s${t.state.stateNumber}:${t.stack.map(r=>r.stateNumber.toString()).join("_")}`}var T2,fg,Wae=R(()=>{"use strict";Mm();T2={},fg=class{static{o(this,"ATNConfigSet")}constructor(){this.map={},this.configs=[]}get size(){return this.configs.length}finalize(){this.map={}}add(e){let r=zN(e);r in this.map||(this.map[r]=this.configs.length,this.configs.push(e))}get elements(){return this.configs}get alts(){return qe(this.configs,e=>e.alt)}get key(){let e="";for(let r in this.map)e+=r+":";return e}};o(zN,"getATNConfigKey")});function cOe(t,e){let r={};return n=>{let i=n.toString(),a=r[i];return a!==void 0||(a={atnStartState:t,decision:e,states:{}},r[i]=a),a}}function Xae(t,e=!0){let r=new Set;for(let n of t){let i=new Set;for(let a of n){if(a===void 0){if(e)break;return!1}let s=[a.tokenTypeIdx].concat(a.categoryMatches);for(let l of s)if(r.has(l)){if(!i.has(l))return!1}else r.add(l),i.add(l)}}return!0}function uOe(t){let e=t.decisionStates.length,r=Array(e);for(let n=0;nzu(i)).join(", "),r=t.production.idx===0?"":t.production.idx,n=`Ambiguous Alternatives Detected: <${t.ambiguityIndices.join(", ")}> in <${mOe(t.production)}${r}> inside <${t.topLevelRule.name}> Rule, +<${e}> may appears as a prefix path in all these alternatives. +`;return n=n+`See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#AMBIGUOUS_ALTERNATIVES +For Further details.`,n}function mOe(t){if(t instanceof Zr)return"SUBRULE";if(t instanceof Jr)return"OPTION";if(t instanceof gn)return"OR";if(t instanceof An)return"AT_LEAST_ONE";if(t instanceof _n)return"AT_LEAST_ONE_SEP";if(t instanceof mn)return"MANY_SEP";if(t instanceof br)return"MANY";if(t instanceof fr)return"CONSUME";throw Error("non exhaustive match")}function gOe(t,e,r){let n=ga(e.configs.elements,a=>a.state.transitions),i=nte(n.filter(a=>a instanceof lg).map(a=>a.tokenType),a=>a.tokenTypeIdx);return{actualToken:r,possibleTokenTypes:i,tokenPath:t}}function yOe(t,e){return t.edges[e.tokenTypeIdx]}function vOe(t,e,r){let n=new fg,i=[];for(let s of t.elements){if(r.is(s.alt)===!1)continue;if(s.state.type===ug){i.push(s);continue}let l=s.state.transitions.length;for(let u=0;u0&&!kOe(a))for(let s of i)a.add(s);return a}function xOe(t,e){if(t instanceof lg&&s2(e,t.tokenType))return t.target}function bOe(t,e){let r;for(let n of t.elements)if(e.is(n.alt)===!0){if(r===void 0)r=n.alt;else if(r!==n.alt)return}return r}function Kae(t){return{configs:t,edges:{},isAcceptState:!1,prediction:-1}}function jae(t,e,r,n){return n=Qae(t,n),e.edges[r.tokenTypeIdx]=n,n}function Qae(t,e){if(e===T2)return e;let r=e.configs.key,n=t.states[r];return n!==void 0?n:(e.configs.finalize(),t.states[r]=e,e)}function wOe(t){let e=new fg,r=t.transitions.length;for(let n=0;n0){let i=[...t.stack],s={state:i.pop(),alt:t.alt,stack:i};vk(s,e)}else e.add(t);return}r.epsilonOnlyTransitions||e.add(t);let n=r.transitions.length;for(let i=0;i1)return!0;return!1}function _Oe(t){for(let e of Array.from(t.values()))if(Object.keys(e).length===1)return!0;return!1}var yk,qae,k2,Zae=R(()=>{"use strict";u0();Yae();Wae();BL();RL();ite();Mm();fw();$w();Ww();$L();o(cOe,"createDFACache");yk=class{static{o(this,"PredicateSet")}constructor(){this.predicates=[]}is(e){return e>=this.predicates.length||this.predicates[e]}set(e,r){this.predicates[e]=r}toString(){let e="",r=this.predicates.length;for(let n=0;nconsole.log(n)}initialize(e){this.atn=$ae(e.rules),this.dfas=uOe(this.atn)}validateAmbiguousAlternationAlternatives(){return[]}validateEmptyOrAlternatives(){return[]}buildLookaheadForAlternation(e){let{prodOccurrence:r,rule:n,hasPredicates:i,dynamicTokensEnabled:a}=e,s=this.dfas,l=this.logging,u=h0(n,"Alternation",r),f=this.atn.decisionMap[u].decision,d=qe(jT({maxLookahead:1,occurrence:r,prodType:"Alternation",rule:n}),p=>qe(p,m=>m[0]));if(Xae(d,!1)&&!a){let p=Vr(d,(m,g,y)=>(Ee(g,v=>{v&&(m[v.tokenTypeIdx]=y,Ee(v.categoryMatches,x=>{m[x]=y}))}),m),{});return i?function(m){var g;let y=this.LA(1),v=p[y.tokenTypeIdx];if(m!==void 0&&v!==void 0){let x=(g=m[v])===null||g===void 0?void 0:g.GATE;if(x!==void 0&&x.call(this)===!1)return}return v}:function(){let m=this.LA(1);return p[m.tokenTypeIdx]}}else return i?function(p){let m=new yk,g=p===void 0?0:p.length;for(let v=0;vqe(p,m=>m[0]));if(Xae(d)&&d[0][0]&&!a){let p=d[0],m=Gr(p);if(m.length===1&&Qt(m[0].categoryMatches)){let y=m[0].tokenTypeIdx;return function(){return this.LA(1).tokenTypeIdx===y}}else{let g=Vr(m,(y,v)=>(v!==void 0&&(y[v.tokenTypeIdx]=!0,Ee(v.categoryMatches,x=>{y[x]=!0})),y),{});return function(){let y=this.LA(1);return g[y.tokenTypeIdx]===!0}}}return function(){let p=GN.call(this,s,f,qae,l);return typeof p=="object"?!1:p===0}}};o(Xae,"isLL1Sequence");o(uOe,"initATNSimulator");o(GN,"adaptivePredict");o(hOe,"performLookahead");o(fOe,"computeLookaheadTarget");o(dOe,"reportLookaheadAmbiguity");o(pOe,"buildAmbiguityError");o(mOe,"getProductionDslName");o(gOe,"buildAdaptivePredictError");o(yOe,"getExistingTargetState");o(vOe,"computeReachSet");o(xOe,"getReachableTarget");o(bOe,"getUniqueAlt");o(Kae,"newDFAState");o(jae,"addDFAEdge");o(Qae,"addDFAState");o(wOe,"computeStartState");o(vk,"closure");o(TOe,"getEpsilonTarget");o(kOe,"hasConfigInRuleStopState");o(EOe,"allConfigsInRuleStopStates");o(COe,"hasConflictTerminatingPrediction");o(SOe,"getConflictingAltSets");o(AOe,"hasConflictingAltSet");o(_Oe,"hasStateAssociatedWithOneAlt")});var Jae=R(()=>{"use strict";Zae()});var ese,$N,tse,xk,Ur,wr,bk,rse,VN,nse,ise,ase,sse,UN,ose,lse,cse,wk,dg,pg,HN,mg,use,YN,WN,qN,XN,jN,hse,fse,KN,dse,QN,E2,pse,mse,gse,yse,vse,xse,bse,wse,Tk,Tse,kse,Ese,Cse,Sse,Ase,_se,Lse,Dse,Rse,Nse,kk,Mse,Ise,Ose,Pse,Bse,Fse,zse,Gse,$se,Vse,Use,Hse,Yse,ZN,JN,Wse,qse,Xse,jse,Kse,Qse,Zse,Jse,eoe,eM,Fe,tM=R(()=>{"use strict";(function(t){function e(r){return typeof r=="string"}o(e,"is"),t.is=e})(ese||(ese={}));(function(t){function e(r){return typeof r=="string"}o(e,"is"),t.is=e})($N||($N={}));(function(t){t.MIN_VALUE=-2147483648,t.MAX_VALUE=2147483647;function e(r){return typeof r=="number"&&t.MIN_VALUE<=r&&r<=t.MAX_VALUE}o(e,"is"),t.is=e})(tse||(tse={}));(function(t){t.MIN_VALUE=0,t.MAX_VALUE=2147483647;function e(r){return typeof r=="number"&&t.MIN_VALUE<=r&&r<=t.MAX_VALUE}o(e,"is"),t.is=e})(xk||(xk={}));(function(t){function e(n,i){return n===Number.MAX_VALUE&&(n=xk.MAX_VALUE),i===Number.MAX_VALUE&&(i=xk.MAX_VALUE),{line:n,character:i}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.objectLiteral(i)&&Fe.uinteger(i.line)&&Fe.uinteger(i.character)}o(r,"is"),t.is=r})(Ur||(Ur={}));(function(t){function e(n,i,a,s){if(Fe.uinteger(n)&&Fe.uinteger(i)&&Fe.uinteger(a)&&Fe.uinteger(s))return{start:Ur.create(n,i),end:Ur.create(a,s)};if(Ur.is(n)&&Ur.is(i))return{start:n,end:i};throw new Error(`Range#create called with invalid arguments[${n}, ${i}, ${a}, ${s}]`)}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.objectLiteral(i)&&Ur.is(i.start)&&Ur.is(i.end)}o(r,"is"),t.is=r})(wr||(wr={}));(function(t){function e(n,i){return{uri:n,range:i}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.objectLiteral(i)&&wr.is(i.range)&&(Fe.string(i.uri)||Fe.undefined(i.uri))}o(r,"is"),t.is=r})(bk||(bk={}));(function(t){function e(n,i,a,s){return{targetUri:n,targetRange:i,targetSelectionRange:a,originSelectionRange:s}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.objectLiteral(i)&&wr.is(i.targetRange)&&Fe.string(i.targetUri)&&wr.is(i.targetSelectionRange)&&(wr.is(i.originSelectionRange)||Fe.undefined(i.originSelectionRange))}o(r,"is"),t.is=r})(rse||(rse={}));(function(t){function e(n,i,a,s){return{red:n,green:i,blue:a,alpha:s}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.objectLiteral(i)&&Fe.numberRange(i.red,0,1)&&Fe.numberRange(i.green,0,1)&&Fe.numberRange(i.blue,0,1)&&Fe.numberRange(i.alpha,0,1)}o(r,"is"),t.is=r})(VN||(VN={}));(function(t){function e(n,i){return{range:n,color:i}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.objectLiteral(i)&&wr.is(i.range)&&VN.is(i.color)}o(r,"is"),t.is=r})(nse||(nse={}));(function(t){function e(n,i,a){return{label:n,textEdit:i,additionalTextEdits:a}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.objectLiteral(i)&&Fe.string(i.label)&&(Fe.undefined(i.textEdit)||pg.is(i))&&(Fe.undefined(i.additionalTextEdits)||Fe.typedArray(i.additionalTextEdits,pg.is))}o(r,"is"),t.is=r})(ise||(ise={}));(function(t){t.Comment="comment",t.Imports="imports",t.Region="region"})(ase||(ase={}));(function(t){function e(n,i,a,s,l,u){let h={startLine:n,endLine:i};return Fe.defined(a)&&(h.startCharacter=a),Fe.defined(s)&&(h.endCharacter=s),Fe.defined(l)&&(h.kind=l),Fe.defined(u)&&(h.collapsedText=u),h}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.objectLiteral(i)&&Fe.uinteger(i.startLine)&&Fe.uinteger(i.startLine)&&(Fe.undefined(i.startCharacter)||Fe.uinteger(i.startCharacter))&&(Fe.undefined(i.endCharacter)||Fe.uinteger(i.endCharacter))&&(Fe.undefined(i.kind)||Fe.string(i.kind))}o(r,"is"),t.is=r})(sse||(sse={}));(function(t){function e(n,i){return{location:n,message:i}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.defined(i)&&bk.is(i.location)&&Fe.string(i.message)}o(r,"is"),t.is=r})(UN||(UN={}));(function(t){t.Error=1,t.Warning=2,t.Information=3,t.Hint=4})(ose||(ose={}));(function(t){t.Unnecessary=1,t.Deprecated=2})(lse||(lse={}));(function(t){function e(r){let n=r;return Fe.objectLiteral(n)&&Fe.string(n.href)}o(e,"is"),t.is=e})(cse||(cse={}));(function(t){function e(n,i,a,s,l,u){let h={range:n,message:i};return Fe.defined(a)&&(h.severity=a),Fe.defined(s)&&(h.code=s),Fe.defined(l)&&(h.source=l),Fe.defined(u)&&(h.relatedInformation=u),h}o(e,"create"),t.create=e;function r(n){var i;let a=n;return Fe.defined(a)&&wr.is(a.range)&&Fe.string(a.message)&&(Fe.number(a.severity)||Fe.undefined(a.severity))&&(Fe.integer(a.code)||Fe.string(a.code)||Fe.undefined(a.code))&&(Fe.undefined(a.codeDescription)||Fe.string((i=a.codeDescription)===null||i===void 0?void 0:i.href))&&(Fe.string(a.source)||Fe.undefined(a.source))&&(Fe.undefined(a.relatedInformation)||Fe.typedArray(a.relatedInformation,UN.is))}o(r,"is"),t.is=r})(wk||(wk={}));(function(t){function e(n,i,...a){let s={title:n,command:i};return Fe.defined(a)&&a.length>0&&(s.arguments=a),s}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.defined(i)&&Fe.string(i.title)&&Fe.string(i.command)}o(r,"is"),t.is=r})(dg||(dg={}));(function(t){function e(a,s){return{range:a,newText:s}}o(e,"replace"),t.replace=e;function r(a,s){return{range:{start:a,end:a},newText:s}}o(r,"insert"),t.insert=r;function n(a){return{range:a,newText:""}}o(n,"del"),t.del=n;function i(a){let s=a;return Fe.objectLiteral(s)&&Fe.string(s.newText)&&wr.is(s.range)}o(i,"is"),t.is=i})(pg||(pg={}));(function(t){function e(n,i,a){let s={label:n};return i!==void 0&&(s.needsConfirmation=i),a!==void 0&&(s.description=a),s}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.objectLiteral(i)&&Fe.string(i.label)&&(Fe.boolean(i.needsConfirmation)||i.needsConfirmation===void 0)&&(Fe.string(i.description)||i.description===void 0)}o(r,"is"),t.is=r})(HN||(HN={}));(function(t){function e(r){let n=r;return Fe.string(n)}o(e,"is"),t.is=e})(mg||(mg={}));(function(t){function e(a,s,l){return{range:a,newText:s,annotationId:l}}o(e,"replace"),t.replace=e;function r(a,s,l){return{range:{start:a,end:a},newText:s,annotationId:l}}o(r,"insert"),t.insert=r;function n(a,s){return{range:a,newText:"",annotationId:s}}o(n,"del"),t.del=n;function i(a){let s=a;return pg.is(s)&&(HN.is(s.annotationId)||mg.is(s.annotationId))}o(i,"is"),t.is=i})(use||(use={}));(function(t){function e(n,i){return{textDocument:n,edits:i}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.defined(i)&&KN.is(i.textDocument)&&Array.isArray(i.edits)}o(r,"is"),t.is=r})(YN||(YN={}));(function(t){function e(n,i,a){let s={kind:"create",uri:n};return i!==void 0&&(i.overwrite!==void 0||i.ignoreIfExists!==void 0)&&(s.options=i),a!==void 0&&(s.annotationId=a),s}o(e,"create"),t.create=e;function r(n){let i=n;return i&&i.kind==="create"&&Fe.string(i.uri)&&(i.options===void 0||(i.options.overwrite===void 0||Fe.boolean(i.options.overwrite))&&(i.options.ignoreIfExists===void 0||Fe.boolean(i.options.ignoreIfExists)))&&(i.annotationId===void 0||mg.is(i.annotationId))}o(r,"is"),t.is=r})(WN||(WN={}));(function(t){function e(n,i,a,s){let l={kind:"rename",oldUri:n,newUri:i};return a!==void 0&&(a.overwrite!==void 0||a.ignoreIfExists!==void 0)&&(l.options=a),s!==void 0&&(l.annotationId=s),l}o(e,"create"),t.create=e;function r(n){let i=n;return i&&i.kind==="rename"&&Fe.string(i.oldUri)&&Fe.string(i.newUri)&&(i.options===void 0||(i.options.overwrite===void 0||Fe.boolean(i.options.overwrite))&&(i.options.ignoreIfExists===void 0||Fe.boolean(i.options.ignoreIfExists)))&&(i.annotationId===void 0||mg.is(i.annotationId))}o(r,"is"),t.is=r})(qN||(qN={}));(function(t){function e(n,i,a){let s={kind:"delete",uri:n};return i!==void 0&&(i.recursive!==void 0||i.ignoreIfNotExists!==void 0)&&(s.options=i),a!==void 0&&(s.annotationId=a),s}o(e,"create"),t.create=e;function r(n){let i=n;return i&&i.kind==="delete"&&Fe.string(i.uri)&&(i.options===void 0||(i.options.recursive===void 0||Fe.boolean(i.options.recursive))&&(i.options.ignoreIfNotExists===void 0||Fe.boolean(i.options.ignoreIfNotExists)))&&(i.annotationId===void 0||mg.is(i.annotationId))}o(r,"is"),t.is=r})(XN||(XN={}));(function(t){function e(r){let n=r;return n&&(n.changes!==void 0||n.documentChanges!==void 0)&&(n.documentChanges===void 0||n.documentChanges.every(i=>Fe.string(i.kind)?WN.is(i)||qN.is(i)||XN.is(i):YN.is(i)))}o(e,"is"),t.is=e})(jN||(jN={}));(function(t){function e(n){return{uri:n}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.defined(i)&&Fe.string(i.uri)}o(r,"is"),t.is=r})(hse||(hse={}));(function(t){function e(n,i){return{uri:n,version:i}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.defined(i)&&Fe.string(i.uri)&&Fe.integer(i.version)}o(r,"is"),t.is=r})(fse||(fse={}));(function(t){function e(n,i){return{uri:n,version:i}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.defined(i)&&Fe.string(i.uri)&&(i.version===null||Fe.integer(i.version))}o(r,"is"),t.is=r})(KN||(KN={}));(function(t){function e(n,i,a,s){return{uri:n,languageId:i,version:a,text:s}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.defined(i)&&Fe.string(i.uri)&&Fe.string(i.languageId)&&Fe.integer(i.version)&&Fe.string(i.text)}o(r,"is"),t.is=r})(dse||(dse={}));(function(t){t.PlainText="plaintext",t.Markdown="markdown";function e(r){let n=r;return n===t.PlainText||n===t.Markdown}o(e,"is"),t.is=e})(QN||(QN={}));(function(t){function e(r){let n=r;return Fe.objectLiteral(r)&&QN.is(n.kind)&&Fe.string(n.value)}o(e,"is"),t.is=e})(E2||(E2={}));(function(t){t.Text=1,t.Method=2,t.Function=3,t.Constructor=4,t.Field=5,t.Variable=6,t.Class=7,t.Interface=8,t.Module=9,t.Property=10,t.Unit=11,t.Value=12,t.Enum=13,t.Keyword=14,t.Snippet=15,t.Color=16,t.File=17,t.Reference=18,t.Folder=19,t.EnumMember=20,t.Constant=21,t.Struct=22,t.Event=23,t.Operator=24,t.TypeParameter=25})(pse||(pse={}));(function(t){t.PlainText=1,t.Snippet=2})(mse||(mse={}));(function(t){t.Deprecated=1})(gse||(gse={}));(function(t){function e(n,i,a){return{newText:n,insert:i,replace:a}}o(e,"create"),t.create=e;function r(n){let i=n;return i&&Fe.string(i.newText)&&wr.is(i.insert)&&wr.is(i.replace)}o(r,"is"),t.is=r})(yse||(yse={}));(function(t){t.asIs=1,t.adjustIndentation=2})(vse||(vse={}));(function(t){function e(r){let n=r;return n&&(Fe.string(n.detail)||n.detail===void 0)&&(Fe.string(n.description)||n.description===void 0)}o(e,"is"),t.is=e})(xse||(xse={}));(function(t){function e(r){return{label:r}}o(e,"create"),t.create=e})(bse||(bse={}));(function(t){function e(r,n){return{items:r||[],isIncomplete:!!n}}o(e,"create"),t.create=e})(wse||(wse={}));(function(t){function e(n){return n.replace(/[\\`*_{}[\]()#+\-.!]/g,"\\$&")}o(e,"fromPlainText"),t.fromPlainText=e;function r(n){let i=n;return Fe.string(i)||Fe.objectLiteral(i)&&Fe.string(i.language)&&Fe.string(i.value)}o(r,"is"),t.is=r})(Tk||(Tk={}));(function(t){function e(r){let n=r;return!!n&&Fe.objectLiteral(n)&&(E2.is(n.contents)||Tk.is(n.contents)||Fe.typedArray(n.contents,Tk.is))&&(r.range===void 0||wr.is(r.range))}o(e,"is"),t.is=e})(Tse||(Tse={}));(function(t){function e(r,n){return n?{label:r,documentation:n}:{label:r}}o(e,"create"),t.create=e})(kse||(kse={}));(function(t){function e(r,n,...i){let a={label:r};return Fe.defined(n)&&(a.documentation=n),Fe.defined(i)?a.parameters=i:a.parameters=[],a}o(e,"create"),t.create=e})(Ese||(Ese={}));(function(t){t.Text=1,t.Read=2,t.Write=3})(Cse||(Cse={}));(function(t){function e(r,n){let i={range:r};return Fe.number(n)&&(i.kind=n),i}o(e,"create"),t.create=e})(Sse||(Sse={}));(function(t){t.File=1,t.Module=2,t.Namespace=3,t.Package=4,t.Class=5,t.Method=6,t.Property=7,t.Field=8,t.Constructor=9,t.Enum=10,t.Interface=11,t.Function=12,t.Variable=13,t.Constant=14,t.String=15,t.Number=16,t.Boolean=17,t.Array=18,t.Object=19,t.Key=20,t.Null=21,t.EnumMember=22,t.Struct=23,t.Event=24,t.Operator=25,t.TypeParameter=26})(Ase||(Ase={}));(function(t){t.Deprecated=1})(_se||(_se={}));(function(t){function e(r,n,i,a,s){let l={name:r,kind:n,location:{uri:a,range:i}};return s&&(l.containerName=s),l}o(e,"create"),t.create=e})(Lse||(Lse={}));(function(t){function e(r,n,i,a){return a!==void 0?{name:r,kind:n,location:{uri:i,range:a}}:{name:r,kind:n,location:{uri:i}}}o(e,"create"),t.create=e})(Dse||(Dse={}));(function(t){function e(n,i,a,s,l,u){let h={name:n,detail:i,kind:a,range:s,selectionRange:l};return u!==void 0&&(h.children=u),h}o(e,"create"),t.create=e;function r(n){let i=n;return i&&Fe.string(i.name)&&Fe.number(i.kind)&&wr.is(i.range)&&wr.is(i.selectionRange)&&(i.detail===void 0||Fe.string(i.detail))&&(i.deprecated===void 0||Fe.boolean(i.deprecated))&&(i.children===void 0||Array.isArray(i.children))&&(i.tags===void 0||Array.isArray(i.tags))}o(r,"is"),t.is=r})(Rse||(Rse={}));(function(t){t.Empty="",t.QuickFix="quickfix",t.Refactor="refactor",t.RefactorExtract="refactor.extract",t.RefactorInline="refactor.inline",t.RefactorRewrite="refactor.rewrite",t.Source="source",t.SourceOrganizeImports="source.organizeImports",t.SourceFixAll="source.fixAll"})(Nse||(Nse={}));(function(t){t.Invoked=1,t.Automatic=2})(kk||(kk={}));(function(t){function e(n,i,a){let s={diagnostics:n};return i!=null&&(s.only=i),a!=null&&(s.triggerKind=a),s}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.defined(i)&&Fe.typedArray(i.diagnostics,wk.is)&&(i.only===void 0||Fe.typedArray(i.only,Fe.string))&&(i.triggerKind===void 0||i.triggerKind===kk.Invoked||i.triggerKind===kk.Automatic)}o(r,"is"),t.is=r})(Mse||(Mse={}));(function(t){function e(n,i,a){let s={title:n},l=!0;return typeof i=="string"?(l=!1,s.kind=i):dg.is(i)?s.command=i:s.edit=i,l&&a!==void 0&&(s.kind=a),s}o(e,"create"),t.create=e;function r(n){let i=n;return i&&Fe.string(i.title)&&(i.diagnostics===void 0||Fe.typedArray(i.diagnostics,wk.is))&&(i.kind===void 0||Fe.string(i.kind))&&(i.edit!==void 0||i.command!==void 0)&&(i.command===void 0||dg.is(i.command))&&(i.isPreferred===void 0||Fe.boolean(i.isPreferred))&&(i.edit===void 0||jN.is(i.edit))}o(r,"is"),t.is=r})(Ise||(Ise={}));(function(t){function e(n,i){let a={range:n};return Fe.defined(i)&&(a.data=i),a}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.defined(i)&&wr.is(i.range)&&(Fe.undefined(i.command)||dg.is(i.command))}o(r,"is"),t.is=r})(Ose||(Ose={}));(function(t){function e(n,i){return{tabSize:n,insertSpaces:i}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.defined(i)&&Fe.uinteger(i.tabSize)&&Fe.boolean(i.insertSpaces)}o(r,"is"),t.is=r})(Pse||(Pse={}));(function(t){function e(n,i,a){return{range:n,target:i,data:a}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.defined(i)&&wr.is(i.range)&&(Fe.undefined(i.target)||Fe.string(i.target))}o(r,"is"),t.is=r})(Bse||(Bse={}));(function(t){function e(n,i){return{range:n,parent:i}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.objectLiteral(i)&&wr.is(i.range)&&(i.parent===void 0||t.is(i.parent))}o(r,"is"),t.is=r})(Fse||(Fse={}));(function(t){t.namespace="namespace",t.type="type",t.class="class",t.enum="enum",t.interface="interface",t.struct="struct",t.typeParameter="typeParameter",t.parameter="parameter",t.variable="variable",t.property="property",t.enumMember="enumMember",t.event="event",t.function="function",t.method="method",t.macro="macro",t.keyword="keyword",t.modifier="modifier",t.comment="comment",t.string="string",t.number="number",t.regexp="regexp",t.operator="operator",t.decorator="decorator"})(zse||(zse={}));(function(t){t.declaration="declaration",t.definition="definition",t.readonly="readonly",t.static="static",t.deprecated="deprecated",t.abstract="abstract",t.async="async",t.modification="modification",t.documentation="documentation",t.defaultLibrary="defaultLibrary"})(Gse||(Gse={}));(function(t){function e(r){let n=r;return Fe.objectLiteral(n)&&(n.resultId===void 0||typeof n.resultId=="string")&&Array.isArray(n.data)&&(n.data.length===0||typeof n.data[0]=="number")}o(e,"is"),t.is=e})($se||($se={}));(function(t){function e(n,i){return{range:n,text:i}}o(e,"create"),t.create=e;function r(n){let i=n;return i!=null&&wr.is(i.range)&&Fe.string(i.text)}o(r,"is"),t.is=r})(Vse||(Vse={}));(function(t){function e(n,i,a){return{range:n,variableName:i,caseSensitiveLookup:a}}o(e,"create"),t.create=e;function r(n){let i=n;return i!=null&&wr.is(i.range)&&Fe.boolean(i.caseSensitiveLookup)&&(Fe.string(i.variableName)||i.variableName===void 0)}o(r,"is"),t.is=r})(Use||(Use={}));(function(t){function e(n,i){return{range:n,expression:i}}o(e,"create"),t.create=e;function r(n){let i=n;return i!=null&&wr.is(i.range)&&(Fe.string(i.expression)||i.expression===void 0)}o(r,"is"),t.is=r})(Hse||(Hse={}));(function(t){function e(n,i){return{frameId:n,stoppedLocation:i}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.defined(i)&&wr.is(n.stoppedLocation)}o(r,"is"),t.is=r})(Yse||(Yse={}));(function(t){t.Type=1,t.Parameter=2;function e(r){return r===1||r===2}o(e,"is"),t.is=e})(ZN||(ZN={}));(function(t){function e(n){return{value:n}}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.objectLiteral(i)&&(i.tooltip===void 0||Fe.string(i.tooltip)||E2.is(i.tooltip))&&(i.location===void 0||bk.is(i.location))&&(i.command===void 0||dg.is(i.command))}o(r,"is"),t.is=r})(JN||(JN={}));(function(t){function e(n,i,a){let s={position:n,label:i};return a!==void 0&&(s.kind=a),s}o(e,"create"),t.create=e;function r(n){let i=n;return Fe.objectLiteral(i)&&Ur.is(i.position)&&(Fe.string(i.label)||Fe.typedArray(i.label,JN.is))&&(i.kind===void 0||ZN.is(i.kind))&&i.textEdits===void 0||Fe.typedArray(i.textEdits,pg.is)&&(i.tooltip===void 0||Fe.string(i.tooltip)||E2.is(i.tooltip))&&(i.paddingLeft===void 0||Fe.boolean(i.paddingLeft))&&(i.paddingRight===void 0||Fe.boolean(i.paddingRight))}o(r,"is"),t.is=r})(Wse||(Wse={}));(function(t){function e(r){return{kind:"snippet",value:r}}o(e,"createSnippet"),t.createSnippet=e})(qse||(qse={}));(function(t){function e(r,n,i,a){return{insertText:r,filterText:n,range:i,command:a}}o(e,"create"),t.create=e})(Xse||(Xse={}));(function(t){function e(r){return{items:r}}o(e,"create"),t.create=e})(jse||(jse={}));(function(t){t.Invoked=0,t.Automatic=1})(Kse||(Kse={}));(function(t){function e(r,n){return{range:r,text:n}}o(e,"create"),t.create=e})(Qse||(Qse={}));(function(t){function e(r,n){return{triggerKind:r,selectedCompletionInfo:n}}o(e,"create"),t.create=e})(Zse||(Zse={}));(function(t){function e(r){let n=r;return Fe.objectLiteral(n)&&$N.is(n.uri)&&Fe.string(n.name)}o(e,"is"),t.is=e})(Jse||(Jse={}));(function(t){function e(a,s,l,u){return new eM(a,s,l,u)}o(e,"create"),t.create=e;function r(a){let s=a;return!!(Fe.defined(s)&&Fe.string(s.uri)&&(Fe.undefined(s.languageId)||Fe.string(s.languageId))&&Fe.uinteger(s.lineCount)&&Fe.func(s.getText)&&Fe.func(s.positionAt)&&Fe.func(s.offsetAt))}o(r,"is"),t.is=r;function n(a,s){let l=a.getText(),u=i(s,(f,d)=>{let p=f.range.start.line-d.range.start.line;return p===0?f.range.start.character-d.range.start.character:p}),h=l.length;for(let f=u.length-1;f>=0;f--){let d=u[f],p=a.offsetAt(d.range.start),m=a.offsetAt(d.range.end);if(m<=h)l=l.substring(0,p)+d.newText+l.substring(m,l.length);else throw new Error("Overlapping edit");h=p}return l}o(n,"applyEdits"),t.applyEdits=n;function i(a,s){if(a.length<=1)return a;let l=a.length/2|0,u=a.slice(0,l),h=a.slice(l);i(u,s),i(h,s);let f=0,d=0,p=0;for(;f0&&e.push(r.length),this._lineOffsets=e}return this._lineOffsets}positionAt(e){e=Math.max(Math.min(e,this._content.length),0);let r=this.getLineOffsets(),n=0,i=r.length;if(i===0)return Ur.create(0,e);for(;ne?i=s:n=s+1}let a=n-1;return Ur.create(a,e-r[a])}offsetAt(e){let r=this.getLineOffsets();if(e.line>=r.length)return this._content.length;if(e.line<0)return 0;let n=r[e.line],i=e.line+1"u"}o(n,"undefined"),t.undefined=n;function i(m){return m===!0||m===!1}o(i,"boolean"),t.boolean=i;function a(m){return e.call(m)==="[object String]"}o(a,"string"),t.string=a;function s(m){return e.call(m)==="[object Number]"}o(s,"number"),t.number=s;function l(m,g,y){return e.call(m)==="[object Number]"&&g<=m&&m<=y}o(l,"numberRange"),t.numberRange=l;function u(m){return e.call(m)==="[object Number]"&&-2147483648<=m&&m<=2147483647}o(u,"integer"),t.integer=u;function h(m){return e.call(m)==="[object Number]"&&0<=m&&m<=2147483647}o(h,"uinteger"),t.uinteger=h;function f(m){return e.call(m)==="[object Function]"}o(f,"func"),t.func=f;function d(m){return m!==null&&typeof m=="object"}o(d,"objectLiteral"),t.objectLiteral=d;function p(m,g){return Array.isArray(m)&&m.every(g)}o(p,"typedArray"),t.typedArray=p})(Fe||(Fe={}))});var C2,S2,d0,p0,rM,gg,Ek=R(()=>{"use strict";tM();Vo();Rl();C2=class{static{o(this,"CstNodeBuilder")}constructor(){this.nodeStack=[]}get current(){return this.nodeStack[this.nodeStack.length-1]}buildRootNode(e){return this.rootNode=new gg(e),this.rootNode.root=this.rootNode,this.nodeStack=[this.rootNode],this.rootNode}buildCompositeNode(e){let r=new p0;return r.grammarSource=e,r.root=this.rootNode,this.current.content.push(r),this.nodeStack.push(r),r}buildLeafNode(e,r){let n=new d0(e.startOffset,e.image.length,zm(e),e.tokenType,!1);return n.grammarSource=r,n.root=this.rootNode,this.current.content.push(n),n}removeNode(e){let r=e.container;if(r){let n=r.content.indexOf(e);n>=0&&r.content.splice(n,1)}}construct(e){let r=this.current;typeof e.$type=="string"&&(this.current.astNode=e),e.$cstNode=r;let n=this.nodeStack.pop();n?.content.length===0&&this.removeNode(n)}addHiddenTokens(e){for(let r of e){let n=new d0(r.startOffset,r.image.length,zm(r),r.tokenType,!0);n.root=this.rootNode,this.addHiddenToken(this.rootNode,n)}}addHiddenToken(e,r){let{offset:n,end:i}=r;for(let a=0;al&&i=0;e--){let r=this.content[e];if(!r.hidden)return r}return this.content[this.content.length-1]}},rM=class t extends Array{static{o(this,"CstNodeContainer")}constructor(e){super(),this.parent=e,Object.setPrototypeOf(this,t.prototype)}push(...e){return this.addParents(e),super.push(...e)}unshift(...e){return this.addParents(e),super.unshift(...e)}splice(e,r,...n){return this.addParents(n),super.splice(e,r,...n)}addParents(e){for(let r of e)r.container=this.parent}},gg=class extends p0{static{o(this,"RootCstNodeImpl")}get text(){return this._text.substring(this.offset,this.end)}get fullText(){return this._text}constructor(e){super(),this._text="",this._text=e??""}}});function nM(t){return t.$type===Ck}var Ck,toe,roe,A2,_2,Sk,yg,L2,LOe,iM,D2=R(()=>{"use strict";u0();Jae();Sc();Il();es();Ek();Ck=Symbol("Datatype");o(nM,"isDataTypeNode");toe="\u200B",roe=o(t=>t.endsWith(toe)?t:t+toe,"withRuleSuffix"),A2=class{static{o(this,"AbstractLangiumParser")}constructor(e){this._unorderedGroups=new Map,this.lexer=e.parser.Lexer;let r=this.lexer.definition;this.wrapper=new iM(r,Object.assign(Object.assign({},e.parser.ParserConfig),{errorMessageProvider:e.parser.ParserErrorMessageProvider}))}alternatives(e,r){this.wrapper.wrapOr(e,r)}optional(e,r){this.wrapper.wrapOption(e,r)}many(e,r){this.wrapper.wrapMany(e,r)}atLeastOne(e,r){this.wrapper.wrapAtLeastOne(e,r)}isRecording(){return this.wrapper.IS_RECORDING}get unorderedGroups(){return this._unorderedGroups}getRuleStack(){return this.wrapper.RULE_STACK}finalize(){this.wrapper.wrapSelfAnalysis()}},_2=class extends A2{static{o(this,"LangiumParser")}get current(){return this.stack[this.stack.length-1]}constructor(e){super(e),this.nodeBuilder=new C2,this.stack=[],this.assignmentMap=new Map,this.linker=e.references.Linker,this.converter=e.parser.ValueConverter,this.astReflection=e.shared.AstReflection}rule(e,r){let n=e.fragment?void 0:Jv(e)?Ck:r0(e),i=this.wrapper.DEFINE_RULE(roe(e.name),this.startImplementation(n,r).bind(this));return e.entry&&(this.mainRule=i),i}parse(e){this.nodeBuilder.buildRootNode(e);let r=this.lexer.tokenize(e);this.wrapper.input=r.tokens;let n=this.mainRule.call(this.wrapper,{});return this.nodeBuilder.addHiddenTokens(r.hidden),this.unorderedGroups.clear(),{value:n,lexerErrors:r.errors,parserErrors:this.wrapper.errors}}startImplementation(e,r){return n=>{if(!this.isRecording()){let a={$type:e};this.stack.push(a),e===Ck&&(a.value="")}let i;try{i=r(n)}catch{i=void 0}return!this.isRecording()&&i===void 0&&(i=this.construct()),i}}consume(e,r,n){let i=this.wrapper.wrapConsume(e,r);if(!this.isRecording()&&this.isValidToken(i)){let a=this.nodeBuilder.buildLeafNode(i,n),{assignment:s,isCrossRef:l}=this.getAssignment(n),u=this.current;if(s){let h=Ho(n)?i.image:this.converter.convert(i.image,a);this.assign(s.operator,s.feature,h,a,l)}else if(nM(u)){let h=i.image;Ho(n)||(h=this.converter.convert(h,a).toString()),u.value+=h}}}isValidToken(e){return!e.isInsertedInRecovery&&!isNaN(e.startOffset)&&typeof e.endOffset=="number"&&!isNaN(e.endOffset)}subrule(e,r,n,i){let a;this.isRecording()||(a=this.nodeBuilder.buildCompositeNode(n));let s=this.wrapper.wrapSubrule(e,r,i);!this.isRecording()&&a&&a.length>0&&this.performSubruleAssignment(s,n,a)}performSubruleAssignment(e,r,n){let{assignment:i,isCrossRef:a}=this.getAssignment(r);if(i)this.assign(i.operator,i.feature,e,n,a);else if(!i){let s=this.current;if(nM(s))s.value+=e.toString();else if(typeof e=="object"&&e){let l=e.$type,u=this.assignWithoutOverride(e,s);l&&(u.$type=l);let h=u;this.stack.pop(),this.stack.push(h)}}}action(e,r){if(!this.isRecording()){let n=this.current;if(!n.$cstNode&&r.feature&&r.operator){n=this.construct(!1);let a=n.$cstNode.feature;this.nodeBuilder.buildCompositeNode(a)}let i={$type:e};this.stack.pop(),this.stack.push(i),r.feature&&r.operator&&this.assign(r.operator,r.feature,n,n.$cstNode,!1)}}construct(e=!0){if(this.isRecording())return;let r=this.current;return ET(r),this.nodeBuilder.construct(r),e&&this.stack.pop(),nM(r)?this.converter.convert(r.value,r.$cstNode):(NR(this.astReflection,r),r)}getAssignment(e){if(!this.assignmentMap.has(e)){let r=Qd(e,Nl);this.assignmentMap.set(e,{assignment:r,isCrossRef:r?Kd(r.terminal):!1})}return this.assignmentMap.get(e)}assign(e,r,n,i,a){let s=this.current,l;switch(a&&typeof n=="string"?l=this.linker.buildReference(s,r,i,n):l=n,e){case"=":{s[r]=l;break}case"?=":{s[r]=!0;break}case"+=":Array.isArray(s[r])||(s[r]=[]),s[r].push(l)}}assignWithoutOverride(e,r){for(let[n,i]of Object.entries(r)){let a=e[n];a===void 0?e[n]=i:Array.isArray(a)&&Array.isArray(i)&&(i.push(...a),e[n]=i)}return e}get definitionErrors(){return this.wrapper.definitionErrors}},Sk=class{static{o(this,"AbstractParserErrorMessageProvider")}buildMismatchTokenMessage(e){return Gu.buildMismatchTokenMessage(e)}buildNotAllInputParsedMessage(e){return Gu.buildNotAllInputParsedMessage(e)}buildNoViableAltMessage(e){return Gu.buildNoViableAltMessage(e)}buildEarlyExitMessage(e){return Gu.buildEarlyExitMessage(e)}},yg=class extends Sk{static{o(this,"LangiumParserErrorMessageProvider")}buildMismatchTokenMessage({expected:e,actual:r}){return`Expecting ${e.LABEL?"`"+e.LABEL+"`":e.name.endsWith(":KW")?`keyword '${e.name.substring(0,e.name.length-3)}'`:`token of type '${e.name}'`} but found \`${r.image}\`.`}buildNotAllInputParsedMessage({firstRedundant:e}){return`Expecting end of file but found \`${e.image}\`.`}},L2=class extends A2{static{o(this,"LangiumCompletionParser")}constructor(){super(...arguments),this.tokens=[],this.elementStack=[],this.lastElementStack=[],this.nextTokenIndex=0,this.stackSize=0}action(){}construct(){}parse(e){this.resetState();let r=this.lexer.tokenize(e);return this.tokens=r.tokens,this.wrapper.input=[...this.tokens],this.mainRule.call(this.wrapper,{}),this.unorderedGroups.clear(),{tokens:this.tokens,elementStack:[...this.lastElementStack],tokenIndex:this.nextTokenIndex}}rule(e,r){let n=this.wrapper.DEFINE_RULE(roe(e.name),this.startImplementation(r).bind(this));return e.entry&&(this.mainRule=n),n}resetState(){this.elementStack=[],this.lastElementStack=[],this.nextTokenIndex=0,this.stackSize=0}startImplementation(e){return r=>{let n=this.keepStackSize();try{e(r)}finally{this.resetStackSize(n)}}}removeUnexpectedElements(){this.elementStack.splice(this.stackSize)}keepStackSize(){let e=this.elementStack.length;return this.stackSize=e,e}resetStackSize(e){this.removeUnexpectedElements(),this.stackSize=e}consume(e,r,n){this.wrapper.wrapConsume(e,r),this.isRecording()||(this.lastElementStack=[...this.elementStack,n],this.nextTokenIndex=this.currIdx+1)}subrule(e,r,n,i){this.before(n),this.wrapper.wrapSubrule(e,r,i),this.after(n)}before(e){this.isRecording()||this.elementStack.push(e)}after(e){if(!this.isRecording()){let r=this.elementStack.lastIndexOf(e);r>=0&&this.elementStack.splice(r)}}get currIdx(){return this.wrapper.currIdx}},LOe={recoveryEnabled:!0,nodeLocationTracking:"full",skipValidations:!0,errorMessageProvider:new yg},iM=class extends x2{static{o(this,"ChevrotainWrapper")}constructor(e,r){let n=r&&"maxLookahead"in r;super(e,Object.assign(Object.assign(Object.assign({},LOe),{lookaheadStrategy:n?new $u({maxLookahead:r.maxLookahead}):new k2}),r))}get IS_RECORDING(){return this.RECORDING_PHASE}DEFINE_RULE(e,r){return this.RULE(e,r)}wrapSelfAnalysis(){this.performSelfAnalysis()}wrapConsume(e,r){return this.consume(e,r)}wrapSubrule(e,r,n){return this.subrule(e,r,{ARGS:[n]})}wrapOr(e,r){this.or(e,r)}wrapOption(e,r){this.option(e,r)}wrapMany(e,r){this.many(e,r)}wrapAtLeastOne(e,r){this.atLeastOne(e,r)}}});function _k(t,e,r){return DOe({parser:e,tokens:r,rules:new Map,ruleNames:new Map},t),e}function DOe(t,e){let r=Qv(e,!1),n=Kr(e.rules).filter(Oa).filter(i=>r.has(i));for(let i of n){let a=Object.assign(Object.assign({},t),{consume:1,optional:1,subrule:1,many:1,or:1});a.rules.set(i.name,t.parser.rule(i,m0(a,i.definition)))}}function m0(t,e,r=!1){let n;if(Ho(e))n=BOe(t,e);else if(Iu(e))n=ROe(t,e);else if(Nl(e))n=m0(t,e.terminal);else if(Kd(e))n=noe(t,e);else if(Ml(e))n=NOe(t,e);else if(wT(e))n=IOe(t,e);else if(kT(e))n=OOe(t,e);else if(rf(e))n=POe(t,e);else if(fR(e)){let i=t.consume++;n=o(()=>t.parser.consume(i,fo,e),"method")}else throw new jd(e.$cstNode,`Unexpected element type: ${e.$type}`);return ioe(t,r?void 0:Ak(e),n,e.cardinality)}function ROe(t,e){let r=r0(e);return()=>t.parser.action(r,e)}function NOe(t,e){let r=e.rule.ref;if(Oa(r)){let n=t.subrule++,i=e.arguments.length>0?MOe(r,e.arguments):()=>({});return a=>t.parser.subrule(n,aoe(t,r),e,i(a))}else if(Uo(r)){let n=t.consume++,i=aM(t,r.name);return()=>t.parser.consume(n,i,e)}else if(r)tf(r);else throw new jd(e.$cstNode,`Undefined rule type: ${e.$type}`)}function MOe(t,e){let r=e.map(n=>Vu(n.value));return n=>{let i={};for(let a=0;ae(n)||r(n)}else if(FD(t)){let e=Vu(t.left),r=Vu(t.right);return n=>e(n)&&r(n)}else if(YD(t)){let e=Vu(t.value);return r=>!e(r)}else if(jD(t)){let e=t.parameter.ref.name;return r=>r!==void 0&&r[e]===!0}else if(PD(t)){let e=!!t.true;return()=>e}tf(t)}function IOe(t,e){if(e.elements.length===1)return m0(t,e.elements[0]);{let r=[];for(let i of e.elements){let a={ALT:m0(t,i,!0)},s=Ak(i);s&&(a.GATE=Vu(s)),r.push(a)}let n=t.or++;return i=>t.parser.alternatives(n,r.map(a=>{let s={ALT:o(()=>a.ALT(i),"ALT")},l=a.GATE;return l&&(s.GATE=()=>l(i)),s}))}}function OOe(t,e){if(e.elements.length===1)return m0(t,e.elements[0]);let r=[];for(let l of e.elements){let u={ALT:m0(t,l,!0)},h=Ak(l);h&&(u.GATE=Vu(h)),r.push(u)}let n=t.or++,i=o((l,u)=>{let h=u.getRuleStack().join("-");return`uGroup_${l}_${h}`},"idFunc"),a=o(l=>t.parser.alternatives(n,r.map((u,h)=>{let f={ALT:o(()=>!0,"ALT")},d=t.parser;f.ALT=()=>{if(u.ALT(l),!d.isRecording()){let m=i(n,d);d.unorderedGroups.get(m)||d.unorderedGroups.set(m,[]);let g=d.unorderedGroups.get(m);typeof g?.[h]>"u"&&(g[h]=!0)}};let p=u.GATE;return p?f.GATE=()=>p(l):f.GATE=()=>{let m=d.unorderedGroups.get(i(n,d));return!m?.[h]},f})),"alternatives"),s=ioe(t,Ak(e),a,"*");return l=>{s(l),t.parser.isRecording()||t.parser.unorderedGroups.delete(i(n,t.parser))}}function POe(t,e){let r=e.elements.map(n=>m0(t,n));return n=>r.forEach(i=>i(n))}function Ak(t){if(rf(t))return t.guardCondition}function noe(t,e,r=e.terminal){if(r)if(Ml(r)&&Oa(r.rule.ref)){let n=t.subrule++;return i=>t.parser.subrule(n,aoe(t,r.rule.ref),e,i)}else if(Ml(r)&&Uo(r.rule.ref)){let n=t.consume++,i=aM(t,r.rule.ref.name);return()=>t.parser.consume(n,i,e)}else if(Ho(r)){let n=t.consume++,i=aM(t,r.value);return()=>t.parser.consume(n,i,e)}else throw new Error("Could not build cross reference parser");else{if(!e.type.ref)throw new Error("Could not resolve reference to type: "+e.type.$refText);let n=DT(e.type.ref),i=n?.terminal;if(!i)throw new Error("Could not find name assignment for type: "+r0(e.type.ref));return noe(t,e,i)}}function BOe(t,e){let r=t.consume++,n=t.tokens[e.value];if(!n)throw new Error("Could not find token for keyword: "+e.value);return()=>t.parser.consume(r,n,e)}function ioe(t,e,r,n){let i=e&&Vu(e);if(!n)if(i){let a=t.or++;return s=>t.parser.alternatives(a,[{ALT:o(()=>r(s),"ALT"),GATE:o(()=>i(s),"GATE")},{ALT:gk(),GATE:o(()=>!i(s),"GATE")}])}else return r;if(n==="*"){let a=t.many++;return s=>t.parser.many(a,{DEF:o(()=>r(s),"DEF"),GATE:i?()=>i(s):void 0})}else if(n==="+"){let a=t.many++;if(i){let s=t.or++;return l=>t.parser.alternatives(s,[{ALT:o(()=>t.parser.atLeastOne(a,{DEF:o(()=>r(l),"DEF")}),"ALT"),GATE:o(()=>i(l),"GATE")},{ALT:gk(),GATE:o(()=>!i(l),"GATE")}])}else return s=>t.parser.atLeastOne(a,{DEF:o(()=>r(s),"DEF")})}else if(n==="?"){let a=t.optional++;return s=>t.parser.optional(a,{DEF:o(()=>r(s),"DEF"),GATE:i?()=>i(s):void 0})}else tf(n)}function aoe(t,e){let r=FOe(t,e),n=t.rules.get(r);if(!n)throw new Error(`Rule "${r}" not found."`);return n}function FOe(t,e){if(Oa(e))return e.name;if(t.ruleNames.has(e))return t.ruleNames.get(e);{let r=e,n=r.$container,i=e.$type;for(;!Oa(n);)(rf(n)||wT(n)||kT(n))&&(i=n.elements.indexOf(r).toString()+":"+i),r=n,n=n.$container;return i=n.name+":"+i,t.ruleNames.set(e,i),i}}function aM(t,e){let r=t.tokens[e];if(!r)throw new Error(`Token "${e}" not found."`);return r}var sM=R(()=>{"use strict";u0();Sc();pT();Ds();Il();o(_k,"createParser");o(DOe,"buildRules");o(m0,"buildElement");o(ROe,"buildAction");o(NOe,"buildRuleCall");o(MOe,"buildRuleCallPredicate");o(Vu,"buildPredicate");o(IOe,"buildAlternatives");o(OOe,"buildUnorderedGroup");o(POe,"buildGroup");o(Ak,"getGuardCondition");o(noe,"buildCrossReference");o(BOe,"buildKeyword");o(ioe,"wrap");o(aoe,"getRule");o(FOe,"getRuleName");o(aM,"getToken")});function oM(t){let e=t.Grammar,r=t.parser.Lexer,n=new L2(t);return _k(e,n,r.definition),n.finalize(),n}var lM=R(()=>{"use strict";D2();sM();o(oM,"createCompletionParser")});function cM(t){let e=soe(t);return e.finalize(),e}function soe(t){let e=t.Grammar,r=t.parser.Lexer,n=new _2(t);return _k(e,n,r.definition)}var uM=R(()=>{"use strict";D2();sM();o(cM,"createLangiumParser");o(soe,"prepareLangiumParser")});var g0,hM=R(()=>{"use strict";u0();Sc();es();Il();Um();Ds();g0=class{static{o(this,"DefaultTokenBuilder")}buildTokens(e,r){let n=Kr(Qv(e,!1)),i=this.buildTerminalTokens(n),a=this.buildKeywordTokens(n,i,r);return i.forEach(s=>{let l=s.PATTERN;typeof l=="object"&&l&&"test"in l&&_T(l)?a.unshift(s):a.push(s)}),a}buildTerminalTokens(e){return e.filter(Uo).filter(r=>!r.fragment).map(r=>this.buildTerminalToken(r)).toArray()}buildTerminalToken(e){let r=Hm(e),n=this.requiresCustomPattern(r)?this.regexPatternFunction(r):r,i={name:e.name,PATTERN:n,LINE_BREAKS:!0};return e.hidden&&(i.GROUP=_T(r)?ni.SKIPPED:"hidden"),i}requiresCustomPattern(e){return e.flags.includes("u")?!0:!!(e.source.includes("?<=")||e.source.includes("?(r.lastIndex=i,r.exec(n))}buildKeywordTokens(e,r,n){return e.filter(Oa).flatMap(i=>Ac(i).filter(Ho)).distinct(i=>i.value).toArray().sort((i,a)=>a.value.length-i.value.length).map(i=>this.buildKeywordToken(i,r,!!n?.caseInsensitive))}buildKeywordToken(e,r,n){return{name:e.value,PATTERN:this.buildKeywordPattern(e,n),LONGER_ALT:this.findLongerAlt(e,r)}}buildKeywordPattern(e,r){return r?new RegExp(zR(e.value)):e.value}findLongerAlt(e,r){return r.reduce((n,i)=>{let a=i?.PATTERN;return a?.source&&GR("^"+a.source+"$",e.value)&&n.push(i),n},[])}}});var y0,Dc,fM=R(()=>{"use strict";Sc();Il();y0=class{static{o(this,"DefaultValueConverter")}convert(e,r){let n=r.grammarSource;if(Kd(n)&&(n=UR(n)),Ml(n)){let i=n.rule.ref;if(!i)throw new Error("This cst node was not parsed by a rule.");return this.runConverter(i,e,r)}return e}runConverter(e,r,n){var i;switch(e.name.toUpperCase()){case"INT":return Dc.convertInt(r);case"STRING":return Dc.convertString(r);case"ID":return Dc.convertID(r)}switch((i=QR(e))===null||i===void 0?void 0:i.toLowerCase()){case"number":return Dc.convertNumber(r);case"boolean":return Dc.convertBoolean(r);case"bigint":return Dc.convertBigint(r);case"date":return Dc.convertDate(r);default:return r}}};(function(t){function e(h){let f="";for(let d=1;d{"use strict";Object.defineProperty(mM,"__esModule",{value:!0});var dM;function pM(){if(dM===void 0)throw new Error("No runtime abstraction layer installed");return dM}o(pM,"RAL");(function(t){function e(r){if(r===void 0)throw new Error("No runtime abstraction layer provided");dM=r}o(e,"install"),t.install=e})(pM||(pM={}));mM.default=pM});var coe=gi(Pa=>{"use strict";Object.defineProperty(Pa,"__esModule",{value:!0});Pa.stringArray=Pa.array=Pa.func=Pa.error=Pa.number=Pa.string=Pa.boolean=void 0;function zOe(t){return t===!0||t===!1}o(zOe,"boolean");Pa.boolean=zOe;function ooe(t){return typeof t=="string"||t instanceof String}o(ooe,"string");Pa.string=ooe;function GOe(t){return typeof t=="number"||t instanceof Number}o(GOe,"number");Pa.number=GOe;function $Oe(t){return t instanceof Error}o($Oe,"error");Pa.error=$Oe;function VOe(t){return typeof t=="function"}o(VOe,"func");Pa.func=VOe;function loe(t){return Array.isArray(t)}o(loe,"array");Pa.array=loe;function UOe(t){return loe(t)&&t.every(e=>ooe(e))}o(UOe,"stringArray");Pa.stringArray=UOe});var vM=gi(vg=>{"use strict";Object.defineProperty(vg,"__esModule",{value:!0});vg.Emitter=vg.Event=void 0;var HOe=gM(),uoe;(function(t){let e={dispose(){}};t.None=function(){return e}})(uoe||(vg.Event=uoe={}));var yM=class{static{o(this,"CallbackList")}add(e,r=null,n){this._callbacks||(this._callbacks=[],this._contexts=[]),this._callbacks.push(e),this._contexts.push(r),Array.isArray(n)&&n.push({dispose:o(()=>this.remove(e,r),"dispose")})}remove(e,r=null){if(!this._callbacks)return;let n=!1;for(let i=0,a=this._callbacks.length;i{this._callbacks||(this._callbacks=new yM),this._options&&this._options.onFirstListenerAdd&&this._callbacks.isEmpty()&&this._options.onFirstListenerAdd(this),this._callbacks.add(e,r);let i={dispose:o(()=>{this._callbacks&&(this._callbacks.remove(e,r),i.dispose=t._noop,this._options&&this._options.onLastListenerRemove&&this._callbacks.isEmpty()&&this._options.onLastListenerRemove(this))},"dispose")};return Array.isArray(n)&&n.push(i),i}),this._event}fire(e){this._callbacks&&this._callbacks.invoke.call(this._callbacks,e)}dispose(){this._callbacks&&(this._callbacks.dispose(),this._callbacks=void 0)}};vg.Emitter=Lk;Lk._noop=function(){}});var hoe=gi(xg=>{"use strict";Object.defineProperty(xg,"__esModule",{value:!0});xg.CancellationTokenSource=xg.CancellationToken=void 0;var YOe=gM(),WOe=coe(),xM=vM(),Dk;(function(t){t.None=Object.freeze({isCancellationRequested:!1,onCancellationRequested:xM.Event.None}),t.Cancelled=Object.freeze({isCancellationRequested:!0,onCancellationRequested:xM.Event.None});function e(r){let n=r;return n&&(n===t.None||n===t.Cancelled||WOe.boolean(n.isCancellationRequested)&&!!n.onCancellationRequested)}o(e,"is"),t.is=e})(Dk||(xg.CancellationToken=Dk={}));var qOe=Object.freeze(function(t,e){let r=(0,YOe.default)().timer.setTimeout(t.bind(e),0);return{dispose(){r.dispose()}}}),Rk=class{static{o(this,"MutableToken")}constructor(){this._isCancelled=!1}cancel(){this._isCancelled||(this._isCancelled=!0,this._emitter&&(this._emitter.fire(void 0),this.dispose()))}get isCancellationRequested(){return this._isCancelled}get onCancellationRequested(){return this._isCancelled?qOe:(this._emitter||(this._emitter=new xM.Emitter),this._emitter.event)}dispose(){this._emitter&&(this._emitter.dispose(),this._emitter=void 0)}},bM=class{static{o(this,"CancellationTokenSource")}get token(){return this._token||(this._token=new Rk),this._token}cancel(){this._token?this._token.cancel():this._token=Dk.Cancelled}dispose(){this._token?this._token instanceof Rk&&this._token.dispose():this._token=Dk.None}};xg.CancellationTokenSource=bM});var pr={};var Wo=R(()=>{"use strict";dr(pr,Xi(hoe(),1))});function TM(){return new Promise(t=>{typeof setImmediate>"u"?setTimeout(t,0):setImmediate(t)})}function doe(){return wM=Date.now(),new pr.CancellationTokenSource}function poe(t){foe=t}function of(t){return t===Rc}async function Bi(t){if(t===pr.CancellationToken.None)return;let e=Date.now();if(e-wM>=foe&&(wM=e,await TM()),t.isCancellationRequested)throw Rc}var wM,foe,Rc,as,qo=R(()=>{"use strict";Wo();o(TM,"delayNextTick");wM=0,foe=10;o(doe,"startCancelableOperation");o(poe,"setInterruptionPeriod");Rc=Symbol("OperationCancelled");o(of,"isOperationCancelled");o(Bi,"interruptAndCheck");as=class{static{o(this,"Deferred")}constructor(){this.promise=new Promise((e,r)=>{this.resolve=n=>(e(n),this),this.reject=n=>(r(n),this)})}}});function kM(t,e){if(t.length<=1)return t;let r=t.length/2|0,n=t.slice(0,r),i=t.slice(r);kM(n,e),kM(i,e);let a=0,s=0,l=0;for(;ar.line||e.line===r.line&&e.character>r.character?{start:r,end:e}:t}function XOe(t){let e=yoe(t.range);return e!==t.range?{newText:t.newText,range:e}:t}var Nk,bg,voe=R(()=>{"use strict";Nk=class t{static{o(this,"FullTextDocument")}constructor(e,r,n,i){this._uri=e,this._languageId=r,this._version=n,this._content=i,this._lineOffsets=void 0}get uri(){return this._uri}get languageId(){return this._languageId}get version(){return this._version}getText(e){if(e){let r=this.offsetAt(e.start),n=this.offsetAt(e.end);return this._content.substring(r,n)}return this._content}update(e,r){for(let n of e)if(t.isIncremental(n)){let i=yoe(n.range),a=this.offsetAt(i.start),s=this.offsetAt(i.end);this._content=this._content.substring(0,a)+n.text+this._content.substring(s,this._content.length);let l=Math.max(i.start.line,0),u=Math.max(i.end.line,0),h=this._lineOffsets,f=moe(n.text,!1,a);if(u-l===f.length)for(let p=0,m=f.length;pe?i=s:n=s+1}let a=n-1;return e=this.ensureBeforeEOL(e,r[a]),{line:a,character:e-r[a]}}offsetAt(e){let r=this.getLineOffsets();if(e.line>=r.length)return this._content.length;if(e.line<0)return 0;let n=r[e.line];if(e.character<=0)return n;let i=e.line+1r&&goe(this._content.charCodeAt(e-1));)e--;return e}get lineCount(){return this.getLineOffsets().length}static isIncremental(e){let r=e;return r!=null&&typeof r.text=="string"&&r.range!==void 0&&(r.rangeLength===void 0||typeof r.rangeLength=="number")}static isFull(e){let r=e;return r!=null&&typeof r.text=="string"&&r.range===void 0&&r.rangeLength===void 0}};(function(t){function e(i,a,s,l){return new Nk(i,a,s,l)}o(e,"create"),t.create=e;function r(i,a,s){if(i instanceof Nk)return i.update(a,s),i;throw new Error("TextDocument.update: document must be created by TextDocument.create")}o(r,"update"),t.update=r;function n(i,a){let s=i.getText(),l=kM(a.map(XOe),(f,d)=>{let p=f.range.start.line-d.range.start.line;return p===0?f.range.start.character-d.range.start.character:p}),u=0,h=[];for(let f of l){let d=i.offsetAt(f.range.start);if(du&&h.push(s.substring(u,d)),f.newText.length&&h.push(f.newText),u=i.offsetAt(f.range.end)}return h.push(s.substr(u)),h.join("")}o(n,"applyEdits"),t.applyEdits=n})(bg||(bg={}));o(kM,"mergeSort");o(moe,"computeLineOffsets");o(goe,"isEOL");o(yoe,"getWellformedRange");o(XOe,"getWellformedEdit")});var xoe,Ms,wg,EM=R(()=>{"use strict";(()=>{"use strict";var t={470:i=>{function a(u){if(typeof u!="string")throw new TypeError("Path must be a string. Received "+JSON.stringify(u))}o(a,"e");function s(u,h){for(var f,d="",p=0,m=-1,g=0,y=0;y<=u.length;++y){if(y2){var v=d.lastIndexOf("/");if(v!==d.length-1){v===-1?(d="",p=0):p=(d=d.slice(0,v)).length-1-d.lastIndexOf("/"),m=y,g=0;continue}}else if(d.length===2||d.length===1){d="",p=0,m=y,g=0;continue}}h&&(d.length>0?d+="/..":d="..",p=2)}else d.length>0?d+="/"+u.slice(m+1,y):d=u.slice(m+1,y),p=y-m-1;m=y,g=0}else f===46&&g!==-1?++g:g=-1}return d}o(s,"r");var l={resolve:o(function(){for(var u,h="",f=!1,d=arguments.length-1;d>=-1&&!f;d--){var p;d>=0?p=arguments[d]:(u===void 0&&(u=process.cwd()),p=u),a(p),p.length!==0&&(h=p+"/"+h,f=p.charCodeAt(0)===47)}return h=s(h,!f),f?h.length>0?"/"+h:"/":h.length>0?h:"."},"resolve"),normalize:o(function(u){if(a(u),u.length===0)return".";var h=u.charCodeAt(0)===47,f=u.charCodeAt(u.length-1)===47;return(u=s(u,!h)).length!==0||h||(u="."),u.length>0&&f&&(u+="/"),h?"/"+u:u},"normalize"),isAbsolute:o(function(u){return a(u),u.length>0&&u.charCodeAt(0)===47},"isAbsolute"),join:o(function(){if(arguments.length===0)return".";for(var u,h=0;h0&&(u===void 0?u=f:u+="/"+f)}return u===void 0?".":l.normalize(u)},"join"),relative:o(function(u,h){if(a(u),a(h),u===h||(u=l.resolve(u))===(h=l.resolve(h)))return"";for(var f=1;fy){if(h.charCodeAt(m+x)===47)return h.slice(m+x+1);if(x===0)return h.slice(m+x)}else p>y&&(u.charCodeAt(f+x)===47?v=x:x===0&&(v=0));break}var b=u.charCodeAt(f+x);if(b!==h.charCodeAt(m+x))break;b===47&&(v=x)}var w="";for(x=f+v+1;x<=d;++x)x!==d&&u.charCodeAt(x)!==47||(w.length===0?w+="..":w+="/..");return w.length>0?w+h.slice(m+v):(m+=v,h.charCodeAt(m)===47&&++m,h.slice(m))},"relative"),_makeLong:o(function(u){return u},"_makeLong"),dirname:o(function(u){if(a(u),u.length===0)return".";for(var h=u.charCodeAt(0),f=h===47,d=-1,p=!0,m=u.length-1;m>=1;--m)if((h=u.charCodeAt(m))===47){if(!p){d=m;break}}else p=!1;return d===-1?f?"/":".":f&&d===1?"//":u.slice(0,d)},"dirname"),basename:o(function(u,h){if(h!==void 0&&typeof h!="string")throw new TypeError('"ext" argument must be a string');a(u);var f,d=0,p=-1,m=!0;if(h!==void 0&&h.length>0&&h.length<=u.length){if(h.length===u.length&&h===u)return"";var g=h.length-1,y=-1;for(f=u.length-1;f>=0;--f){var v=u.charCodeAt(f);if(v===47){if(!m){d=f+1;break}}else y===-1&&(m=!1,y=f+1),g>=0&&(v===h.charCodeAt(g)?--g==-1&&(p=f):(g=-1,p=y))}return d===p?p=y:p===-1&&(p=u.length),u.slice(d,p)}for(f=u.length-1;f>=0;--f)if(u.charCodeAt(f)===47){if(!m){d=f+1;break}}else p===-1&&(m=!1,p=f+1);return p===-1?"":u.slice(d,p)},"basename"),extname:o(function(u){a(u);for(var h=-1,f=0,d=-1,p=!0,m=0,g=u.length-1;g>=0;--g){var y=u.charCodeAt(g);if(y!==47)d===-1&&(p=!1,d=g+1),y===46?h===-1?h=g:m!==1&&(m=1):h!==-1&&(m=-1);else if(!p){f=g+1;break}}return h===-1||d===-1||m===0||m===1&&h===d-1&&h===f+1?"":u.slice(h,d)},"extname"),format:o(function(u){if(u===null||typeof u!="object")throw new TypeError('The "pathObject" argument must be of type Object. Received type '+typeof u);return function(h,f){var d=f.dir||f.root,p=f.base||(f.name||"")+(f.ext||"");return d?d===f.root?d+p:d+"/"+p:p}(0,u)},"format"),parse:o(function(u){a(u);var h={root:"",dir:"",base:"",ext:"",name:""};if(u.length===0)return h;var f,d=u.charCodeAt(0),p=d===47;p?(h.root="/",f=1):f=0;for(var m=-1,g=0,y=-1,v=!0,x=u.length-1,b=0;x>=f;--x)if((d=u.charCodeAt(x))!==47)y===-1&&(v=!1,y=x+1),d===46?m===-1?m=x:b!==1&&(b=1):m!==-1&&(b=-1);else if(!v){g=x+1;break}return m===-1||y===-1||b===0||b===1&&m===y-1&&m===g+1?y!==-1&&(h.base=h.name=g===0&&p?u.slice(1,y):u.slice(g,y)):(g===0&&p?(h.name=u.slice(1,m),h.base=u.slice(1,y)):(h.name=u.slice(g,m),h.base=u.slice(g,y)),h.ext=u.slice(m,y)),g>0?h.dir=u.slice(0,g-1):p&&(h.dir="/"),h},"parse"),sep:"/",delimiter:":",win32:null,posix:null};l.posix=l,i.exports=l}},e={};function r(i){var a=e[i];if(a!==void 0)return a.exports;var s=e[i]={exports:{}};return t[i](s,s.exports,r),s.exports}o(r,"r"),r.d=(i,a)=>{for(var s in a)r.o(a,s)&&!r.o(i,s)&&Object.defineProperty(i,s,{enumerable:!0,get:a[s]})},r.o=(i,a)=>Object.prototype.hasOwnProperty.call(i,a),r.r=i=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(i,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(i,"__esModule",{value:!0})};var n={};(()=>{let i;r.r(n),r.d(n,{URI:o(()=>p,"URI"),Utils:o(()=>M,"Utils")}),typeof process=="object"?i=process.platform==="win32":typeof navigator=="object"&&(i=navigator.userAgent.indexOf("Windows")>=0);let a=/^\w[\w\d+.-]*$/,s=/^\//,l=/^\/\//;function u(N,k){if(!N.scheme&&k)throw new Error(`[UriError]: Scheme is missing: {scheme: "", authority: "${N.authority}", path: "${N.path}", query: "${N.query}", fragment: "${N.fragment}"}`);if(N.scheme&&!a.test(N.scheme))throw new Error("[UriError]: Scheme contains illegal characters.");if(N.path){if(N.authority){if(!s.test(N.path))throw new Error('[UriError]: If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character')}else if(l.test(N.path))throw new Error('[UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")')}}o(u,"s");let h="",f="/",d=/^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;class p{static{o(this,"f")}static isUri(k){return k instanceof p||!!k&&typeof k.authority=="string"&&typeof k.fragment=="string"&&typeof k.path=="string"&&typeof k.query=="string"&&typeof k.scheme=="string"&&typeof k.fsPath=="string"&&typeof k.with=="function"&&typeof k.toString=="function"}scheme;authority;path;query;fragment;constructor(k,I,C,O,D,P=!1){typeof k=="object"?(this.scheme=k.scheme||h,this.authority=k.authority||h,this.path=k.path||h,this.query=k.query||h,this.fragment=k.fragment||h):(this.scheme=function(F,B){return F||B?F:"file"}(k,P),this.authority=I||h,this.path=function(F,B){switch(F){case"https":case"http":case"file":B?B[0]!==f&&(B=f+B):B=f}return B}(this.scheme,C||h),this.query=O||h,this.fragment=D||h,u(this,P))}get fsPath(){return b(this,!1)}with(k){if(!k)return this;let{scheme:I,authority:C,path:O,query:D,fragment:P}=k;return I===void 0?I=this.scheme:I===null&&(I=h),C===void 0?C=this.authority:C===null&&(C=h),O===void 0?O=this.path:O===null&&(O=h),D===void 0?D=this.query:D===null&&(D=h),P===void 0?P=this.fragment:P===null&&(P=h),I===this.scheme&&C===this.authority&&O===this.path&&D===this.query&&P===this.fragment?this:new g(I,C,O,D,P)}static parse(k,I=!1){let C=d.exec(k);return C?new g(C[2]||h,E(C[4]||h),E(C[5]||h),E(C[7]||h),E(C[9]||h),I):new g(h,h,h,h,h)}static file(k){let I=h;if(i&&(k=k.replace(/\\/g,f)),k[0]===f&&k[1]===f){let C=k.indexOf(f,2);C===-1?(I=k.substring(2),k=f):(I=k.substring(2,C),k=k.substring(C)||f)}return new g("file",I,k,h,h)}static from(k){let I=new g(k.scheme,k.authority,k.path,k.query,k.fragment);return u(I,!0),I}toString(k=!1){return w(this,k)}toJSON(){return this}static revive(k){if(k){if(k instanceof p)return k;{let I=new g(k);return I._formatted=k.external,I._fsPath=k._sep===m?k.fsPath:null,I}}return k}}let m=i?1:void 0;class g extends p{static{o(this,"l")}_formatted=null;_fsPath=null;get fsPath(){return this._fsPath||(this._fsPath=b(this,!1)),this._fsPath}toString(k=!1){return k?w(this,!0):(this._formatted||(this._formatted=w(this,!1)),this._formatted)}toJSON(){let k={$mid:1};return this._fsPath&&(k.fsPath=this._fsPath,k._sep=m),this._formatted&&(k.external=this._formatted),this.path&&(k.path=this.path),this.scheme&&(k.scheme=this.scheme),this.authority&&(k.authority=this.authority),this.query&&(k.query=this.query),this.fragment&&(k.fragment=this.fragment),k}}let y={58:"%3A",47:"%2F",63:"%3F",35:"%23",91:"%5B",93:"%5D",64:"%40",33:"%21",36:"%24",38:"%26",39:"%27",40:"%28",41:"%29",42:"%2A",43:"%2B",44:"%2C",59:"%3B",61:"%3D",32:"%20"};function v(N,k,I){let C,O=-1;for(let D=0;D=97&&P<=122||P>=65&&P<=90||P>=48&&P<=57||P===45||P===46||P===95||P===126||k&&P===47||I&&P===91||I&&P===93||I&&P===58)O!==-1&&(C+=encodeURIComponent(N.substring(O,D)),O=-1),C!==void 0&&(C+=N.charAt(D));else{C===void 0&&(C=N.substr(0,D));let F=y[P];F!==void 0?(O!==-1&&(C+=encodeURIComponent(N.substring(O,D)),O=-1),C+=F):O===-1&&(O=D)}}return O!==-1&&(C+=encodeURIComponent(N.substring(O))),C!==void 0?C:N}o(v,"d");function x(N){let k;for(let I=0;I1&&N.scheme==="file"?`//${N.authority}${N.path}`:N.path.charCodeAt(0)===47&&(N.path.charCodeAt(1)>=65&&N.path.charCodeAt(1)<=90||N.path.charCodeAt(1)>=97&&N.path.charCodeAt(1)<=122)&&N.path.charCodeAt(2)===58?k?N.path.substr(1):N.path[1].toLowerCase()+N.path.substr(2):N.path,i&&(I=I.replace(/\//g,"\\")),I}o(b,"m");function w(N,k){let I=k?x:v,C="",{scheme:O,authority:D,path:P,query:F,fragment:B}=N;if(O&&(C+=O,C+=":"),(D||O==="file")&&(C+=f,C+=f),D){let $=D.indexOf("@");if($!==-1){let z=D.substr(0,$);D=D.substr($+1),$=z.lastIndexOf(":"),$===-1?C+=I(z,!1,!1):(C+=I(z.substr(0,$),!1,!1),C+=":",C+=I(z.substr($+1),!1,!0)),C+="@"}D=D.toLowerCase(),$=D.lastIndexOf(":"),$===-1?C+=I(D,!1,!0):(C+=I(D.substr(0,$),!1,!0),C+=D.substr($))}if(P){if(P.length>=3&&P.charCodeAt(0)===47&&P.charCodeAt(2)===58){let $=P.charCodeAt(1);$>=65&&$<=90&&(P=`/${String.fromCharCode($+32)}:${P.substr(3)}`)}else if(P.length>=2&&P.charCodeAt(1)===58){let $=P.charCodeAt(0);$>=65&&$<=90&&(P=`${String.fromCharCode($+32)}:${P.substr(2)}`)}C+=I(P,!0,!1)}return F&&(C+="?",C+=I(F,!1,!1)),B&&(C+="#",C+=k?B:v(B,!1,!1)),C}o(w,"y");function S(N){try{return decodeURIComponent(N)}catch{return N.length>3?N.substr(0,3)+S(N.substr(3)):N}}o(S,"v");let T=/(%[0-9A-Za-z][0-9A-Za-z])+/g;function E(N){return N.match(T)?N.replace(T,k=>S(k)):N}o(E,"C");var _=r(470);let A=_.posix||_,L="/";var M;(function(N){N.joinPath=function(k,...I){return k.with({path:A.join(k.path,...I)})},N.resolvePath=function(k,...I){let C=k.path,O=!1;C[0]!==L&&(C=L+C,O=!0);let D=A.resolve(C,...I);return O&&D[0]===L&&!k.authority&&(D=D.substring(1)),k.with({path:D})},N.dirname=function(k){if(k.path.length===0||k.path===L)return k;let I=A.dirname(k.path);return I.length===1&&I.charCodeAt(0)===46&&(I=""),k.with({path:I})},N.basename=function(k){return A.basename(k.path)},N.extname=function(k){return A.extname(k.path)}})(M||(M={}))})(),xoe=n})();({URI:Ms,Utils:wg}=xoe)});var ss,Nc=R(()=>{"use strict";EM();(function(t){t.basename=wg.basename,t.dirname=wg.dirname,t.extname=wg.extname,t.joinPath=wg.joinPath,t.resolvePath=wg.resolvePath;function e(n,i){return n?.toString()===i?.toString()}o(e,"equals"),t.equals=e;function r(n,i){let a=typeof n=="string"?n:n.path,s=typeof i=="string"?i:i.path,l=a.split("/").filter(p=>p.length>0),u=s.split("/").filter(p=>p.length>0),h=0;for(;h{"use strict";voe();Tg();Wo();Ds();Nc();(function(t){t[t.Changed=0]="Changed",t[t.Parsed=1]="Parsed",t[t.IndexedContent=2]="IndexedContent",t[t.ComputedScopes=3]="ComputedScopes",t[t.Linked=4]="Linked",t[t.IndexedReferences=5]="IndexedReferences",t[t.Validated=6]="Validated"})(yn||(yn={}));R2=class{static{o(this,"DefaultLangiumDocumentFactory")}constructor(e){this.serviceRegistry=e.ServiceRegistry,this.textDocuments=e.workspace.TextDocuments,this.fileSystemProvider=e.workspace.FileSystemProvider}async fromUri(e,r=pr.CancellationToken.None){let n=await this.fileSystemProvider.readFile(e);return this.createAsync(e,n,r)}fromTextDocument(e,r,n){return r=r??Ms.parse(e.uri),n?this.createAsync(r,e,n):this.create(r,e)}fromString(e,r,n){return n?this.createAsync(r,e,n):this.create(r,e)}fromModel(e,r){return this.create(r,{$model:e})}create(e,r){if(typeof r=="string"){let n=this.parse(e,r);return this.createLangiumDocument(n,e,void 0,r)}else if("$model"in r){let n={value:r.$model,parserErrors:[],lexerErrors:[]};return this.createLangiumDocument(n,e)}else{let n=this.parse(e,r.getText());return this.createLangiumDocument(n,e,r)}}async createAsync(e,r,n){if(typeof r=="string"){let i=await this.parseAsync(e,r,n);return this.createLangiumDocument(i,e,void 0,r)}else{let i=await this.parseAsync(e,r.getText(),n);return this.createLangiumDocument(i,e,r)}}createLangiumDocument(e,r,n,i){let a;if(n)a={parseResult:e,uri:r,state:yn.Parsed,references:[],textDocument:n};else{let s=this.createTextDocumentGetter(r,i);a={parseResult:e,uri:r,state:yn.Parsed,references:[],get textDocument(){return s()}}}return e.value.$document=a,a}async update(e,r){var n,i;let a=(n=e.parseResult.value.$cstNode)===null||n===void 0?void 0:n.root.fullText,s=(i=this.textDocuments)===null||i===void 0?void 0:i.get(e.uri.toString()),l=s?s.getText():await this.fileSystemProvider.readFile(e.uri);if(s)Object.defineProperty(e,"textDocument",{value:s});else{let u=this.createTextDocumentGetter(e.uri,l);Object.defineProperty(e,"textDocument",{get:u})}return a!==l&&(e.parseResult=await this.parseAsync(e.uri,l,r),e.parseResult.value.$document=e),e.state=yn.Parsed,e}parse(e,r){return this.serviceRegistry.getServices(e).parser.LangiumParser.parse(r)}parseAsync(e,r,n){return this.serviceRegistry.getServices(e).parser.AsyncParser.parse(r,n)}createTextDocumentGetter(e,r){let n=this.serviceRegistry,i;return()=>i??(i=bg.create(e.toString(),n.getServices(e).LanguageMetaData.languageId,0,r??""))}},N2=class{static{o(this,"DefaultLangiumDocuments")}constructor(e){this.documentMap=new Map,this.langiumDocumentFactory=e.workspace.LangiumDocumentFactory}get all(){return Kr(this.documentMap.values())}addDocument(e){let r=e.uri.toString();if(this.documentMap.has(r))throw new Error(`A document with the URI '${r}' is already present.`);this.documentMap.set(r,e)}getDocument(e){let r=e.toString();return this.documentMap.get(r)}async getOrCreateDocument(e,r){let n=this.getDocument(e);return n||(n=await this.langiumDocumentFactory.fromUri(e,r),this.addDocument(n),n)}createDocument(e,r,n){if(n)return this.langiumDocumentFactory.fromString(r,e,n).then(i=>(this.addDocument(i),i));{let i=this.langiumDocumentFactory.fromString(r,e);return this.addDocument(i),i}}hasDocument(e){return this.documentMap.has(e.toString())}invalidateDocument(e){let r=e.toString(),n=this.documentMap.get(r);return n&&(n.state=yn.Changed,n.precomputedScopes=void 0,n.references=[],n.diagnostics=void 0),n}deleteDocument(e){let r=e.toString(),n=this.documentMap.get(r);return n&&(n.state=yn.Changed,this.documentMap.delete(r)),n}}});var M2,CM=R(()=>{"use strict";Wo();Vo();es();qo();Tg();M2=class{static{o(this,"DefaultLinker")}constructor(e){this.reflection=e.shared.AstReflection,this.langiumDocuments=()=>e.shared.workspace.LangiumDocuments,this.scopeProvider=e.references.ScopeProvider,this.astNodeLocator=e.workspace.AstNodeLocator}async link(e,r=pr.CancellationToken.None){for(let n of Yo(e.parseResult.value))await Bi(r),$m(n).forEach(i=>this.doLink(i,e))}doLink(e,r){let n=e.reference;if(n._ref===void 0)try{let i=this.getCandidate(e);if(Wd(i))n._ref=i;else if(n._nodeDescription=i,this.langiumDocuments().hasDocument(i.documentUri)){let a=this.loadAstNode(i);n._ref=a??this.createLinkingError(e,i)}}catch(i){n._ref=Object.assign(Object.assign({},e),{message:`An error occurred while resolving reference to '${n.$refText}': ${i}`})}r.references.push(n)}unlink(e){for(let r of e.references)delete r._ref,delete r._nodeDescription;e.references=[]}getCandidate(e){let n=this.scopeProvider.getScope(e).getElement(e.reference.$refText);return n??this.createLinkingError(e)}buildReference(e,r,n,i){let a=this,s={$refNode:n,$refText:i,get ref(){var l;if(Xn(this._ref))return this._ref;if(ED(this._nodeDescription)){let u=a.loadAstNode(this._nodeDescription);this._ref=u??a.createLinkingError({reference:s,container:e,property:r},this._nodeDescription)}else if(this._ref===void 0){let u=a.getLinkedNode({reference:s,container:e,property:r});if(u.error&&Oi(e).state{"use strict";Il();o(boe,"isNamed");I2=class{static{o(this,"DefaultNameProvider")}getName(e){if(boe(e))return e.name}getNameNode(e){return Zv(e.$cstNode,"name")}}});var O2,AM=R(()=>{"use strict";Il();Vo();es();Rl();Ds();Nc();O2=class{static{o(this,"DefaultReferences")}constructor(e){this.nameProvider=e.references.NameProvider,this.index=e.shared.workspace.IndexManager,this.nodeLocator=e.workspace.AstNodeLocator}findDeclaration(e){if(e){let r=jR(e),n=e.astNode;if(r&&n){let i=n[r.feature];if(xa(i))return i.ref;if(Array.isArray(i)){for(let a of i)if(xa(a)&&a.$refNode&&a.$refNode.offset<=e.offset&&a.$refNode.end>=e.end)return a.ref}}if(n){let i=this.nameProvider.getNameNode(n);if(i&&(i===e||SD(e,i)))return n}}}findDeclarationNode(e){let r=this.findDeclaration(e);if(r?.$cstNode){let n=this.nameProvider.getNameNode(r);return n??r.$cstNode}}findReferences(e,r){let n=[];if(r.includeDeclaration){let a=this.getReferenceToSelf(e);a&&n.push(a)}let i=this.index.findAllReferences(e,this.nodeLocator.getAstNodePath(e));return r.documentUri&&(i=i.filter(a=>ss.equals(a.sourceUri,r.documentUri))),n.push(...i),Kr(n)}getReferenceToSelf(e){let r=this.nameProvider.getNameNode(e);if(r){let n=Oi(e),i=this.nodeLocator.getAstNodePath(e);return{sourceUri:n.uri,sourcePath:i,targetUri:n.uri,targetPath:i,segment:Xd(r),local:!0}}}}});var Mc,v0,kg=R(()=>{"use strict";Ds();Mc=class{static{o(this,"MultiMap")}constructor(e){if(this.map=new Map,e)for(let[r,n]of e)this.add(r,n)}get size(){return Fm.sum(Kr(this.map.values()).map(e=>e.length))}clear(){this.map.clear()}delete(e,r){if(r===void 0)return this.map.delete(e);{let n=this.map.get(e);if(n){let i=n.indexOf(r);if(i>=0)return n.length===1?this.map.delete(e):n.splice(i,1),!0}return!1}}get(e){var r;return(r=this.map.get(e))!==null&&r!==void 0?r:[]}has(e,r){if(r===void 0)return this.map.has(e);{let n=this.map.get(e);return n?n.indexOf(r)>=0:!1}}add(e,r){return this.map.has(e)?this.map.get(e).push(r):this.map.set(e,[r]),this}addAll(e,r){return this.map.has(e)?this.map.get(e).push(...r):this.map.set(e,Array.from(r)),this}forEach(e){this.map.forEach((r,n)=>r.forEach(i=>e(i,n,this)))}[Symbol.iterator](){return this.entries().iterator()}entries(){return Kr(this.map.entries()).flatMap(([e,r])=>r.map(n=>[e,n]))}keys(){return Kr(this.map.keys())}values(){return Kr(this.map.values()).flat()}entriesGroupedByKey(){return Kr(this.map.entries())}},v0=class{static{o(this,"BiMap")}get size(){return this.map.size}constructor(e){if(this.map=new Map,this.inverse=new Map,e)for(let[r,n]of e)this.set(r,n)}clear(){this.map.clear(),this.inverse.clear()}set(e,r){return this.map.set(e,r),this.inverse.set(r,e),this}get(e){return this.map.get(e)}getKey(e){return this.inverse.get(e)}delete(e){let r=this.map.get(e);return r!==void 0?(this.map.delete(e),this.inverse.delete(r),!0):!1}}});var P2,_M=R(()=>{"use strict";Wo();es();kg();qo();P2=class{static{o(this,"DefaultScopeComputation")}constructor(e){this.nameProvider=e.references.NameProvider,this.descriptions=e.workspace.AstNodeDescriptionProvider}async computeExports(e,r=pr.CancellationToken.None){return this.computeExportsForNode(e.parseResult.value,e,void 0,r)}async computeExportsForNode(e,r,n=Wv,i=pr.CancellationToken.None){let a=[];this.exportNode(e,a,r);for(let s of n(e))await Bi(i),this.exportNode(s,a,r);return a}exportNode(e,r,n){let i=this.nameProvider.getName(e);i&&r.push(this.descriptions.createDescription(e,i,n))}async computeLocalScopes(e,r=pr.CancellationToken.None){let n=e.parseResult.value,i=new Mc;for(let a of Ac(n))await Bi(r),this.processNode(a,e,i);return i}processNode(e,r,n){let i=e.$container;if(i){let a=this.nameProvider.getName(e);a&&n.add(i,this.descriptions.createDescription(e,a,r))}}}});var Eg,B2,jOe,LM=R(()=>{"use strict";Ds();Eg=class{static{o(this,"StreamScope")}constructor(e,r,n){var i;this.elements=e,this.outerScope=r,this.caseInsensitive=(i=n?.caseInsensitive)!==null&&i!==void 0?i:!1}getAllElements(){return this.outerScope?this.elements.concat(this.outerScope.getAllElements()):this.elements}getElement(e){let r=this.caseInsensitive?this.elements.find(n=>n.name.toLowerCase()===e.toLowerCase()):this.elements.find(n=>n.name===e);if(r)return r;if(this.outerScope)return this.outerScope.getElement(e)}},B2=class{static{o(this,"MapScope")}constructor(e,r,n){var i;this.elements=new Map,this.caseInsensitive=(i=n?.caseInsensitive)!==null&&i!==void 0?i:!1;for(let a of e){let s=this.caseInsensitive?a.name.toLowerCase():a.name;this.elements.set(s,a)}this.outerScope=r}getElement(e){let r=this.caseInsensitive?e.toLowerCase():e,n=this.elements.get(r);if(n)return n;if(this.outerScope)return this.outerScope.getElement(e)}getAllElements(){let e=Kr(this.elements.values());return this.outerScope&&(e=e.concat(this.outerScope.getAllElements())),e}},jOe={getElement(){},getAllElements(){return Gv}}});var Cg,F2,x0,Mk,Sg,Ik=R(()=>{"use strict";Cg=class{static{o(this,"DisposableCache")}constructor(){this.toDispose=[],this.isDisposed=!1}onDispose(e){this.toDispose.push(e)}dispose(){this.throwIfDisposed(),this.clear(),this.isDisposed=!0,this.toDispose.forEach(e=>e.dispose())}throwIfDisposed(){if(this.isDisposed)throw new Error("This cache has already been disposed")}},F2=class extends Cg{static{o(this,"SimpleCache")}constructor(){super(...arguments),this.cache=new Map}has(e){return this.throwIfDisposed(),this.cache.has(e)}set(e,r){this.throwIfDisposed(),this.cache.set(e,r)}get(e,r){if(this.throwIfDisposed(),this.cache.has(e))return this.cache.get(e);if(r){let n=r();return this.cache.set(e,n),n}else return}delete(e){return this.throwIfDisposed(),this.cache.delete(e)}clear(){this.throwIfDisposed(),this.cache.clear()}},x0=class extends Cg{static{o(this,"ContextCache")}constructor(e){super(),this.cache=new Map,this.converter=e??(r=>r)}has(e,r){return this.throwIfDisposed(),this.cacheForContext(e).has(r)}set(e,r,n){this.throwIfDisposed(),this.cacheForContext(e).set(r,n)}get(e,r,n){this.throwIfDisposed();let i=this.cacheForContext(e);if(i.has(r))return i.get(r);if(n){let a=n();return i.set(r,a),a}else return}delete(e,r){return this.throwIfDisposed(),this.cacheForContext(e).delete(r)}clear(e){if(this.throwIfDisposed(),e){let r=this.converter(e);this.cache.delete(r)}else this.cache.clear()}cacheForContext(e){let r=this.converter(e),n=this.cache.get(r);return n||(n=new Map,this.cache.set(r,n)),n}},Mk=class extends x0{static{o(this,"DocumentCache")}constructor(e){super(r=>r.toString()),this.onDispose(e.workspace.DocumentBuilder.onUpdate((r,n)=>{let i=r.concat(n);for(let a of i)this.clear(a)}))}},Sg=class extends F2{static{o(this,"WorkspaceCache")}constructor(e){super(),this.onDispose(e.workspace.DocumentBuilder.onUpdate(()=>{this.clear()}))}}});var z2,DM=R(()=>{"use strict";LM();es();Ds();Ik();z2=class{static{o(this,"DefaultScopeProvider")}constructor(e){this.reflection=e.shared.AstReflection,this.nameProvider=e.references.NameProvider,this.descriptions=e.workspace.AstNodeDescriptionProvider,this.indexManager=e.shared.workspace.IndexManager,this.globalScopeCache=new Sg(e.shared)}getScope(e){let r=[],n=this.reflection.getReferenceType(e),i=Oi(e.container).precomputedScopes;if(i){let s=e.container;do{let l=i.get(s);l.length>0&&r.push(Kr(l).filter(u=>this.reflection.isSubtype(u.type,n))),s=s.$container}while(s)}let a=this.getGlobalScope(n,e);for(let s=r.length-1;s>=0;s--)a=this.createScope(r[s],a);return a}createScope(e,r,n){return new Eg(Kr(e),r,n)}createScopeForNodes(e,r,n){let i=Kr(e).map(a=>{let s=this.nameProvider.getName(a);if(s)return this.descriptions.createDescription(a,s)}).nonNullable();return new Eg(i,r,n)}getGlobalScope(e,r){return this.globalScopeCache.get(e,()=>new B2(this.indexManager.allElements(e)))}}});function RM(t){return typeof t.$comment=="string"}function woe(t){return typeof t=="object"&&!!t&&("$ref"in t||"$error"in t)}var G2,Ok=R(()=>{"use strict";EM();Vo();es();Il();o(RM,"isAstNodeWithComment");o(woe,"isIntermediateReference");G2=class{static{o(this,"DefaultJsonSerializer")}constructor(e){this.ignoreProperties=new Set(["$container","$containerProperty","$containerIndex","$document","$cstNode"]),this.langiumDocuments=e.shared.workspace.LangiumDocuments,this.astNodeLocator=e.workspace.AstNodeLocator,this.nameProvider=e.references.NameProvider,this.commentProvider=e.documentation.CommentProvider}serialize(e,r={}){let n=r?.replacer,i=o((s,l)=>this.replacer(s,l,r),"defaultReplacer"),a=n?(s,l)=>n(s,l,i):i;try{return this.currentDocument=Oi(e),JSON.stringify(e,a,r?.space)}finally{this.currentDocument=void 0}}deserialize(e,r={}){let n=JSON.parse(e);return this.linkNode(n,n,r),n}replacer(e,r,{refText:n,sourceText:i,textRegions:a,comments:s,uriConverter:l}){var u,h,f,d;if(!this.ignoreProperties.has(e))if(xa(r)){let p=r.ref,m=n?r.$refText:void 0;if(p){let g=Oi(p),y="";this.currentDocument&&this.currentDocument!==g&&(l?y=l(g.uri,r):y=g.uri.toString());let v=this.astNodeLocator.getAstNodePath(p);return{$ref:`${y}#${v}`,$refText:m}}else return{$error:(h=(u=r.error)===null||u===void 0?void 0:u.message)!==null&&h!==void 0?h:"Could not resolve reference",$refText:m}}else if(Xn(r)){let p;if(a&&(p=this.addAstNodeRegionWithAssignmentsTo(Object.assign({},r)),(!e||r.$document)&&p?.$textRegion&&(p.$textRegion.documentURI=(f=this.currentDocument)===null||f===void 0?void 0:f.uri.toString())),i&&!e&&(p??(p=Object.assign({},r)),p.$sourceText=(d=r.$cstNode)===null||d===void 0?void 0:d.text),s){p??(p=Object.assign({},r));let m=this.commentProvider.getComment(r);m&&(p.$comment=m.replace(/\r/g,""))}return p??r}else return r}addAstNodeRegionWithAssignmentsTo(e){let r=o(n=>({offset:n.offset,end:n.end,length:n.length,range:n.range}),"createDocumentSegment");if(e.$cstNode){let n=e.$textRegion=r(e.$cstNode),i=n.assignments={};return Object.keys(e).filter(a=>!a.startsWith("$")).forEach(a=>{let s=YR(e.$cstNode,a).map(r);s.length!==0&&(i[a]=s)}),e}}linkNode(e,r,n,i,a,s){for(let[u,h]of Object.entries(e))if(Array.isArray(h))for(let f=0;f{"use strict";Nc();$2=class{static{o(this,"DefaultServiceRegistry")}register(e){if(!this.singleton&&!this.map){this.singleton=e;return}if(!this.map&&(this.map={},this.singleton)){for(let r of this.singleton.LanguageMetaData.fileExtensions)this.map[r]=this.singleton;this.singleton=void 0}for(let r of e.LanguageMetaData.fileExtensions)this.map[r]!==void 0&&this.map[r]!==e&&console.warn(`The file extension ${r} is used by multiple languages. It is now assigned to '${e.LanguageMetaData.languageId}'.`),this.map[r]=e}getServices(e){if(this.singleton!==void 0)return this.singleton;if(this.map===void 0)throw new Error("The service registry is empty. Use `register` to register the services of a language.");let r=ss.extname(e),n=this.map[r];if(!n)throw new Error(`The service registry contains no services for the extension '${r}'.`);return n}get all(){return this.singleton!==void 0?[this.singleton]:this.map!==void 0?Object.values(this.map):[]}}});function Pk(t){return{code:t}}var Ag,V2,U2=R(()=>{"use strict";kg();qo();Ds();o(Pk,"diagnosticData");(function(t){t.all=["fast","slow","built-in"]})(Ag||(Ag={}));V2=class{static{o(this,"ValidationRegistry")}constructor(e){this.entries=new Mc,this.reflection=e.shared.AstReflection}register(e,r=this,n="fast"){if(n==="built-in")throw new Error("The 'built-in' category is reserved for lexer, parser, and linker errors.");for(let[i,a]of Object.entries(e)){let s=a;if(Array.isArray(s))for(let l of s){let u={check:this.wrapValidationException(l,r),category:n};this.addEntry(i,u)}else if(typeof s=="function"){let l={check:this.wrapValidationException(s,r),category:n};this.addEntry(i,l)}}}wrapValidationException(e,r){return async(n,i,a)=>{try{await e.call(r,n,i,a)}catch(s){if(of(s))throw s;console.error("An error occurred during validation:",s);let l=s instanceof Error?s.message:String(s);s instanceof Error&&s.stack&&console.error(s.stack),i("error","An error occurred during validation: "+l,{node:n})}}}addEntry(e,r){if(e==="AstNode"){this.entries.add("AstNode",r);return}for(let n of this.reflection.getAllSubTypes(e))this.entries.add(n,r)}getChecks(e,r){let n=Kr(this.entries.get(e)).concat(this.entries.get("AstNode"));return r&&(n=n.filter(i=>r.includes(i.category))),n.map(i=>i.check)}}});function Toe(t){if(t.range)return t.range;let e;return typeof t.property=="string"?e=Zv(t.node.$cstNode,t.property,t.index):typeof t.keyword=="string"&&(e=qR(t.node.$cstNode,t.keyword,t.index)),e??(e=t.node.$cstNode),e?e.range:{start:{line:0,character:0},end:{line:0,character:0}}}function Bk(t){switch(t){case"error":return 1;case"warning":return 2;case"info":return 3;case"hint":return 4;default:throw new Error("Invalid diagnostic severity: "+t)}}var H2,Uu,MM=R(()=>{"use strict";Wo();Il();es();Rl();qo();U2();H2=class{static{o(this,"DefaultDocumentValidator")}constructor(e){this.validationRegistry=e.validation.ValidationRegistry,this.metadata=e.LanguageMetaData}async validateDocument(e,r={},n=pr.CancellationToken.None){let i=e.parseResult,a=[];if(await Bi(n),(!r.categories||r.categories.includes("built-in"))&&(this.processLexingErrors(i,a,r),r.stopAfterLexingErrors&&a.some(s=>{var l;return((l=s.data)===null||l===void 0?void 0:l.code)===Uu.LexingError})||(this.processParsingErrors(i,a,r),r.stopAfterParsingErrors&&a.some(s=>{var l;return((l=s.data)===null||l===void 0?void 0:l.code)===Uu.ParsingError}))||(this.processLinkingErrors(e,a,r),r.stopAfterLinkingErrors&&a.some(s=>{var l;return((l=s.data)===null||l===void 0?void 0:l.code)===Uu.LinkingError}))))return a;try{a.push(...await this.validateAst(i.value,r,n))}catch(s){if(of(s))throw s;console.error("An error occurred during validation:",s)}return await Bi(n),a}processLexingErrors(e,r,n){for(let i of e.lexerErrors){let a={severity:Bk("error"),range:{start:{line:i.line-1,character:i.column-1},end:{line:i.line-1,character:i.column+i.length-1}},message:i.message,data:Pk(Uu.LexingError),source:this.getSource()};r.push(a)}}processParsingErrors(e,r,n){for(let i of e.parserErrors){let a;if(isNaN(i.token.startOffset)){if("previousToken"in i){let s=i.previousToken;if(isNaN(s.startOffset)){let l={line:0,character:0};a={start:l,end:l}}else{let l={line:s.endLine-1,character:s.endColumn};a={start:l,end:l}}}}else a=zm(i.token);if(a){let s={severity:Bk("error"),range:a,message:i.message,data:Pk(Uu.ParsingError),source:this.getSource()};r.push(s)}}}processLinkingErrors(e,r,n){for(let i of e.references){let a=i.error;if(a){let s={node:a.container,property:a.property,index:a.index,data:{code:Uu.LinkingError,containerType:a.container.$type,property:a.property,refText:a.reference.$refText}};r.push(this.toDiagnostic("error",a.message,s))}}}async validateAst(e,r,n=pr.CancellationToken.None){let i=[],a=o((s,l,u)=>{i.push(this.toDiagnostic(s,l,u))},"acceptor");return await Promise.all(Yo(e).map(async s=>{await Bi(n);let l=this.validationRegistry.getChecks(s.$type,r.categories);for(let u of l)await u(s,a,n)})),i}toDiagnostic(e,r,n){return{message:r,range:Toe(n),severity:Bk(e),code:n.code,codeDescription:n.codeDescription,tags:n.tags,relatedInformation:n.relatedInformation,data:n.data,source:this.getSource()}}getSource(){return this.metadata.languageId}};o(Toe,"getDiagnosticRange");o(Bk,"toDiagnosticSeverity");(function(t){t.LexingError="lexing-error",t.ParsingError="parsing-error",t.LinkingError="linking-error"})(Uu||(Uu={}))});var Y2,W2,IM=R(()=>{"use strict";Wo();Vo();es();Rl();qo();Nc();Y2=class{static{o(this,"DefaultAstNodeDescriptionProvider")}constructor(e){this.astNodeLocator=e.workspace.AstNodeLocator,this.nameProvider=e.references.NameProvider}createDescription(e,r,n=Oi(e)){r??(r=this.nameProvider.getName(e));let i=this.astNodeLocator.getAstNodePath(e);if(!r)throw new Error(`Node at path ${i} has no name.`);let a,s=o(()=>{var l;return a??(a=Xd((l=this.nameProvider.getNameNode(e))!==null&&l!==void 0?l:e.$cstNode))},"nameSegmentGetter");return{node:e,name:r,get nameSegment(){return s()},selectionSegment:Xd(e.$cstNode),type:e.$type,documentUri:n.uri,path:i}}},W2=class{static{o(this,"DefaultReferenceDescriptionProvider")}constructor(e){this.nodeLocator=e.workspace.AstNodeLocator}async createDescriptions(e,r=pr.CancellationToken.None){let n=[],i=e.parseResult.value;for(let a of Yo(i))await Bi(r),$m(a).filter(s=>!Wd(s)).forEach(s=>{let l=this.createDescription(s);l&&n.push(l)});return n}createDescription(e){let r=e.reference.$nodeDescription,n=e.reference.$refNode;if(!r||!n)return;let i=Oi(e.container).uri;return{sourceUri:i,sourcePath:this.nodeLocator.getAstNodePath(e.container),targetUri:r.documentUri,targetPath:r.path,segment:Xd(n),local:ss.equals(r.documentUri,i)}}}});var q2,OM=R(()=>{"use strict";q2=class{static{o(this,"DefaultAstNodeLocator")}constructor(){this.segmentSeparator="/",this.indexSeparator="@"}getAstNodePath(e){if(e.$container){let r=this.getAstNodePath(e.$container),n=this.getPathSegment(e);return r+this.segmentSeparator+n}return""}getPathSegment({$containerProperty:e,$containerIndex:r}){if(!e)throw new Error("Missing '$containerProperty' in AST node.");return r!==void 0?e+this.indexSeparator+r:e}getAstNode(e,r){return r.split(this.segmentSeparator).reduce((i,a)=>{if(!i||a.length===0)return i;let s=a.indexOf(this.indexSeparator);if(s>0){let l=a.substring(0,s),u=parseInt(a.substring(s+1)),h=i[l];return h?.[u]}return i[a]},e)}}});var X2,PM=R(()=>{"use strict";qo();X2=class{static{o(this,"DefaultConfigurationProvider")}constructor(e){this._ready=new as,this.settings={},this.workspaceConfig=!1,this.serviceRegistry=e.ServiceRegistry}get ready(){return this._ready.promise}initialize(e){var r,n;this.workspaceConfig=(n=(r=e.capabilities.workspace)===null||r===void 0?void 0:r.configuration)!==null&&n!==void 0?n:!1}async initialized(e){if(this.workspaceConfig){if(e.register){let r=this.serviceRegistry.all;e.register({section:r.map(n=>this.toSectionName(n.LanguageMetaData.languageId))})}if(e.fetchConfiguration){let r=this.serviceRegistry.all.map(i=>({section:this.toSectionName(i.LanguageMetaData.languageId)})),n=await e.fetchConfiguration(r);r.forEach((i,a)=>{this.updateSectionConfiguration(i.section,n[a])})}}this._ready.resolve()}updateConfiguration(e){e.settings&&Object.keys(e.settings).forEach(r=>{this.updateSectionConfiguration(r,e.settings[r])})}updateSectionConfiguration(e,r){this.settings[e]=r}async getConfiguration(e,r){await this.ready;let n=this.toSectionName(e);if(this.settings[n])return this.settings[n][r]}toSectionName(e){return`${e}`}}});var b0,BM=R(()=>{"use strict";(function(t){function e(r){return{dispose:o(async()=>await r(),"dispose")}}o(e,"create"),t.create=e})(b0||(b0={}))});var j2,FM=R(()=>{"use strict";Wo();BM();kg();qo();Ds();U2();Tg();j2=class{static{o(this,"DefaultDocumentBuilder")}constructor(e){this.updateBuildOptions={validation:{categories:["built-in","fast"]}},this.updateListeners=[],this.buildPhaseListeners=new Mc,this.buildState=new Map,this.documentBuildWaiters=new Map,this.currentState=yn.Changed,this.langiumDocuments=e.workspace.LangiumDocuments,this.langiumDocumentFactory=e.workspace.LangiumDocumentFactory,this.indexManager=e.workspace.IndexManager,this.serviceRegistry=e.ServiceRegistry}async build(e,r={},n=pr.CancellationToken.None){var i,a;for(let s of e){let l=s.uri.toString();if(s.state===yn.Validated){if(typeof r.validation=="boolean"&&r.validation)s.state=yn.IndexedReferences,s.diagnostics=void 0,this.buildState.delete(l);else if(typeof r.validation=="object"){let u=this.buildState.get(l),h=(i=u?.result)===null||i===void 0?void 0:i.validationChecks;if(h){let d=((a=r.validation.categories)!==null&&a!==void 0?a:Ag.all).filter(p=>!h.includes(p));d.length>0&&(this.buildState.set(l,{completed:!1,options:{validation:Object.assign(Object.assign({},r.validation),{categories:d})},result:u.result}),s.state=yn.IndexedReferences)}}}else this.buildState.delete(l)}this.currentState=yn.Changed,await this.emitUpdate(e.map(s=>s.uri),[]),await this.buildDocuments(e,r,n)}async update(e,r,n=pr.CancellationToken.None){this.currentState=yn.Changed;for(let s of r)this.langiumDocuments.deleteDocument(s),this.buildState.delete(s.toString()),this.indexManager.remove(s);for(let s of e){if(!this.langiumDocuments.invalidateDocument(s)){let u=this.langiumDocumentFactory.fromModel({$type:"INVALID"},s);u.state=yn.Changed,this.langiumDocuments.addDocument(u)}this.buildState.delete(s.toString())}let i=Kr(e).concat(r).map(s=>s.toString()).toSet();this.langiumDocuments.all.filter(s=>!i.has(s.uri.toString())&&this.shouldRelink(s,i)).forEach(s=>{this.serviceRegistry.getServices(s.uri).references.Linker.unlink(s),s.state=Math.min(s.state,yn.ComputedScopes),s.diagnostics=void 0}),await this.emitUpdate(e,r),await Bi(n);let a=this.langiumDocuments.all.filter(s=>{var l;return s.staten(e,r)))}shouldRelink(e,r){return e.references.some(n=>n.error!==void 0)?!0:this.indexManager.isAffected(e,r)}onUpdate(e){return this.updateListeners.push(e),b0.create(()=>{let r=this.updateListeners.indexOf(e);r>=0&&this.updateListeners.splice(r,1)})}async buildDocuments(e,r,n){this.prepareBuild(e,r),await this.runCancelable(e,yn.Parsed,n,a=>this.langiumDocumentFactory.update(a,n)),await this.runCancelable(e,yn.IndexedContent,n,a=>this.indexManager.updateContent(a,n)),await this.runCancelable(e,yn.ComputedScopes,n,async a=>{let s=this.serviceRegistry.getServices(a.uri).references.ScopeComputation;a.precomputedScopes=await s.computeLocalScopes(a,n)}),await this.runCancelable(e,yn.Linked,n,a=>this.serviceRegistry.getServices(a.uri).references.Linker.link(a,n)),await this.runCancelable(e,yn.IndexedReferences,n,a=>this.indexManager.updateReferences(a,n));let i=e.filter(a=>this.shouldValidate(a));await this.runCancelable(i,yn.Validated,n,a=>this.validate(a,n));for(let a of e){let s=this.buildState.get(a.uri.toString());s&&(s.completed=!0)}}prepareBuild(e,r){for(let n of e){let i=n.uri.toString(),a=this.buildState.get(i);(!a||a.completed)&&this.buildState.set(i,{completed:!1,options:r,result:a?.result})}}async runCancelable(e,r,n,i){let a=e.filter(s=>s.state{this.buildPhaseListeners.delete(e,r)})}waitUntil(e,r,n){let i;if(r&&"path"in r?i=r:n=r,n??(n=pr.CancellationToken.None),i){let a=this.langiumDocuments.getDocument(i);if(a&&a.state>e)return Promise.resolve(i)}return this.currentState>=e?Promise.resolve(void 0):n.isCancellationRequested?Promise.reject(Rc):new Promise((a,s)=>{let l=this.onBuildPhase(e,()=>{if(l.dispose(),u.dispose(),i){let h=this.langiumDocuments.getDocument(i);a(h?.uri)}else a(void 0)}),u=n.onCancellationRequested(()=>{l.dispose(),u.dispose(),s(Rc)})})}async notifyBuildPhase(e,r,n){if(e.length===0)return;let i=this.buildPhaseListeners.get(r);for(let a of i)await Bi(n),await a(e,n)}shouldValidate(e){return!!this.getBuildOptions(e).validation}async validate(e,r){var n,i;let a=this.serviceRegistry.getServices(e.uri).validation.DocumentValidator,s=this.getBuildOptions(e).validation,l=typeof s=="object"?s:void 0,u=await a.validateDocument(e,l,r);e.diagnostics?e.diagnostics.push(...u):e.diagnostics=u;let h=this.buildState.get(e.uri.toString());if(h){(n=h.result)!==null&&n!==void 0||(h.result={});let f=(i=l?.categories)!==null&&i!==void 0?i:Ag.all;h.result.validationChecks?h.result.validationChecks.push(...f):h.result.validationChecks=[...f]}}getBuildOptions(e){var r,n;return(n=(r=this.buildState.get(e.uri.toString()))===null||r===void 0?void 0:r.options)!==null&&n!==void 0?n:{}}}});var K2,zM=R(()=>{"use strict";es();Ik();Wo();Ds();Nc();K2=class{static{o(this,"DefaultIndexManager")}constructor(e){this.symbolIndex=new Map,this.symbolByTypeIndex=new x0,this.referenceIndex=new Map,this.documents=e.workspace.LangiumDocuments,this.serviceRegistry=e.ServiceRegistry,this.astReflection=e.AstReflection}findAllReferences(e,r){let n=Oi(e).uri,i=[];return this.referenceIndex.forEach(a=>{a.forEach(s=>{ss.equals(s.targetUri,n)&&s.targetPath===r&&i.push(s)})}),Kr(i)}allElements(e,r){let n=Kr(this.symbolIndex.keys());return r&&(n=n.filter(i=>!r||r.has(i))),n.map(i=>this.getFileDescriptions(i,e)).flat()}getFileDescriptions(e,r){var n;return r?this.symbolByTypeIndex.get(e,r,()=>{var a;return((a=this.symbolIndex.get(e))!==null&&a!==void 0?a:[]).filter(l=>this.astReflection.isSubtype(l.type,r))}):(n=this.symbolIndex.get(e))!==null&&n!==void 0?n:[]}remove(e){let r=e.toString();this.symbolIndex.delete(r),this.symbolByTypeIndex.clear(r),this.referenceIndex.delete(r)}async updateContent(e,r=pr.CancellationToken.None){let i=await this.serviceRegistry.getServices(e.uri).references.ScopeComputation.computeExports(e,r),a=e.uri.toString();this.symbolIndex.set(a,i),this.symbolByTypeIndex.clear(a)}async updateReferences(e,r=pr.CancellationToken.None){let i=await this.serviceRegistry.getServices(e.uri).workspace.ReferenceDescriptionProvider.createDescriptions(e,r);this.referenceIndex.set(e.uri.toString(),i)}isAffected(e,r){let n=this.referenceIndex.get(e.uri.toString());return n?n.some(i=>!i.local&&r.has(i.targetUri.toString())):!1}}});var Q2,GM=R(()=>{"use strict";Wo();qo();Nc();Q2=class{static{o(this,"DefaultWorkspaceManager")}constructor(e){this.initialBuildOptions={},this._ready=new as,this.serviceRegistry=e.ServiceRegistry,this.langiumDocuments=e.workspace.LangiumDocuments,this.documentBuilder=e.workspace.DocumentBuilder,this.fileSystemProvider=e.workspace.FileSystemProvider,this.mutex=e.workspace.WorkspaceLock}get ready(){return this._ready.promise}initialize(e){var r;this.folders=(r=e.workspaceFolders)!==null&&r!==void 0?r:void 0}initialized(e){return this.mutex.write(r=>{var n;return this.initializeWorkspace((n=this.folders)!==null&&n!==void 0?n:[],r)})}async initializeWorkspace(e,r=pr.CancellationToken.None){let n=await this.performStartup(e);await Bi(r),await this.documentBuilder.build(n,this.initialBuildOptions,r)}async performStartup(e){let r=this.serviceRegistry.all.flatMap(a=>a.LanguageMetaData.fileExtensions),n=[],i=o(a=>{n.push(a),this.langiumDocuments.hasDocument(a.uri)||this.langiumDocuments.addDocument(a)},"collector");return await this.loadAdditionalDocuments(e,i),await Promise.all(e.map(a=>[a,this.getRootFolder(a)]).map(async a=>this.traverseFolder(...a,r,i))),this._ready.resolve(),n}loadAdditionalDocuments(e,r){return Promise.resolve()}getRootFolder(e){return Ms.parse(e.uri)}async traverseFolder(e,r,n,i){let a=await this.fileSystemProvider.readDirectory(r);await Promise.all(a.map(async s=>{if(this.includeEntry(e,s,n)){if(s.isDirectory)await this.traverseFolder(e,s.uri,n,i);else if(s.isFile){let l=await this.langiumDocuments.getOrCreateDocument(s.uri);i(l)}}}))}includeEntry(e,r,n){let i=ss.basename(r.uri);if(i.startsWith("."))return!1;if(r.isDirectory)return i!=="node_modules"&&i!=="out";if(r.isFile){let a=ss.extname(r.uri);return n.includes(a)}return!1}}});function koe(t){return Array.isArray(t)&&(t.length===0||"name"in t[0])}function VM(t){return t&&"modes"in t&&"defaultMode"in t}function $M(t){return!koe(t)&&!VM(t)}var Z2,UM=R(()=>{"use strict";u0();Z2=class{static{o(this,"DefaultLexer")}constructor(e){let r=e.parser.TokenBuilder.buildTokens(e.Grammar,{caseInsensitive:e.LanguageMetaData.caseInsensitive});this.tokenTypes=this.toTokenTypeDictionary(r);let n=$M(r)?Object.values(r):r;this.chevrotainLexer=new ni(n,{positionTracking:"full"})}get definition(){return this.tokenTypes}tokenize(e){var r;let n=this.chevrotainLexer.tokenize(e);return{tokens:n.tokens,errors:n.errors,hidden:(r=n.groups.hidden)!==null&&r!==void 0?r:[]}}toTokenTypeDictionary(e){if($M(e))return e;let r=VM(e)?Object.values(e.modes).flat():e,n={};return r.forEach(i=>n[i.name]=i),n}};o(koe,"isTokenTypeArray");o(VM,"isIMultiModeLexerDefinition");o($M,"isTokenTypeDictionary")});function WM(t,e,r){let n,i;typeof t=="string"?(i=e,n=r):(i=t.range.start,n=e),i||(i=Ur.create(0,0));let a=Soe(t),s=XM(n),l=QOe({lines:a,position:i,options:s});return rPe({index:0,tokens:l,position:i})}function qM(t,e){let r=XM(e),n=Soe(t);if(n.length===0)return!1;let i=n[0],a=n[n.length-1],s=r.start,l=r.end;return!!s?.exec(i)&&!!l?.exec(a)}function Soe(t){let e="";return typeof t=="string"?e=t:e=t.text,e.split(BR)}function QOe(t){var e,r,n;let i=[],a=t.position.line,s=t.position.character;for(let l=0;l=f.length){if(i.length>0){let m=Ur.create(a,s);i.push({type:"break",content:"",range:wr.create(m,m)})}}else{Eoe.lastIndex=d;let m=Eoe.exec(f);if(m){let g=m[0],y=m[1],v=Ur.create(a,s+d),x=Ur.create(a,s+d+g.length);i.push({type:"tag",content:y,range:wr.create(v,x)}),d+=g.length,d=YM(f,d)}if(d0&&i[i.length-1].type==="break"?i.slice(0,-1):i}function ZOe(t,e,r,n){let i=[];if(t.length===0){let a=Ur.create(r,n),s=Ur.create(r,n+e.length);i.push({type:"text",content:e,range:wr.create(a,s)})}else{let a=0;for(let l of t){let u=l.index,h=e.substring(a,u);h.length>0&&i.push({type:"text",content:e.substring(a,u),range:wr.create(Ur.create(r,a+n),Ur.create(r,u+n))});let f=h.length+1,d=l[1];if(i.push({type:"inline-tag",content:d,range:wr.create(Ur.create(r,a+f+n),Ur.create(r,a+f+d.length+n))}),f+=d.length,l.length===4){f+=l[2].length;let p=l[3];i.push({type:"text",content:p,range:wr.create(Ur.create(r,a+f+n),Ur.create(r,a+f+p.length+n))})}else i.push({type:"text",content:"",range:wr.create(Ur.create(r,a+f+n),Ur.create(r,a+f+n))});a=u+l[0].length}let s=e.substring(a);s.length>0&&i.push({type:"text",content:s,range:wr.create(Ur.create(r,a+n),Ur.create(r,a+n+s.length))})}return i}function YM(t,e){let r=t.substring(e).match(JOe);return r?e+r.index:t.length}function tPe(t){let e=t.match(ePe);if(e&&typeof e.index=="number")return e.index}function rPe(t){var e,r,n,i;let a=Ur.create(t.position.line,t.position.character);if(t.tokens.length===0)return new Fk([],wr.create(a,a));let s=[];for(;t.index0){let u=YM(e,a);s=e.substring(u),e=e.substring(0,a)}return(t==="linkcode"||t==="link"&&r.link==="code")&&(s=`\`${s}\``),(i=(n=r.renderLink)===null||n===void 0?void 0:n.call(r,e,s))!==null&&i!==void 0?i:oPe(e,s)}}function oPe(t,e){try{return Ms.parse(t,!0),`[${e}](${t})`}catch{return t}}function Coe(t){return t.endsWith(` +`)?` +`:` + +`}var Eoe,KOe,JOe,ePe,Fk,J2,ex,zk,jM=R(()=>{"use strict";tM();Um();Nc();o(WM,"parseJSDoc");o(qM,"isJSDoc");o(Soe,"getLines");Eoe=/\s*(@([\p{L}][\p{L}\p{N}]*)?)/uy,KOe=/\{(@[\p{L}][\p{L}\p{N}]*)(\s*)([^\r\n}]+)?\}/gu;o(QOe,"tokenize");o(ZOe,"buildInlineTokens");JOe=/\S/,ePe=/\s*$/;o(YM,"skipWhitespace");o(tPe,"lastCharacter");o(rPe,"parseJSDocComment");o(nPe,"parseJSDocElement");o(iPe,"appendEmptyLine");o(Aoe,"parseJSDocText");o(aPe,"parseJSDocInline");o(_oe,"parseJSDocTag");o(Loe,"parseJSDocLine");o(XM,"normalizeOptions");o(HM,"normalizeOption");Fk=class{static{o(this,"JSDocCommentImpl")}constructor(e,r){this.elements=e,this.range=r}getTag(e){return this.getAllTags().find(r=>r.name===e)}getTags(e){return this.getAllTags().filter(r=>r.name===e)}getAllTags(){return this.elements.filter(e=>"name"in e)}toString(){let e="";for(let r of this.elements)if(e.length===0)e=r.toString();else{let n=r.toString();e+=Coe(e)+n}return e.trim()}toMarkdown(e){let r="";for(let n of this.elements)if(r.length===0)r=n.toMarkdown(e);else{let i=n.toMarkdown(e);r+=Coe(r)+i}return r.trim()}},J2=class{static{o(this,"JSDocTagImpl")}constructor(e,r,n,i){this.name=e,this.content=r,this.inline=n,this.range=i}toString(){let e=`@${this.name}`,r=this.content.toString();return this.content.inlines.length===1?e=`${e} ${r}`:this.content.inlines.length>1&&(e=`${e} +${r}`),this.inline?`{${e}}`:e}toMarkdown(e){var r,n;return(n=(r=e?.renderTag)===null||r===void 0?void 0:r.call(e,this))!==null&&n!==void 0?n:this.toMarkdownDefault(e)}toMarkdownDefault(e){let r=this.content.toMarkdown(e);if(this.inline){let a=sPe(this.name,r,e??{});if(typeof a=="string")return a}let n="";e?.tag==="italic"||e?.tag===void 0?n="*":e?.tag==="bold"?n="**":e?.tag==="bold-italic"&&(n="***");let i=`${n}@${this.name}${n}`;return this.content.inlines.length===1?i=`${i} \u2014 ${r}`:this.content.inlines.length>1&&(i=`${i} +${r}`),this.inline?`{${i}}`:i}};o(sPe,"renderInlineTag");o(oPe,"renderLinkDefault");ex=class{static{o(this,"JSDocTextImpl")}constructor(e,r){this.inlines=e,this.range=r}toString(){let e="";for(let r=0;rn.range.start.line&&(e+=` +`)}return e}toMarkdown(e){let r="";for(let n=0;ni.range.start.line&&(r+=` +`)}return r}},zk=class{static{o(this,"JSDocLineImpl")}constructor(e,r){this.text=e,this.range=r}toString(){return this.text}toMarkdown(){return this.text}};o(Coe,"fillNewlines")});var tx,KM=R(()=>{"use strict";es();jM();tx=class{static{o(this,"JSDocDocumentationProvider")}constructor(e){this.indexManager=e.shared.workspace.IndexManager,this.commentProvider=e.documentation.CommentProvider}getDocumentation(e){let r=this.commentProvider.getComment(e);if(r&&qM(r))return WM(r).toMarkdown({renderLink:o((i,a)=>this.documentationLinkRenderer(e,i,a),"renderLink"),renderTag:o(i=>this.documentationTagRenderer(e,i),"renderTag")})}documentationLinkRenderer(e,r,n){var i;let a=(i=this.findNameInPrecomputedScopes(e,r))!==null&&i!==void 0?i:this.findNameInGlobalScope(e,r);if(a&&a.nameSegment){let s=a.nameSegment.range.start.line+1,l=a.nameSegment.range.start.character+1,u=a.documentUri.with({fragment:`L${s},${l}`});return`[${n}](${u.toString()})`}else return}documentationTagRenderer(e,r){}findNameInPrecomputedScopes(e,r){let i=Oi(e).precomputedScopes;if(!i)return;let a=e;do{let l=i.get(a).find(u=>u.name===r);if(l)return l;a=a.$container}while(a)}findNameInGlobalScope(e,r){return this.indexManager.allElements().find(i=>i.name===r)}}});var rx,QM=R(()=>{"use strict";Ok();Rl();rx=class{static{o(this,"DefaultCommentProvider")}constructor(e){this.grammarConfig=()=>e.parser.GrammarConfig}getComment(e){var r;return RM(e)?e.$comment:(r=_D(e.$cstNode,this.grammarConfig().multilineCommentRules))===null||r===void 0?void 0:r.text}}});var ii={};var ZM=R(()=>{"use strict";dr(ii,Xi(vM(),1))});var nx,JM,eI,tI=R(()=>{"use strict";qo();ZM();nx=class{static{o(this,"DefaultAsyncParser")}constructor(e){this.syncParser=e.parser.LangiumParser}parse(e){return Promise.resolve(this.syncParser.parse(e))}},JM=class{static{o(this,"AbstractThreadedAsyncParser")}constructor(e){this.threadCount=8,this.terminationDelay=200,this.workerPool=[],this.queue=[],this.hydrator=e.serializer.Hydrator}initializeWorkers(){for(;this.workerPool.length{if(this.queue.length>0){let r=this.queue.shift();r&&(e.lock(),r.resolve(e))}}),this.workerPool.push(e)}}async parse(e,r){let n=await this.acquireParserWorker(r),i=new as,a,s=r.onCancellationRequested(()=>{a=setTimeout(()=>{this.terminateWorker(n)},this.terminationDelay)});return n.parse(e).then(l=>{let u=this.hydrator.hydrate(l);i.resolve(u)}).catch(l=>{i.reject(l)}).finally(()=>{s.dispose(),clearTimeout(a)}),i.promise}terminateWorker(e){e.terminate();let r=this.workerPool.indexOf(e);r>=0&&this.workerPool.splice(r,1)}async acquireParserWorker(e){this.initializeWorkers();for(let n of this.workerPool)if(n.ready)return n.lock(),n;let r=new as;return e.onCancellationRequested(()=>{let n=this.queue.indexOf(r);n>=0&&this.queue.splice(n,1),r.reject(Rc)}),this.queue.push(r),r.promise}},eI=class{static{o(this,"ParserWorker")}get ready(){return this._ready}get onReady(){return this.onReadyEmitter.event}constructor(e,r,n,i){this.onReadyEmitter=new ii.Emitter,this.deferred=new as,this._ready=!0,this._parsing=!1,this.sendMessage=e,this._terminate=i,r(a=>{let s=a;this.deferred.resolve(s),this.unlock()}),n(a=>{this.deferred.reject(a),this.unlock()})}terminate(){this.deferred.reject(Rc),this._terminate()}lock(){this._ready=!1}unlock(){this._parsing=!1,this._ready=!0,this.onReadyEmitter.fire()}parse(e){if(this._parsing)throw new Error("Parser worker is busy");return this._parsing=!0,this.deferred=new as,this.sendMessage(e),this.deferred.promise}}});var ix,rI=R(()=>{"use strict";Wo();qo();ix=class{static{o(this,"DefaultWorkspaceLock")}constructor(){this.previousTokenSource=new pr.CancellationTokenSource,this.writeQueue=[],this.readQueue=[],this.done=!0}write(e){this.cancelWrite();let r=new pr.CancellationTokenSource;return this.previousTokenSource=r,this.enqueue(this.writeQueue,e,r.token)}read(e){return this.enqueue(this.readQueue,e)}enqueue(e,r,n){let i=new as,a={action:r,deferred:i,cancellationToken:n??pr.CancellationToken.None};return e.push(a),this.performNextOperation(),i.promise}async performNextOperation(){if(!this.done)return;let e=[];if(this.writeQueue.length>0)e.push(this.writeQueue.shift());else if(this.readQueue.length>0)e.push(...this.readQueue.splice(0,this.readQueue.length));else return;this.done=!1,await Promise.all(e.map(async({action:r,deferred:n,cancellationToken:i})=>{try{let a=await Promise.resolve().then(()=>r(i));n.resolve(a)}catch(a){of(a)?n.resolve(void 0):n.reject(a)}})),this.done=!0,this.performNextOperation()}cancelWrite(){this.previousTokenSource.cancel()}}});var ax,nI=R(()=>{"use strict";Ek();Sc();Vo();es();kg();Rl();ax=class{static{o(this,"DefaultHydrator")}constructor(e){this.grammarElementIdMap=new v0,this.tokenTypeIdMap=new v0,this.grammar=e.Grammar,this.lexer=e.parser.Lexer,this.linker=e.references.Linker}dehydrate(e){return{lexerErrors:e.lexerErrors.map(r=>Object.assign({},r)),parserErrors:e.parserErrors.map(r=>Object.assign({},r)),value:this.dehydrateAstNode(e.value,this.createDehyrationContext(e.value))}}createDehyrationContext(e){let r=new Map,n=new Map;for(let i of Yo(e))r.set(i,{});if(e.$cstNode)for(let i of qd(e.$cstNode))n.set(i,{});return{astNodes:r,cstNodes:n}}dehydrateAstNode(e,r){let n=r.astNodes.get(e);n.$type=e.$type,n.$containerIndex=e.$containerIndex,n.$containerProperty=e.$containerProperty,e.$cstNode!==void 0&&(n.$cstNode=this.dehydrateCstNode(e.$cstNode,r));for(let[i,a]of Object.entries(e))if(!i.startsWith("$"))if(Array.isArray(a)){let s=[];n[i]=s;for(let l of a)Xn(l)?s.push(this.dehydrateAstNode(l,r)):xa(l)?s.push(this.dehydrateReference(l,r)):s.push(l)}else Xn(a)?n[i]=this.dehydrateAstNode(a,r):xa(a)?n[i]=this.dehydrateReference(a,r):a!==void 0&&(n[i]=a);return n}dehydrateReference(e,r){let n={};return n.$refText=e.$refText,e.$refNode&&(n.$refNode=r.cstNodes.get(e.$refNode)),n}dehydrateCstNode(e,r){let n=r.cstNodes.get(e);return zv(e)?n.fullText=e.fullText:n.grammarSource=this.getGrammarElementId(e.grammarSource),n.hidden=e.hidden,n.astNode=r.astNodes.get(e.astNode),co(e)?n.content=e.content.map(i=>this.dehydrateCstNode(i,r)):ef(e)&&(n.tokenType=e.tokenType.name,n.offset=e.offset,n.length=e.length,n.startLine=e.range.start.line,n.startColumn=e.range.start.character,n.endLine=e.range.end.line,n.endColumn=e.range.end.character),n}hydrate(e){let r=e.value,n=this.createHydrationContext(r);return"$cstNode"in r&&this.hydrateCstNode(r.$cstNode,n),{lexerErrors:e.lexerErrors,parserErrors:e.parserErrors,value:this.hydrateAstNode(r,n)}}createHydrationContext(e){let r=new Map,n=new Map;for(let a of Yo(e))r.set(a,{});let i;if(e.$cstNode)for(let a of qd(e.$cstNode)){let s;"fullText"in a?(s=new gg(a.fullText),i=s):"content"in a?s=new p0:"tokenType"in a&&(s=this.hydrateCstLeafNode(a)),s&&(n.set(a,s),s.root=i)}return{astNodes:r,cstNodes:n}}hydrateAstNode(e,r){let n=r.astNodes.get(e);n.$type=e.$type,n.$containerIndex=e.$containerIndex,n.$containerProperty=e.$containerProperty,e.$cstNode&&(n.$cstNode=r.cstNodes.get(e.$cstNode));for(let[i,a]of Object.entries(e))if(!i.startsWith("$"))if(Array.isArray(a)){let s=[];n[i]=s;for(let l of a)Xn(l)?s.push(this.setParent(this.hydrateAstNode(l,r),n)):xa(l)?s.push(this.hydrateReference(l,n,i,r)):s.push(l)}else Xn(a)?n[i]=this.setParent(this.hydrateAstNode(a,r),n):xa(a)?n[i]=this.hydrateReference(a,n,i,r):a!==void 0&&(n[i]=a);return n}setParent(e,r){return e.$container=r,e}hydrateReference(e,r,n,i){return this.linker.buildReference(r,n,i.cstNodes.get(e.$refNode),e.$refText)}hydrateCstNode(e,r,n=0){let i=r.cstNodes.get(e);if(typeof e.grammarSource=="number"&&(i.grammarSource=this.getGrammarElement(e.grammarSource)),i.astNode=r.astNodes.get(e.astNode),co(i))for(let a of e.content){let s=this.hydrateCstNode(a,r,n++);i.content.push(s)}return i}hydrateCstLeafNode(e){let r=this.getTokenType(e.tokenType),n=e.offset,i=e.length,a=e.startLine,s=e.startColumn,l=e.endLine,u=e.endColumn,h=e.hidden;return new d0(n,i,{start:{line:a,character:s},end:{line:l,character:u}},r,h)}getTokenType(e){return this.lexer.definition[e]}getGrammarElementId(e){return this.grammarElementIdMap.size===0&&this.createGrammarElementIdMap(),this.grammarElementIdMap.get(e)}getGrammarElement(e){this.grammarElementIdMap.size===0&&this.createGrammarElementIdMap();let r=this.grammarElementIdMap.getKey(e);if(r)return r;throw new Error("Invalid grammar element id: "+e)}createGrammarElementIdMap(){let e=0;for(let r of Yo(this.grammar))Uv(r)&&this.grammarElementIdMap.set(r,e++)}}});function po(t){return{documentation:{CommentProvider:o(e=>new rx(e),"CommentProvider"),DocumentationProvider:o(e=>new tx(e),"DocumentationProvider")},parser:{AsyncParser:o(e=>new nx(e),"AsyncParser"),GrammarConfig:o(e=>JR(e),"GrammarConfig"),LangiumParser:o(e=>cM(e),"LangiumParser"),CompletionParser:o(e=>oM(e),"CompletionParser"),ValueConverter:o(()=>new y0,"ValueConverter"),TokenBuilder:o(()=>new g0,"TokenBuilder"),Lexer:o(e=>new Z2(e),"Lexer"),ParserErrorMessageProvider:o(()=>new yg,"ParserErrorMessageProvider")},workspace:{AstNodeLocator:o(()=>new q2,"AstNodeLocator"),AstNodeDescriptionProvider:o(e=>new Y2(e),"AstNodeDescriptionProvider"),ReferenceDescriptionProvider:o(e=>new W2(e),"ReferenceDescriptionProvider")},references:{Linker:o(e=>new M2(e),"Linker"),NameProvider:o(()=>new I2,"NameProvider"),ScopeProvider:o(e=>new z2(e),"ScopeProvider"),ScopeComputation:o(e=>new P2(e),"ScopeComputation"),References:o(e=>new O2(e),"References")},serializer:{Hydrator:o(e=>new ax(e),"Hydrator"),JsonSerializer:o(e=>new G2(e),"JsonSerializer")},validation:{DocumentValidator:o(e=>new H2(e),"DocumentValidator"),ValidationRegistry:o(e=>new V2(e),"ValidationRegistry")},shared:o(()=>t.shared,"shared")}}function mo(t){return{ServiceRegistry:o(()=>new $2,"ServiceRegistry"),workspace:{LangiumDocuments:o(e=>new N2(e),"LangiumDocuments"),LangiumDocumentFactory:o(e=>new R2(e),"LangiumDocumentFactory"),DocumentBuilder:o(e=>new j2(e),"DocumentBuilder"),IndexManager:o(e=>new K2(e),"IndexManager"),WorkspaceManager:o(e=>new Q2(e),"WorkspaceManager"),FileSystemProvider:o(e=>t.fileSystemProvider(e),"FileSystemProvider"),WorkspaceLock:o(()=>new ix,"WorkspaceLock"),ConfigurationProvider:o(e=>new X2(e),"ConfigurationProvider")}}}var iI=R(()=>{"use strict";eN();lM();uM();hM();fM();CM();SM();AM();_M();DM();Ok();NM();MM();U2();IM();OM();PM();FM();Tg();zM();GM();UM();KM();QM();D2();tI();rI();nI();o(po,"createDefaultCoreModule");o(mo,"createDefaultSharedCoreModule")});function Fi(t,e,r,n,i,a,s,l,u){let h=[t,e,r,n,i,a,s,l,u].reduce(Gk,{});return Moe(h)}function Noe(t){if(t&&t[sI])for(let e of Object.values(t))Noe(e);return t}function Moe(t,e){let r=new Proxy({},{deleteProperty:o(()=>!1,"deleteProperty"),get:o((n,i)=>Roe(n,i,t,e||r),"get"),getOwnPropertyDescriptor:o((n,i)=>(Roe(n,i,t,e||r),Object.getOwnPropertyDescriptor(n,i)),"getOwnPropertyDescriptor"),has:o((n,i)=>i in t,"has"),ownKeys:o(()=>[...Reflect.ownKeys(t),sI],"ownKeys")});return r[sI]=!0,r}function Roe(t,e,r,n){if(e in t){if(t[e]instanceof Error)throw new Error("Construction failure. Please make sure that your dependencies are constructable.",{cause:t[e]});if(t[e]===Doe)throw new Error('Cycle detected. Please make "'+String(e)+'" lazy. See https://langium.org/docs/configuration-services/#resolving-cyclic-dependencies');return t[e]}else if(e in r){let i=r[e];t[e]=Doe;try{t[e]=typeof i=="function"?i(n):Moe(i,n)}catch(a){throw t[e]=a instanceof Error?a:void 0,a}return t[e]}else return}function Gk(t,e){if(e){for(let[r,n]of Object.entries(e))if(n!==void 0){let i=t[r];i!==null&&n!==null&&typeof i=="object"&&typeof n=="object"?t[r]=Gk(i,n):t[r]=n}}return t}var aI,sI,Doe,oI=R(()=>{"use strict";(function(t){t.merge=(e,r)=>Gk(Gk({},e),r)})(aI||(aI={}));o(Fi,"inject");sI=Symbol("isProxy");o(Noe,"eagerLoad");o(Moe,"_inject");Doe=Symbol();o(Roe,"_resolve");o(Gk,"_merge")});var Ioe=R(()=>{"use strict"});var Ooe=R(()=>{"use strict";QM();KM();jM()});var Poe=R(()=>{"use strict"});var Boe=R(()=>{"use strict";eN();Poe()});var Foe=R(()=>{"use strict"});var zoe=R(()=>{"use strict";tI();lM();Ek();uM();D2();UM();Foe();hM();fM()});var Goe=R(()=>{"use strict";CM();SM();AM();LM();_M();DM()});var $oe=R(()=>{"use strict";nI();Ok()});var $k,go,lI=R(()=>{"use strict";$k=class{static{o(this,"EmptyFileSystemProvider")}readFile(){throw new Error("No file system is available.")}async readDirectory(){return[]}},go={fileSystemProvider:o(()=>new $k,"fileSystemProvider")}});function uPe(){let t=Fi(mo(go),cPe),e=Fi(po({shared:t}),lPe);return t.ServiceRegistry.register(e),e}function lf(t){var e;let r=uPe(),n=r.serializer.JsonSerializer.deserialize(t);return r.shared.workspace.LangiumDocumentFactory.fromModel(n,Ms.parse(`memory://${(e=n.name)!==null&&e!==void 0?e:"grammar"}.langium`)),n}var lPe,cPe,Voe=R(()=>{"use strict";iI();oI();Sc();lI();Nc();lPe={Grammar:o(()=>{},"Grammar"),LanguageMetaData:o(()=>({caseInsensitive:!1,fileExtensions:[".langium"],languageId:"langium"}),"LanguageMetaData")},cPe={AstReflection:o(()=>new Gm,"AstReflection")};o(uPe,"createMinimalGrammarServices");o(lf,"loadGrammarFromJson")});var Rr={};hr(Rr,{AstUtils:()=>CT,BiMap:()=>v0,Cancellation:()=>pr,ContextCache:()=>x0,CstUtils:()=>dT,DONE_RESULT:()=>Ja,Deferred:()=>as,Disposable:()=>b0,DisposableCache:()=>Cg,DocumentCache:()=>Mk,EMPTY_STREAM:()=>Gv,ErrorWithLocation:()=>jd,GrammarUtils:()=>RT,MultiMap:()=>Mc,OperationCancelled:()=>Rc,Reduction:()=>Fm,RegExpUtils:()=>LT,SimpleCache:()=>F2,StreamImpl:()=>uo,TreeStreamImpl:()=>Cc,URI:()=>Ms,UriUtils:()=>ss,WorkspaceCache:()=>Sg,assertUnreachable:()=>tf,delayNextTick:()=>TM,interruptAndCheck:()=>Bi,isOperationCancelled:()=>of,loadGrammarFromJson:()=>lf,setInterruptionPeriod:()=>poe,startCancelableOperation:()=>doe,stream:()=>Kr});var Uoe=R(()=>{"use strict";Ik();ZM();dr(Rr,ii);kg();BM();pT();Voe();qo();Ds();Nc();es();Wo();Rl();Il();Um()});var Hoe=R(()=>{"use strict";MM();U2()});var Yoe=R(()=>{"use strict";IM();OM();PM();FM();Tg();lI();zM();rI();GM()});var ba={};hr(ba,{AbstractAstReflection:()=>Yd,AbstractCstNode:()=>S2,AbstractLangiumParser:()=>A2,AbstractParserErrorMessageProvider:()=>Sk,AbstractThreadedAsyncParser:()=>JM,AstUtils:()=>CT,BiMap:()=>v0,Cancellation:()=>pr,CompositeCstNodeImpl:()=>p0,ContextCache:()=>x0,CstNodeBuilder:()=>C2,CstUtils:()=>dT,DONE_RESULT:()=>Ja,DatatypeSymbol:()=>Ck,DefaultAstNodeDescriptionProvider:()=>Y2,DefaultAstNodeLocator:()=>q2,DefaultAsyncParser:()=>nx,DefaultCommentProvider:()=>rx,DefaultConfigurationProvider:()=>X2,DefaultDocumentBuilder:()=>j2,DefaultDocumentValidator:()=>H2,DefaultHydrator:()=>ax,DefaultIndexManager:()=>K2,DefaultJsonSerializer:()=>G2,DefaultLangiumDocumentFactory:()=>R2,DefaultLangiumDocuments:()=>N2,DefaultLexer:()=>Z2,DefaultLinker:()=>M2,DefaultNameProvider:()=>I2,DefaultReferenceDescriptionProvider:()=>W2,DefaultReferences:()=>O2,DefaultScopeComputation:()=>P2,DefaultScopeProvider:()=>z2,DefaultServiceRegistry:()=>$2,DefaultTokenBuilder:()=>g0,DefaultValueConverter:()=>y0,DefaultWorkspaceLock:()=>ix,DefaultWorkspaceManager:()=>Q2,Deferred:()=>as,Disposable:()=>b0,DisposableCache:()=>Cg,DocumentCache:()=>Mk,DocumentState:()=>yn,DocumentValidator:()=>Uu,EMPTY_SCOPE:()=>jOe,EMPTY_STREAM:()=>Gv,EmptyFileSystem:()=>go,EmptyFileSystemProvider:()=>$k,ErrorWithLocation:()=>jd,GrammarAST:()=>Yv,GrammarUtils:()=>RT,JSDocDocumentationProvider:()=>tx,LangiumCompletionParser:()=>L2,LangiumParser:()=>_2,LangiumParserErrorMessageProvider:()=>yg,LeafCstNodeImpl:()=>d0,MapScope:()=>B2,Module:()=>aI,MultiMap:()=>Mc,OperationCancelled:()=>Rc,ParserWorker:()=>eI,Reduction:()=>Fm,RegExpUtils:()=>LT,RootCstNodeImpl:()=>gg,SimpleCache:()=>F2,StreamImpl:()=>uo,StreamScope:()=>Eg,TextDocument:()=>bg,TreeStreamImpl:()=>Cc,URI:()=>Ms,UriUtils:()=>ss,ValidationCategory:()=>Ag,ValidationRegistry:()=>V2,ValueConverter:()=>Dc,WorkspaceCache:()=>Sg,assertUnreachable:()=>tf,createCompletionParser:()=>oM,createDefaultCoreModule:()=>po,createDefaultSharedCoreModule:()=>mo,createGrammarConfig:()=>JR,createLangiumParser:()=>cM,delayNextTick:()=>TM,diagnosticData:()=>Pk,eagerLoad:()=>Noe,getDiagnosticRange:()=>Toe,inject:()=>Fi,interruptAndCheck:()=>Bi,isAstNode:()=>Xn,isAstNodeDescription:()=>ED,isAstNodeWithComment:()=>RM,isCompositeCstNode:()=>co,isIMultiModeLexerDefinition:()=>VM,isJSDoc:()=>qM,isLeafCstNode:()=>ef,isLinkingError:()=>Wd,isNamed:()=>boe,isOperationCancelled:()=>of,isReference:()=>xa,isRootCstNode:()=>zv,isTokenTypeArray:()=>koe,isTokenTypeDictionary:()=>$M,loadGrammarFromJson:()=>lf,parseJSDoc:()=>WM,prepareLangiumParser:()=>soe,setInterruptionPeriod:()=>poe,startCancelableOperation:()=>doe,stream:()=>Kr,toDiagnosticSeverity:()=>Bk});var Ic=R(()=>{"use strict";iI();oI();NM();Ioe();Vo();Ooe();Boe();zoe();Goe();$oe();Uoe();dr(ba,Rr);Hoe();Yoe();Sc()});function Joe(t){return Pl.isInstance(t,Zoe)}function ele(t){return Pl.isInstance(t,cI)}function tle(t){return Pl.isInstance(t,uI)}function rle(t){return Pl.isInstance(t,pPe)}function nle(t){return Pl.isInstance(t,hI)}function ale(t){return Pl.isInstance(t,ile)}function sle(t){return Pl.isInstance(t,fI)}function lle(t){return Pl.isInstance(t,ole)}function ule(t){return Pl.isInstance(t,cle)}function fle(t){return Pl.isInstance(t,hle)}function ple(t){return Pl.isInstance(t,dle)}var hPe,Tt,Qoe,Zoe,cI,fPe,dPe,uI,pPe,hI,ile,fI,ole,cle,hle,dle,mPe,mle,Pl,Woe,gPe,qoe,yPe,Xoe,vPe,joe,xPe,Koe,bPe,wPe,TPe,kPe,EPe,CPe,Bl,dI,pI,mI,gI,yI,SPe,APe,_Pe,LPe,_g,w0,Xo,DPe,jo=R(()=>{"use strict";Ic();Ic();Ic();Ic();hPe=Object.defineProperty,Tt=o((t,e)=>hPe(t,"name",{value:e,configurable:!0}),"__name"),Qoe="Statement",Zoe="Architecture";o(Joe,"isArchitecture");Tt(Joe,"isArchitecture");cI="Branch";o(ele,"isBranch");Tt(ele,"isBranch");fPe="Checkout",dPe="CherryPicking",uI="Commit";o(tle,"isCommit");Tt(tle,"isCommit");pPe="Common";o(rle,"isCommon");Tt(rle,"isCommon");hI="GitGraph";o(nle,"isGitGraph");Tt(nle,"isGitGraph");ile="Info";o(ale,"isInfo");Tt(ale,"isInfo");fI="Merge";o(sle,"isMerge");Tt(sle,"isMerge");ole="Packet";o(lle,"isPacket");Tt(lle,"isPacket");cle="PacketBlock";o(ule,"isPacketBlock");Tt(ule,"isPacketBlock");hle="Pie";o(fle,"isPie");Tt(fle,"isPie");dle="PieSection";o(ple,"isPieSection");Tt(ple,"isPieSection");mPe="Direction",mle=class extends Yd{static{o(this,"MermaidAstReflection")}static{Tt(this,"MermaidAstReflection")}getAllTypes(){return["Architecture","Branch","Checkout","CherryPicking","Commit","Common","Direction","Edge","GitGraph","Group","Info","Junction","Merge","Packet","PacketBlock","Pie","PieSection","Service","Statement"]}computeIsSubtype(t,e){switch(t){case cI:case fPe:case dPe:case uI:case fI:return this.isSubtype(Qoe,e);case mPe:return this.isSubtype(hI,e);default:return!1}}getReferenceType(t){let e=`${t.container.$type}:${t.property}`;switch(e){default:throw new Error(`${e} is not a valid reference id.`)}}getTypeMetaData(t){switch(t){case"Architecture":return{name:"Architecture",properties:[{name:"accDescr"},{name:"accTitle"},{name:"edges",defaultValue:[]},{name:"groups",defaultValue:[]},{name:"junctions",defaultValue:[]},{name:"services",defaultValue:[]},{name:"title"}]};case"Branch":return{name:"Branch",properties:[{name:"name"},{name:"order"}]};case"Checkout":return{name:"Checkout",properties:[{name:"branch"}]};case"CherryPicking":return{name:"CherryPicking",properties:[{name:"id"},{name:"parent"},{name:"tags",defaultValue:[]}]};case"Commit":return{name:"Commit",properties:[{name:"id"},{name:"message"},{name:"tags",defaultValue:[]},{name:"type"}]};case"Common":return{name:"Common",properties:[{name:"accDescr"},{name:"accTitle"},{name:"title"}]};case"Edge":return{name:"Edge",properties:[{name:"lhsDir"},{name:"lhsGroup",defaultValue:!1},{name:"lhsId"},{name:"lhsInto",defaultValue:!1},{name:"rhsDir"},{name:"rhsGroup",defaultValue:!1},{name:"rhsId"},{name:"rhsInto",defaultValue:!1},{name:"title"}]};case"GitGraph":return{name:"GitGraph",properties:[{name:"accDescr"},{name:"accTitle"},{name:"statements",defaultValue:[]},{name:"title"}]};case"Group":return{name:"Group",properties:[{name:"icon"},{name:"id"},{name:"in"},{name:"title"}]};case"Info":return{name:"Info",properties:[{name:"accDescr"},{name:"accTitle"},{name:"title"}]};case"Junction":return{name:"Junction",properties:[{name:"id"},{name:"in"}]};case"Merge":return{name:"Merge",properties:[{name:"branch"},{name:"id"},{name:"tags",defaultValue:[]},{name:"type"}]};case"Packet":return{name:"Packet",properties:[{name:"accDescr"},{name:"accTitle"},{name:"blocks",defaultValue:[]},{name:"title"}]};case"PacketBlock":return{name:"PacketBlock",properties:[{name:"end"},{name:"label"},{name:"start"}]};case"Pie":return{name:"Pie",properties:[{name:"accDescr"},{name:"accTitle"},{name:"sections",defaultValue:[]},{name:"showData",defaultValue:!1},{name:"title"}]};case"PieSection":return{name:"PieSection",properties:[{name:"label"},{name:"value"}]};case"Service":return{name:"Service",properties:[{name:"icon"},{name:"iconText"},{name:"id"},{name:"in"},{name:"title"}]};case"Direction":return{name:"Direction",properties:[{name:"accDescr"},{name:"accTitle"},{name:"dir"},{name:"statements",defaultValue:[]},{name:"title"}]};default:return{name:t,properties:[]}}}},Pl=new mle,gPe=Tt(()=>Woe??(Woe=lf('{"$type":"Grammar","isDeclared":true,"name":"Info","imports":[],"rules":[{"$type":"ParserRule","name":"Info","entry":true,"definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[],"cardinality":"*"},{"$type":"Keyword","value":"info"},{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[],"cardinality":"*"},{"$type":"Group","elements":[{"$type":"Keyword","value":"showInfo"},{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[],"cardinality":"*"}],"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[],"cardinality":"?"}]},"definesHiddenTokens":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"TitleAndAccessibilities","fragment":true,"definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[]}}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}],"cardinality":"+"},"definesHiddenTokens":false,"entry":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"EOL","fragment":true,"dataType":"string","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[],"cardinality":"+"},{"$type":"EndOfFile"}]},"definesHiddenTokens":false,"entry":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"TerminalRule","name":"NEWLINE","definition":{"$type":"RegexToken","regex":"/\\\\r?\\\\n/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]+/"},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/"},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/"},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/"},"fragment":false}],"definesHiddenTokens":false,"hiddenTokens":[],"interfaces":[{"$type":"Interface","name":"Common","attributes":[{"$type":"TypeAttribute","name":"accDescr","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accTitle","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"title","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}}],"superTypes":[]}],"types":[],"usedGrammars":[]}')),"InfoGrammar"),yPe=Tt(()=>qoe??(qoe=lf(`{"$type":"Grammar","isDeclared":true,"name":"Packet","imports":[],"rules":[{"$type":"ParserRule","name":"Packet","entry":true,"definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[],"cardinality":"*"},{"$type":"Keyword","value":"packet-beta"},{"$type":"Alternatives","elements":[{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[],"cardinality":"*"},{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]},{"$type":"Assignment","feature":"blocks","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]},"cardinality":"*"}]},{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[],"cardinality":"+"},{"$type":"Assignment","feature":"blocks","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]},"cardinality":"+"}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[],"cardinality":"*"}]}]},"definesHiddenTokens":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"PacketBlock","definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"start","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":"-"},{"$type":"Assignment","feature":"end","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}}],"cardinality":"?"},{"$type":"Keyword","value":":"},{"$type":"Assignment","feature":"label","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}]},"definesHiddenTokens":false,"entry":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"TerminalRule","name":"INT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/0|[1-9][0-9]*/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"STRING","definition":{"$type":"RegexToken","regex":"/\\"[^\\"]*\\"|'[^']*'/"},"fragment":false,"hidden":false},{"$type":"ParserRule","name":"TitleAndAccessibilities","fragment":true,"definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@7"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]}},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@9"},"arguments":[]}}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}],"cardinality":"+"},"definesHiddenTokens":false,"entry":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"EOL","fragment":true,"dataType":"string","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[],"cardinality":"+"},{"$type":"EndOfFile"}]},"definesHiddenTokens":false,"entry":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"TerminalRule","name":"NEWLINE","definition":{"$type":"RegexToken","regex":"/\\\\r?\\\\n/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]+/"},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/"},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/"},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/"},"fragment":false}],"definesHiddenTokens":false,"hiddenTokens":[],"interfaces":[{"$type":"Interface","name":"Common","attributes":[{"$type":"TypeAttribute","name":"accDescr","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accTitle","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"title","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}}],"superTypes":[]}],"types":[],"usedGrammars":[]}`)),"PacketGrammar"),vPe=Tt(()=>Xoe??(Xoe=lf('{"$type":"Grammar","isDeclared":true,"name":"Pie","imports":[],"rules":[{"$type":"ParserRule","name":"Pie","entry":true,"definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[],"cardinality":"*"},{"$type":"Keyword","value":"pie"},{"$type":"Assignment","feature":"showData","operator":"?=","terminal":{"$type":"Keyword","value":"showData"},"cardinality":"?"},{"$type":"Alternatives","elements":[{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[],"cardinality":"*"},{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]},{"$type":"Assignment","feature":"sections","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]},"cardinality":"*"}]},{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[],"cardinality":"+"},{"$type":"Assignment","feature":"sections","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]},"cardinality":"+"}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[],"cardinality":"*"}]}]},"definesHiddenTokens":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"PieSection","definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"label","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}},{"$type":"Keyword","value":":"},{"$type":"Assignment","feature":"value","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}]},"definesHiddenTokens":false,"entry":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"TerminalRule","name":"PIE_SECTION_LABEL","definition":{"$type":"RegexToken","regex":"/\\"[^\\"]+\\"/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"PIE_SECTION_VALUE","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/(0|[1-9][0-9]*)(\\\\.[0-9]+)?/"},"fragment":false,"hidden":false},{"$type":"ParserRule","name":"TitleAndAccessibilities","fragment":true,"definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@7"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]}},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@9"},"arguments":[]}}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}],"cardinality":"+"},"definesHiddenTokens":false,"entry":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"EOL","fragment":true,"dataType":"string","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[],"cardinality":"+"},{"$type":"EndOfFile"}]},"definesHiddenTokens":false,"entry":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"TerminalRule","name":"NEWLINE","definition":{"$type":"RegexToken","regex":"/\\\\r?\\\\n/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]+/"},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/"},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/"},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/"},"fragment":false}],"definesHiddenTokens":false,"hiddenTokens":[],"interfaces":[{"$type":"Interface","name":"Common","attributes":[{"$type":"TypeAttribute","name":"accDescr","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accTitle","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"title","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}}],"superTypes":[]}],"types":[],"usedGrammars":[]}')),"PieGrammar"),xPe=Tt(()=>joe??(joe=lf('{"$type":"Grammar","isDeclared":true,"name":"Architecture","imports":[],"rules":[{"$type":"ParserRule","name":"Architecture","entry":true,"definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@18"},"arguments":[],"cardinality":"*"},{"$type":"Keyword","value":"architecture-beta"},{"$type":"Alternatives","elements":[{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@18"},"arguments":[],"cardinality":"*"},{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]}]},{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@18"},"arguments":[],"cardinality":"*"},{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[],"cardinality":"*"}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@18"},"arguments":[],"cardinality":"*"}]}]},"definesHiddenTokens":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"Statement","fragment":true,"definition":{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"groups","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}},{"$type":"Assignment","feature":"services","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@6"},"arguments":[]}},{"$type":"Assignment","feature":"junctions","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@7"},"arguments":[]}},{"$type":"Assignment","feature":"edges","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@8"},"arguments":[]}}]},"definesHiddenTokens":false,"entry":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"LeftPort","fragment":true,"definition":{"$type":"Group","elements":[{"$type":"Keyword","value":":"},{"$type":"Assignment","feature":"lhsDir","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@9"},"arguments":[]}}]},"definesHiddenTokens":false,"entry":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"RightPort","fragment":true,"definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"rhsDir","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@9"},"arguments":[]}},{"$type":"Keyword","value":":"}]},"definesHiddenTokens":false,"entry":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"Arrow","fragment":true,"definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]},{"$type":"Assignment","feature":"lhsInto","operator":"?=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]},"cardinality":"?"},{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"--"},{"$type":"Group","elements":[{"$type":"Keyword","value":"-"},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]}},{"$type":"Keyword","value":"-"}]}]},{"$type":"Assignment","feature":"rhsInto","operator":"?=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]},"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}]},"definesHiddenTokens":false,"entry":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"Group","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"group"},{"$type":"Assignment","feature":"id","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@10"},"arguments":[]}},{"$type":"Assignment","feature":"icon","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]},"cardinality":"?"},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]},"cardinality":"?"},{"$type":"Group","elements":[{"$type":"Keyword","value":"in"},{"$type":"Assignment","feature":"in","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@10"},"arguments":[]}}],"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}]},"definesHiddenTokens":false,"entry":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"Service","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"service"},{"$type":"Assignment","feature":"id","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@10"},"arguments":[]}},{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"iconText","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@11"},"arguments":[]}},{"$type":"Assignment","feature":"icon","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]}}],"cardinality":"?"},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]},"cardinality":"?"},{"$type":"Group","elements":[{"$type":"Keyword","value":"in"},{"$type":"Assignment","feature":"in","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@10"},"arguments":[]}}],"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}]},"definesHiddenTokens":false,"entry":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"Junction","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"junction"},{"$type":"Assignment","feature":"id","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@10"},"arguments":[]}},{"$type":"Group","elements":[{"$type":"Keyword","value":"in"},{"$type":"Assignment","feature":"in","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@10"},"arguments":[]}}],"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}]},"definesHiddenTokens":false,"entry":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"Edge","definition":{"$type":"Group","elements":[{"$type":"Assignment","feature":"lhsId","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@10"},"arguments":[]}},{"$type":"Assignment","feature":"lhsGroup","operator":"?=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]},"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]},{"$type":"Assignment","feature":"rhsId","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@10"},"arguments":[]}},{"$type":"Assignment","feature":"rhsGroup","operator":"?=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]},"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}]},"definesHiddenTokens":false,"entry":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"TerminalRule","name":"ARROW_DIRECTION","definition":{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalAlternatives","elements":[{"$type":"TerminalAlternatives","elements":[{"$type":"CharacterRange","left":{"$type":"Keyword","value":"L"}},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"R"}}]},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"T"}}]},{"$type":"CharacterRange","left":{"$type":"Keyword","value":"B"}}]},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ARCH_ID","definition":{"$type":"RegexToken","regex":"/[\\\\w]+/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ARCH_TEXT_ICON","definition":{"$type":"RegexToken","regex":"/\\\\(\\"[^\\"]+\\"\\\\)/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ARCH_ICON","definition":{"$type":"RegexToken","regex":"/\\\\([\\\\w-:]+\\\\)/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ARCH_TITLE","definition":{"$type":"RegexToken","regex":"/\\\\[[\\\\w ]+\\\\]/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ARROW_GROUP","definition":{"$type":"RegexToken","regex":"/\\\\{group\\\\}/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ARROW_INTO","definition":{"$type":"RegexToken","regex":"/<|>/"},"fragment":false,"hidden":false},{"$type":"ParserRule","name":"TitleAndAccessibilities","fragment":true,"definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@21"},"arguments":[]}}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}],"cardinality":"+"},"definesHiddenTokens":false,"entry":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"EOL","fragment":true,"dataType":"string","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@18"},"arguments":[],"cardinality":"+"},{"$type":"EndOfFile"}]},"definesHiddenTokens":false,"entry":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"TerminalRule","name":"NEWLINE","definition":{"$type":"RegexToken","regex":"/\\\\r?\\\\n/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]+/"},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/"},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/"},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/"},"fragment":false}],"definesHiddenTokens":false,"hiddenTokens":[],"interfaces":[{"$type":"Interface","name":"Common","attributes":[{"$type":"TypeAttribute","name":"accDescr","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accTitle","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"title","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}}],"superTypes":[]}],"types":[],"usedGrammars":[]}')),"ArchitectureGrammar"),bPe=Tt(()=>Koe??(Koe=lf(`{"$type":"Grammar","isDeclared":true,"name":"GitGraph","interfaces":[{"$type":"Interface","name":"Common","attributes":[{"$type":"TypeAttribute","name":"accDescr","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"accTitle","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}},{"$type":"TypeAttribute","name":"title","isOptional":true,"type":{"$type":"SimpleType","primitiveType":"string"}}],"superTypes":[]}],"rules":[{"$type":"ParserRule","name":"TitleAndAccessibilities","fragment":true,"definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Assignment","feature":"accDescr","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@3"},"arguments":[]}},{"$type":"Assignment","feature":"accTitle","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@4"},"arguments":[]}},{"$type":"Assignment","feature":"title","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@5"},"arguments":[]}}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}],"cardinality":"+"},"definesHiddenTokens":false,"entry":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"EOL","fragment":true,"dataType":"string","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[],"cardinality":"+"},{"$type":"EndOfFile"}]},"definesHiddenTokens":false,"entry":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"TerminalRule","name":"NEWLINE","definition":{"$type":"RegexToken","regex":"/\\\\r?\\\\n/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_DESCR","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accDescr(?:[\\\\t ]*:([^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)|\\\\s*{([^}]*)})/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ACC_TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*accTitle[\\\\t ]*:(?:[^\\\\n\\\\r]*?(?=%%)|[^\\\\n\\\\r]*)/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"TITLE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*title(?:[\\\\t ][^\\\\n\\\\r]*?(?=%%)|[\\\\t ][^\\\\n\\\\r]*|)/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","hidden":true,"name":"WHITESPACE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]+/"},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"YAML","definition":{"$type":"RegexToken","regex":"/---[\\\\t ]*\\\\r?\\\\n(?:[\\\\S\\\\s]*?\\\\r?\\\\n)?---(?:\\\\r?\\\\n|(?!\\\\S))/"},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"DIRECTIVE","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%{[\\\\S\\\\s]*?}%%(?:\\\\r?\\\\n|(?!\\\\S))/"},"fragment":false},{"$type":"TerminalRule","hidden":true,"name":"SINGLE_LINE_COMMENT","definition":{"$type":"RegexToken","regex":"/[\\\\t ]*%%[^\\\\n\\\\r]*/"},"fragment":false},{"$type":"ParserRule","name":"GitGraph","entry":true,"definition":{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[],"cardinality":"*"},{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"gitGraph"},{"$type":"Group","elements":[{"$type":"Keyword","value":"gitGraph"},{"$type":"Keyword","value":":"}]},{"$type":"Keyword","value":"gitGraph:"},{"$type":"Group","elements":[{"$type":"Keyword","value":"gitGraph"},{"$type":"RuleCall","rule":{"$ref":"#/rules@12"},"arguments":[]},{"$type":"Keyword","value":":"}]}]},{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[],"cardinality":"*"},{"$type":"Group","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[],"cardinality":"*"},{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@0"},"arguments":[]},{"$type":"Assignment","feature":"statements","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@11"},"arguments":[]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@2"},"arguments":[]}],"cardinality":"*"}]}]},"definesHiddenTokens":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"Statement","definition":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@13"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@14"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@15"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@16"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@17"},"arguments":[]}]},"definesHiddenTokens":false,"entry":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"Direction","definition":{"$type":"Assignment","feature":"dir","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"LR"},{"$type":"Keyword","value":"TB"},{"$type":"Keyword","value":"BT"}]}},"definesHiddenTokens":false,"entry":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"Commit","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"commit"},{"$type":"Alternatives","elements":[{"$type":"Group","elements":[{"$type":"Keyword","value":"id:"},{"$type":"Assignment","feature":"id","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Keyword","value":"msg:","cardinality":"?"},{"$type":"Assignment","feature":"message","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Keyword","value":"tag:"},{"$type":"Assignment","feature":"tags","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Keyword","value":"type:"},{"$type":"Assignment","feature":"type","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"NORMAL"},{"$type":"Keyword","value":"REVERSE"},{"$type":"Keyword","value":"HIGHLIGHT"}]}}]}],"cardinality":"*"},{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}]},"definesHiddenTokens":false,"entry":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"Branch","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"branch"},{"$type":"Assignment","feature":"name","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}]}},{"$type":"Group","elements":[{"$type":"Keyword","value":"order:"},{"$type":"Assignment","feature":"order","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@18"},"arguments":[]}}],"cardinality":"?"},{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}]},"definesHiddenTokens":false,"entry":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"Merge","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"merge"},{"$type":"Assignment","feature":"branch","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}]}},{"$type":"Alternatives","elements":[{"$type":"Group","elements":[{"$type":"Keyword","value":"id:"},{"$type":"Assignment","feature":"id","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Keyword","value":"tag:"},{"$type":"Assignment","feature":"tags","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Keyword","value":"type:"},{"$type":"Assignment","feature":"type","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"NORMAL"},{"$type":"Keyword","value":"REVERSE"},{"$type":"Keyword","value":"HIGHLIGHT"}]}}]}],"cardinality":"*"},{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}]},"definesHiddenTokens":false,"entry":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"Checkout","definition":{"$type":"Group","elements":[{"$type":"Alternatives","elements":[{"$type":"Keyword","value":"checkout"},{"$type":"Keyword","value":"switch"}]},{"$type":"Assignment","feature":"branch","operator":"=","terminal":{"$type":"Alternatives","elements":[{"$type":"RuleCall","rule":{"$ref":"#/rules@19"},"arguments":[]},{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}]}},{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}]},"definesHiddenTokens":false,"entry":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"ParserRule","name":"CherryPicking","definition":{"$type":"Group","elements":[{"$type":"Keyword","value":"cherry-pick"},{"$type":"Alternatives","elements":[{"$type":"Group","elements":[{"$type":"Keyword","value":"id:"},{"$type":"Assignment","feature":"id","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Keyword","value":"tag:"},{"$type":"Assignment","feature":"tags","operator":"+=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}}]},{"$type":"Group","elements":[{"$type":"Keyword","value":"parent:"},{"$type":"Assignment","feature":"parent","operator":"=","terminal":{"$type":"RuleCall","rule":{"$ref":"#/rules@20"},"arguments":[]}}]}],"cardinality":"*"},{"$type":"RuleCall","rule":{"$ref":"#/rules@1"},"arguments":[]}]},"definesHiddenTokens":false,"entry":false,"fragment":false,"hiddenTokens":[],"parameters":[],"wildcard":false},{"$type":"TerminalRule","name":"INT","type":{"$type":"ReturnType","name":"number"},"definition":{"$type":"RegexToken","regex":"/[0-9]+(?=\\\\s)/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"ID","type":{"$type":"ReturnType","name":"string"},"definition":{"$type":"RegexToken","regex":"/\\\\w([-\\\\./\\\\w]*[-\\\\w])?/"},"fragment":false,"hidden":false},{"$type":"TerminalRule","name":"STRING","definition":{"$type":"RegexToken","regex":"/\\"[^\\"]*\\"|'[^']*'/"},"fragment":false,"hidden":false}],"definesHiddenTokens":false,"hiddenTokens":[],"imports":[],"types":[],"usedGrammars":[]}`)),"GitGraphGrammar"),wPe={languageId:"info",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1},TPe={languageId:"packet",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1},kPe={languageId:"pie",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1},EPe={languageId:"architecture",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1},CPe={languageId:"gitGraph",fileExtensions:[".mmd",".mermaid"],caseInsensitive:!1},Bl={AstReflection:Tt(()=>new mle,"AstReflection")},dI={Grammar:Tt(()=>gPe(),"Grammar"),LanguageMetaData:Tt(()=>wPe,"LanguageMetaData"),parser:{}},pI={Grammar:Tt(()=>yPe(),"Grammar"),LanguageMetaData:Tt(()=>TPe,"LanguageMetaData"),parser:{}},mI={Grammar:Tt(()=>vPe(),"Grammar"),LanguageMetaData:Tt(()=>kPe,"LanguageMetaData"),parser:{}},gI={Grammar:Tt(()=>xPe(),"Grammar"),LanguageMetaData:Tt(()=>EPe,"LanguageMetaData"),parser:{}},yI={Grammar:Tt(()=>bPe(),"Grammar"),LanguageMetaData:Tt(()=>CPe,"LanguageMetaData"),parser:{}},SPe=/accDescr(?:[\t ]*:([^\n\r]*)|\s*{([^}]*)})/,APe=/accTitle[\t ]*:([^\n\r]*)/,_Pe=/title([\t ][^\n\r]*|)/,LPe={ACC_DESCR:SPe,ACC_TITLE:APe,TITLE:_Pe},_g=class extends y0{static{o(this,"AbstractMermaidValueConverter")}static{Tt(this,"AbstractMermaidValueConverter")}runConverter(t,e,r){let n=this.runCommonConverter(t,e,r);return n===void 0&&(n=this.runCustomConverter(t,e,r)),n===void 0?super.runConverter(t,e,r):n}runCommonConverter(t,e,r){let n=LPe[t.name];if(n===void 0)return;let i=n.exec(e);if(i!==null){if(i[1]!==void 0)return i[1].trim().replace(/[\t ]{2,}/gm," ");if(i[2]!==void 0)return i[2].replace(/^\s*/gm,"").replace(/\s+$/gm,"").replace(/[\t ]{2,}/gm," ").replace(/[\n\r]{2,}/gm,` +`)}}},w0=class extends _g{static{o(this,"CommonValueConverter")}static{Tt(this,"CommonValueConverter")}runCustomConverter(t,e,r){}},Xo=class extends g0{static{o(this,"AbstractMermaidTokenBuilder")}static{Tt(this,"AbstractMermaidTokenBuilder")}constructor(t){super(),this.keywords=new Set(t)}buildKeywordTokens(t,e,r){let n=super.buildKeywordTokens(t,e,r);return n.forEach(i=>{this.keywords.has(i.name)&&i.PATTERN!==void 0&&(i.PATTERN=new RegExp(i.PATTERN.toString()+"(?:(?=%%)|(?!\\S))"))}),n}},DPe=class extends Xo{static{o(this,"CommonTokenBuilder")}static{Tt(this,"CommonTokenBuilder")}}});function Uk(t=go){let e=Fi(mo(t),Bl),r=Fi(po({shared:e}),yI,Vk);return e.ServiceRegistry.register(r),{shared:e,GitGraph:r}}var RPe,Vk,vI=R(()=>{"use strict";jo();Ic();RPe=class extends Xo{static{o(this,"GitGraphTokenBuilder")}static{Tt(this,"GitGraphTokenBuilder")}constructor(){super(["gitGraph"])}},Vk={parser:{TokenBuilder:Tt(()=>new RPe,"TokenBuilder"),ValueConverter:Tt(()=>new w0,"ValueConverter")}};o(Uk,"createGitGraphServices");Tt(Uk,"createGitGraphServices")});function Yk(t=go){let e=Fi(mo(t),Bl),r=Fi(po({shared:e}),dI,Hk);return e.ServiceRegistry.register(r),{shared:e,Info:r}}var NPe,Hk,xI=R(()=>{"use strict";jo();Ic();NPe=class extends Xo{static{o(this,"InfoTokenBuilder")}static{Tt(this,"InfoTokenBuilder")}constructor(){super(["info","showInfo"])}},Hk={parser:{TokenBuilder:Tt(()=>new NPe,"TokenBuilder"),ValueConverter:Tt(()=>new w0,"ValueConverter")}};o(Yk,"createInfoServices");Tt(Yk,"createInfoServices")});function qk(t=go){let e=Fi(mo(t),Bl),r=Fi(po({shared:e}),pI,Wk);return e.ServiceRegistry.register(r),{shared:e,Packet:r}}var MPe,Wk,bI=R(()=>{"use strict";jo();Ic();MPe=class extends Xo{static{o(this,"PacketTokenBuilder")}static{Tt(this,"PacketTokenBuilder")}constructor(){super(["packet-beta"])}},Wk={parser:{TokenBuilder:Tt(()=>new MPe,"TokenBuilder"),ValueConverter:Tt(()=>new w0,"ValueConverter")}};o(qk,"createPacketServices");Tt(qk,"createPacketServices")});function jk(t=go){let e=Fi(mo(t),Bl),r=Fi(po({shared:e}),mI,Xk);return e.ServiceRegistry.register(r),{shared:e,Pie:r}}var IPe,OPe,Xk,wI=R(()=>{"use strict";jo();Ic();IPe=class extends Xo{static{o(this,"PieTokenBuilder")}static{Tt(this,"PieTokenBuilder")}constructor(){super(["pie","showData"])}},OPe=class extends _g{static{o(this,"PieValueConverter")}static{Tt(this,"PieValueConverter")}runCustomConverter(t,e,r){if(t.name==="PIE_SECTION_LABEL")return e.replace(/"/g,"").trim()}},Xk={parser:{TokenBuilder:Tt(()=>new IPe,"TokenBuilder"),ValueConverter:Tt(()=>new OPe,"ValueConverter")}};o(jk,"createPieServices");Tt(jk,"createPieServices")});function Qk(t=go){let e=Fi(mo(t),Bl),r=Fi(po({shared:e}),gI,Kk);return e.ServiceRegistry.register(r),{shared:e,Architecture:r}}var PPe,BPe,Kk,TI=R(()=>{"use strict";jo();Ic();PPe=class extends Xo{static{o(this,"ArchitectureTokenBuilder")}static{Tt(this,"ArchitectureTokenBuilder")}constructor(){super(["architecture"])}},BPe=class extends _g{static{o(this,"ArchitectureValueConverter")}static{Tt(this,"ArchitectureValueConverter")}runCustomConverter(t,e,r){if(t.name==="ARCH_ICON")return e.replace(/[()]/g,"").trim();if(t.name==="ARCH_TEXT_ICON")return e.replace(/["()]/g,"");if(t.name==="ARCH_TITLE")return e.replace(/[[\]]/g,"").trim()}},Kk={parser:{TokenBuilder:Tt(()=>new PPe,"TokenBuilder"),ValueConverter:Tt(()=>new BPe,"ValueConverter")}};o(Qk,"createArchitectureServices");Tt(Qk,"createArchitectureServices")});var gle={};hr(gle,{InfoModule:()=>Hk,createInfoServices:()=>Yk});var yle=R(()=>{"use strict";xI();jo()});var vle={};hr(vle,{PacketModule:()=>Wk,createPacketServices:()=>qk});var xle=R(()=>{"use strict";bI();jo()});var ble={};hr(ble,{PieModule:()=>Xk,createPieServices:()=>jk});var wle=R(()=>{"use strict";wI();jo()});var Tle={};hr(Tle,{ArchitectureModule:()=>Kk,createArchitectureServices:()=>Qk});var kle=R(()=>{"use strict";TI();jo()});var Ele={};hr(Ele,{GitGraphModule:()=>Vk,createGitGraphServices:()=>Uk});var Cle=R(()=>{"use strict";vI();jo()});async function Fl(t,e){let r=FPe[t];if(!r)throw new Error(`Unknown diagram type: ${t}`);T0[t]||await r();let i=T0[t].parse(e);if(i.lexerErrors.length>0||i.parserErrors.length>0)throw new zPe(i);return i.value}var T0,FPe,zPe,Lg=R(()=>{"use strict";vI();xI();bI();wI();TI();jo();T0={},FPe={info:Tt(async()=>{let{createInfoServices:t}=await Promise.resolve().then(()=>(yle(),gle)),e=t().Info.parser.LangiumParser;T0.info=e},"info"),packet:Tt(async()=>{let{createPacketServices:t}=await Promise.resolve().then(()=>(xle(),vle)),e=t().Packet.parser.LangiumParser;T0.packet=e},"packet"),pie:Tt(async()=>{let{createPieServices:t}=await Promise.resolve().then(()=>(wle(),ble)),e=t().Pie.parser.LangiumParser;T0.pie=e},"pie"),architecture:Tt(async()=>{let{createArchitectureServices:t}=await Promise.resolve().then(()=>(kle(),Tle)),e=t().Architecture.parser.LangiumParser;T0.architecture=e},"architecture"),gitGraph:Tt(async()=>{let{createGitGraphServices:t}=await Promise.resolve().then(()=>(Cle(),Ele)),e=t().GitGraph.parser.LangiumParser;T0.gitGraph=e},"gitGraph")};o(Fl,"parse");Tt(Fl,"parse");zPe=class extends Error{static{o(this,"MermaidParseError")}constructor(t){let e=t.lexerErrors.map(n=>n.message).join(` +`),r=t.parserErrors.map(n=>n.message).join(` +`);super(`Parsing failed: ${e} ${r}`),this.result=t}static{Tt(this,"MermaidParseError")}}});function cf(t,e){t.accDescr&&e.setAccDescription?.(t.accDescr),t.accTitle&&e.setAccTitle?.(t.accTitle),t.title&&e.setDiagramTitle?.(t.title)}var sx=R(()=>{"use strict";o(cf,"populateCommonDb")});var Hr,Zk=R(()=>{"use strict";Hr={NORMAL:0,REVERSE:1,HIGHLIGHT:2,MERGE:3,CHERRY_PICK:4}});var uf,Jk=R(()=>{"use strict";uf=class{constructor(e){this.init=e;this.records=this.init()}static{o(this,"ImperativeState")}reset(){this.records=this.init()}}});function kI(){return J_({length:7})}function $Pe(t,e){let r=Object.create(null);return t.reduce((n,i)=>{let a=e(i);return r[a]||(r[a]=!0,n.push(i)),n},[])}function Sle(t,e,r){let n=t.indexOf(e);n===-1?t.push(r):t.splice(n,1,r)}function _le(t){let e=t.reduce((i,a)=>i.seq>a.seq?i:a,t[0]),r="";t.forEach(function(i){i===e?r+=" *":r+=" |"});let n=[r,e.id,e.seq];for(let i in pt.records.branches)pt.records.branches.get(i)===e.id&&n.push(i);if(V.debug(n.join(" ")),e.parents&&e.parents.length==2&&e.parents[0]&&e.parents[1]){let i=pt.records.commits.get(e.parents[0]);Sle(t,e,i),e.parents[1]&&t.push(pt.records.commits.get(e.parents[1]))}else{if(e.parents.length==0)return;if(e.parents[0]){let i=pt.records.commits.get(e.parents[0]);Sle(t,e,i)}}t=$Pe(t,i=>i.id),_le(t)}var GPe,k0,pt,VPe,UPe,HPe,YPe,WPe,qPe,XPe,Ale,jPe,KPe,QPe,ZPe,JPe,Lle,eBe,tBe,rBe,eE,EI=R(()=>{"use strict";ut();xr();qs();rr();bi();Zk();Jk();sl();GPe=mr.gitGraph,k0=o(()=>Ts({...GPe,...Or().gitGraph}),"getConfig"),pt=new uf(()=>{let t=k0(),e=t.mainBranchName,r=t.mainBranchOrder;return{mainBranchName:e,commits:new Map,head:null,branchConfig:new Map([[e,{name:e,order:r}]]),branches:new Map([[e,null]]),currBranch:e,direction:"LR",seq:0,options:{}}});o(kI,"getID");o($Pe,"uniqBy");VPe=o(function(t){pt.records.direction=t},"setDirection"),UPe=o(function(t){V.debug("options str",t),t=t?.trim(),t=t||"{}";try{pt.records.options=JSON.parse(t)}catch(e){V.error("error while parsing gitGraph options",e.message)}},"setOptions"),HPe=o(function(){return pt.records.options},"getOptions"),YPe=o(function(t){let e=t.msg,r=t.id,n=t.type,i=t.tags;V.info("commit",e,r,n,i),V.debug("Entering commit:",e,r,n,i);let a=k0();r=We.sanitizeText(r,a),e=We.sanitizeText(e,a),i=i?.map(l=>We.sanitizeText(l,a));let s={id:r||pt.records.seq+"-"+kI(),message:e,seq:pt.records.seq++,type:n??Hr.NORMAL,tags:i??[],parents:pt.records.head==null?[]:[pt.records.head.id],branch:pt.records.currBranch};pt.records.head=s,V.info("main branch",a.mainBranchName),pt.records.commits.set(s.id,s),pt.records.branches.set(pt.records.currBranch,s.id),V.debug("in pushCommit "+s.id)},"commit"),WPe=o(function(t){let e=t.name,r=t.order;if(e=We.sanitizeText(e,k0()),pt.records.branches.has(e))throw new Error(`Trying to create an existing branch. (Help: Either use a new name if you want create a new branch or try using "checkout ${e}")`);pt.records.branches.set(e,pt.records.head!=null?pt.records.head.id:null),pt.records.branchConfig.set(e,{name:e,order:r}),Ale(e),V.debug("in createBranch")},"branch"),qPe=o(t=>{let e=t.branch,r=t.id,n=t.type,i=t.tags,a=k0();e=We.sanitizeText(e,a),r&&(r=We.sanitizeText(r,a));let s=pt.records.branches.get(pt.records.currBranch),l=pt.records.branches.get(e),u=s?pt.records.commits.get(s):void 0,h=l?pt.records.commits.get(l):void 0;if(u&&h&&u.branch===e)throw new Error(`Cannot merge branch '${e}' into itself.`);if(pt.records.currBranch===e){let p=new Error('Incorrect usage of "merge". Cannot merge a branch to itself');throw p.hash={text:`merge ${e}`,token:`merge ${e}`,expected:["branch abc"]},p}if(u===void 0||!u){let p=new Error(`Incorrect usage of "merge". Current branch (${pt.records.currBranch})has no commits`);throw p.hash={text:`merge ${e}`,token:`merge ${e}`,expected:["commit"]},p}if(!pt.records.branches.has(e)){let p=new Error('Incorrect usage of "merge". Branch to be merged ('+e+") does not exist");throw p.hash={text:`merge ${e}`,token:`merge ${e}`,expected:[`branch ${e}`]},p}if(h===void 0||!h){let p=new Error('Incorrect usage of "merge". Branch to be merged ('+e+") has no commits");throw p.hash={text:`merge ${e}`,token:`merge ${e}`,expected:['"commit"']},p}if(u===h){let p=new Error('Incorrect usage of "merge". Both branches have same head');throw p.hash={text:`merge ${e}`,token:`merge ${e}`,expected:["branch abc"]},p}if(r&&pt.records.commits.has(r)){let p=new Error('Incorrect usage of "merge". Commit with id:'+r+" already exists, use different custom Id");throw p.hash={text:`merge ${e} ${r} ${n} ${i?.join(" ")}`,token:`merge ${e} ${r} ${n} ${i?.join(" ")}`,expected:[`merge ${e} ${r}_UNIQUE ${n} ${i?.join(" ")}`]},p}let f=l||"",d={id:r||`${pt.records.seq}-${kI()}`,message:`merged branch ${e} into ${pt.records.currBranch}`,seq:pt.records.seq++,parents:pt.records.head==null?[]:[pt.records.head.id,f],branch:pt.records.currBranch,type:Hr.MERGE,customType:n,customId:!!r,tags:i??[]};pt.records.head=d,pt.records.commits.set(d.id,d),pt.records.branches.set(pt.records.currBranch,d.id),V.debug(pt.records.branches),V.debug("in mergeBranch")},"merge"),XPe=o(function(t){let e=t.id,r=t.targetId,n=t.tags,i=t.parent;V.debug("Entering cherryPick:",e,r,n);let a=k0();if(e=We.sanitizeText(e,a),r=We.sanitizeText(r,a),n=n?.map(u=>We.sanitizeText(u,a)),i=We.sanitizeText(i,a),!e||!pt.records.commits.has(e)){let u=new Error('Incorrect usage of "cherryPick". Source commit id should exist and provided');throw u.hash={text:`cherryPick ${e} ${r}`,token:`cherryPick ${e} ${r}`,expected:["cherry-pick abc"]},u}let s=pt.records.commits.get(e);if(s===void 0||!s)throw new Error('Incorrect usage of "cherryPick". Source commit id should exist and provided');if(i&&!(Array.isArray(s.parents)&&s.parents.includes(i)))throw new Error("Invalid operation: The specified parent commit is not an immediate parent of the cherry-picked commit.");let l=s.branch;if(s.type===Hr.MERGE&&!i)throw new Error("Incorrect usage of cherry-pick: If the source commit is a merge commit, an immediate parent commit must be specified.");if(!r||!pt.records.commits.has(r)){if(l===pt.records.currBranch){let d=new Error('Incorrect usage of "cherryPick". Source commit is already on current branch');throw d.hash={text:`cherryPick ${e} ${r}`,token:`cherryPick ${e} ${r}`,expected:["cherry-pick abc"]},d}let u=pt.records.branches.get(pt.records.currBranch);if(u===void 0||!u){let d=new Error(`Incorrect usage of "cherry-pick". Current branch (${pt.records.currBranch})has no commits`);throw d.hash={text:`cherryPick ${e} ${r}`,token:`cherryPick ${e} ${r}`,expected:["cherry-pick abc"]},d}let h=pt.records.commits.get(u);if(h===void 0||!h){let d=new Error(`Incorrect usage of "cherry-pick". Current branch (${pt.records.currBranch})has no commits`);throw d.hash={text:`cherryPick ${e} ${r}`,token:`cherryPick ${e} ${r}`,expected:["cherry-pick abc"]},d}let f={id:pt.records.seq+"-"+kI(),message:`cherry-picked ${s?.message} into ${pt.records.currBranch}`,seq:pt.records.seq++,parents:pt.records.head==null?[]:[pt.records.head.id,s.id],branch:pt.records.currBranch,type:Hr.CHERRY_PICK,tags:n?n.filter(Boolean):[`cherry-pick:${s.id}${s.type===Hr.MERGE?`|parent:${i}`:""}`]};pt.records.head=f,pt.records.commits.set(f.id,f),pt.records.branches.set(pt.records.currBranch,f.id),V.debug(pt.records.branches),V.debug("in cherryPick")}},"cherryPick"),Ale=o(function(t){if(t=We.sanitizeText(t,k0()),pt.records.branches.has(t)){pt.records.currBranch=t;let e=pt.records.branches.get(pt.records.currBranch);e===void 0||!e?pt.records.head=null:pt.records.head=pt.records.commits.get(e)??null}else{let e=new Error(`Trying to checkout branch which is not yet created. (Help try using "branch ${t}")`);throw e.hash={text:`checkout ${t}`,token:`checkout ${t}`,expected:[`branch ${t}`]},e}},"checkout");o(Sle,"upsert");o(_le,"prettyPrintCommitHistory");jPe=o(function(){V.debug(pt.records.commits);let t=Lle()[0];_le([t])},"prettyPrint"),KPe=o(function(){pt.reset(),vr()},"clear"),QPe=o(function(){return[...pt.records.branchConfig.values()].map((e,r)=>e.order!==null&&e.order!==void 0?e:{...e,order:parseFloat(`0.${r}`)}).sort((e,r)=>(e.order??0)-(r.order??0)).map(({name:e})=>({name:e}))},"getBranchesAsObjArray"),ZPe=o(function(){return pt.records.branches},"getBranches"),JPe=o(function(){return pt.records.commits},"getCommits"),Lle=o(function(){let t=[...pt.records.commits.values()];return t.forEach(function(e){V.debug(e.id)}),t.sort((e,r)=>e.seq-r.seq),t},"getCommitsArray"),eBe=o(function(){return pt.records.currBranch},"getCurrentBranch"),tBe=o(function(){return pt.records.direction},"getDirection"),rBe=o(function(){return pt.records.head},"getHead"),eE={commitType:Hr,getConfig:k0,setDirection:VPe,setOptions:UPe,getOptions:HPe,commit:YPe,branch:WPe,merge:qPe,cherryPick:XPe,checkout:Ale,prettyPrint:jPe,clear:KPe,getBranchesAsObjArray:QPe,getBranches:ZPe,getCommits:JPe,getCommitsArray:Lle,getCurrentBranch:eBe,getDirection:tBe,getHead:rBe,setAccTitle:kr,getAccTitle:Ar,getAccDescription:Lr,setAccDescription:_r,setDiagramTitle:nn,getDiagramTitle:Xr}});var nBe,iBe,aBe,sBe,oBe,lBe,cBe,Dle,Rle=R(()=>{"use strict";Lg();ut();sx();EI();Zk();nBe=o((t,e)=>{cf(t,e),t.dir&&e.setDirection(t.dir);for(let r of t.statements)iBe(r,e)},"populate"),iBe=o((t,e)=>{let n={Commit:o(i=>e.commit(aBe(i)),"Commit"),Branch:o(i=>e.branch(sBe(i)),"Branch"),Merge:o(i=>e.merge(oBe(i)),"Merge"),Checkout:o(i=>e.checkout(lBe(i)),"Checkout"),CherryPicking:o(i=>e.cherryPick(cBe(i)),"CherryPicking")}[t.$type];n?n(t):V.error(`Unknown statement type: ${t.$type}`)},"parseStatement"),aBe=o(t=>({id:t.id,msg:t.message??"",type:t.type!==void 0?Hr[t.type]:Hr.NORMAL,tags:t.tags??void 0}),"parseCommit"),sBe=o(t=>({name:t.name,order:t.order??0}),"parseBranch"),oBe=o(t=>({branch:t.branch,id:t.id??"",type:t.type!==void 0?Hr[t.type]:void 0,tags:t.tags??void 0}),"parseMerge"),lBe=o(t=>t.branch,"parseCheckout"),cBe=o(t=>({id:t.id,targetId:"",tags:t.tags?.length===0?void 0:t.tags,parent:t.parent}),"parseCherryPicking"),Dle={parse:o(async t=>{let e=await Fl("gitGraph",t);V.debug(e),nBe(e,eE)},"parse")}});var uBe,Ko,ff,df,Oc,Hu,E0,Is,Os,tE,ox,rE,hf,Tr,hBe,Mle,Ile,fBe,dBe,pBe,mBe,gBe,yBe,vBe,xBe,bBe,wBe,TBe,kBe,Nle,EBe,lx,CBe,SBe,ABe,_Be,LBe,Ole,Ple=R(()=>{"use strict";Zt();_t();ut();xr();Zk();uBe=de(),Ko=uBe?.gitGraph,ff=10,df=40,Oc=4,Hu=2,E0=8,Is=new Map,Os=new Map,tE=30,ox=new Map,rE=[],hf=0,Tr="LR",hBe=o(()=>{Is.clear(),Os.clear(),ox.clear(),hf=0,rE=[],Tr="LR"},"clear"),Mle=o(t=>{let e=document.createElementNS("http://www.w3.org/2000/svg","text");return(typeof t=="string"?t.split(/\\n|\n|/gi):t).forEach(n=>{let i=document.createElementNS("http://www.w3.org/2000/svg","tspan");i.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),i.setAttribute("dy","1em"),i.setAttribute("x","0"),i.setAttribute("class","row"),i.textContent=n.trim(),e.appendChild(i)}),e},"drawText"),Ile=o(t=>{let e,r,n;return Tr==="BT"?(r=o((i,a)=>i<=a,"comparisonFunc"),n=1/0):(r=o((i,a)=>i>=a,"comparisonFunc"),n=0),t.forEach(i=>{let a=Tr==="TB"||Tr=="BT"?Os.get(i)?.y:Os.get(i)?.x;a!==void 0&&r(a,n)&&(e=i,n=a)}),e},"findClosestParent"),fBe=o(t=>{let e="",r=1/0;return t.forEach(n=>{let i=Os.get(n).y;i<=r&&(e=n,r=i)}),e||void 0},"findClosestParentBT"),dBe=o((t,e,r)=>{let n=r,i=r,a=[];t.forEach(s=>{let l=e.get(s);if(!l)throw new Error(`Commit not found for key ${s}`);l.parents.length?(n=mBe(l),i=Math.max(n,i)):a.push(l),gBe(l,n)}),n=i,a.forEach(s=>{yBe(s,n,r)}),t.forEach(s=>{let l=e.get(s);if(l?.parents.length){let u=fBe(l.parents);n=Os.get(u).y-df,n<=i&&(i=n);let h=Is.get(l.branch).pos,f=n-ff;Os.set(l.id,{x:h,y:f})}})},"setParallelBTPos"),pBe=o(t=>{let e=Ile(t.parents.filter(n=>n!==null));if(!e)throw new Error(`Closest parent not found for commit ${t.id}`);let r=Os.get(e)?.y;if(r===void 0)throw new Error(`Closest parent position not found for commit ${t.id}`);return r},"findClosestParentPos"),mBe=o(t=>pBe(t)+df,"calculateCommitPosition"),gBe=o((t,e)=>{let r=Is.get(t.branch);if(!r)throw new Error(`Branch not found for commit ${t.id}`);let n=r.pos,i=e+ff;return Os.set(t.id,{x:n,y:i}),{x:n,y:i}},"setCommitPosition"),yBe=o((t,e,r)=>{let n=Is.get(t.branch);if(!n)throw new Error(`Branch not found for commit ${t.id}`);let i=e+r,a=n.pos;Os.set(t.id,{x:a,y:i})},"setRootPosition"),vBe=o((t,e,r,n,i,a)=>{if(a===Hr.HIGHLIGHT)t.append("rect").attr("x",r.x-10).attr("y",r.y-10).attr("width",20).attr("height",20).attr("class",`commit ${e.id} commit-highlight${i%E0} ${n}-outer`),t.append("rect").attr("x",r.x-6).attr("y",r.y-6).attr("width",12).attr("height",12).attr("class",`commit ${e.id} commit${i%E0} ${n}-inner`);else if(a===Hr.CHERRY_PICK)t.append("circle").attr("cx",r.x).attr("cy",r.y).attr("r",10).attr("class",`commit ${e.id} ${n}`),t.append("circle").attr("cx",r.x-3).attr("cy",r.y+2).attr("r",2.75).attr("fill","#fff").attr("class",`commit ${e.id} ${n}`),t.append("circle").attr("cx",r.x+3).attr("cy",r.y+2).attr("r",2.75).attr("fill","#fff").attr("class",`commit ${e.id} ${n}`),t.append("line").attr("x1",r.x+3).attr("y1",r.y+1).attr("x2",r.x).attr("y2",r.y-5).attr("stroke","#fff").attr("class",`commit ${e.id} ${n}`),t.append("line").attr("x1",r.x-3).attr("y1",r.y+1).attr("x2",r.x).attr("y2",r.y-5).attr("stroke","#fff").attr("class",`commit ${e.id} ${n}`);else{let s=t.append("circle");if(s.attr("cx",r.x),s.attr("cy",r.y),s.attr("r",e.type===Hr.MERGE?9:10),s.attr("class",`commit ${e.id} commit${i%E0}`),a===Hr.MERGE){let l=t.append("circle");l.attr("cx",r.x),l.attr("cy",r.y),l.attr("r",6),l.attr("class",`commit ${n} ${e.id} commit${i%E0}`)}a===Hr.REVERSE&&t.append("path").attr("d",`M ${r.x-5},${r.y-5}L${r.x+5},${r.y+5}M${r.x-5},${r.y+5}L${r.x+5},${r.y-5}`).attr("class",`commit ${n} ${e.id} commit${i%E0}`)}},"drawCommitBullet"),xBe=o((t,e,r,n)=>{if(e.type!==Hr.CHERRY_PICK&&(e.customId&&e.type===Hr.MERGE||e.type!==Hr.MERGE)&&Ko?.showCommitLabel){let i=t.append("g"),a=i.insert("rect").attr("class","commit-label-bkg"),s=i.append("text").attr("x",n).attr("y",r.y+25).attr("class","commit-label").text(e.id),l=s.node()?.getBBox();if(l&&(a.attr("x",r.posWithOffset-l.width/2-Hu).attr("y",r.y+13.5).attr("width",l.width+2*Hu).attr("height",l.height+2*Hu),Tr==="TB"||Tr==="BT"?(a.attr("x",r.x-(l.width+4*Oc+5)).attr("y",r.y-12),s.attr("x",r.x-(l.width+4*Oc)).attr("y",r.y+l.height-12)):s.attr("x",r.posWithOffset-l.width/2),Ko.rotateCommitLabel))if(Tr==="TB"||Tr==="BT")s.attr("transform","rotate(-45, "+r.x+", "+r.y+")"),a.attr("transform","rotate(-45, "+r.x+", "+r.y+")");else{let u=-7.5-(l.width+10)/25*9.5,h=10+l.width/25*8.5;i.attr("transform","translate("+u+", "+h+") rotate(-45, "+n+", "+r.y+")")}}},"drawCommitLabel"),bBe=o((t,e,r,n)=>{if(e.tags.length>0){let i=0,a=0,s=0,l=[];for(let u of e.tags.reverse()){let h=t.insert("polygon"),f=t.append("circle"),d=t.append("text").attr("y",r.y-16-i).attr("class","tag-label").text(u),p=d.node()?.getBBox();if(!p)throw new Error("Tag bbox not found");a=Math.max(a,p.width),s=Math.max(s,p.height),d.attr("x",r.posWithOffset-p.width/2),l.push({tag:d,hole:f,rect:h,yOffset:i}),i+=20}for(let{tag:u,hole:h,rect:f,yOffset:d}of l){let p=s/2,m=r.y-19.2-d;if(f.attr("class","tag-label-bkg").attr("points",` + ${n-a/2-Oc/2},${m+Hu} + ${n-a/2-Oc/2},${m-Hu} + ${r.posWithOffset-a/2-Oc},${m-p-Hu} + ${r.posWithOffset+a/2+Oc},${m-p-Hu} + ${r.posWithOffset+a/2+Oc},${m+p+Hu} + ${r.posWithOffset-a/2-Oc},${m+p+Hu}`),h.attr("cy",m).attr("cx",n-a/2+Oc/2).attr("r",1.5).attr("class","tag-hole"),Tr==="TB"||Tr==="BT"){let g=n+d;f.attr("class","tag-label-bkg").attr("points",` + ${r.x},${g+2} + ${r.x},${g-2} + ${r.x+ff},${g-p-2} + ${r.x+ff+a+4},${g-p-2} + ${r.x+ff+a+4},${g+p+2} + ${r.x+ff},${g+p+2}`).attr("transform","translate(12,12) rotate(45, "+r.x+","+n+")"),h.attr("cx",r.x+Oc/2).attr("cy",g).attr("transform","translate(12,12) rotate(45, "+r.x+","+n+")"),u.attr("x",r.x+5).attr("y",g+3).attr("transform","translate(14,14) rotate(45, "+r.x+","+n+")")}}}},"drawCommitTags"),wBe=o(t=>{switch(t.customType??t.type){case Hr.NORMAL:return"commit-normal";case Hr.REVERSE:return"commit-reverse";case Hr.HIGHLIGHT:return"commit-highlight";case Hr.MERGE:return"commit-merge";case Hr.CHERRY_PICK:return"commit-cherry-pick";default:return"commit-normal"}},"getCommitClassType"),TBe=o((t,e,r,n)=>{let i={x:0,y:0};if(t.parents.length>0){let a=Ile(t.parents);if(a){let s=n.get(a)??i;return e==="TB"?s.y+df:e==="BT"?(n.get(t.id)??i).y-df:s.x+df}}else return e==="TB"?tE:e==="BT"?(n.get(t.id)??i).y-df:0;return 0},"calculatePosition"),kBe=o((t,e,r)=>{let n=Tr==="BT"&&r?e:e+ff,i=Tr==="TB"||Tr==="BT"?n:Is.get(t.branch)?.pos,a=Tr==="TB"||Tr==="BT"?Is.get(t.branch)?.pos:n;if(a===void 0||i===void 0)throw new Error(`Position were undefined for commit ${t.id}`);return{x:a,y:i,posWithOffset:n}},"getCommitPosition"),Nle=o((t,e,r)=>{if(!Ko)throw new Error("GitGraph config not found");let n=t.append("g").attr("class","commit-bullets"),i=t.append("g").attr("class","commit-labels"),a=Tr==="TB"||Tr==="BT"?tE:0,s=[...e.keys()],l=Ko?.parallelCommits??!1,u=o((f,d)=>{let p=e.get(f)?.seq,m=e.get(d)?.seq;return p!==void 0&&m!==void 0?p-m:0},"sortKeys"),h=s.sort(u);Tr==="BT"&&(l&&dBe(h,e,a),h=h.reverse()),h.forEach(f=>{let d=e.get(f);if(!d)throw new Error(`Commit not found for key ${f}`);l&&(a=TBe(d,Tr,a,Os));let p=kBe(d,a,l);if(r){let m=wBe(d),g=d.customType??d.type,y=Is.get(d.branch)?.index??0;vBe(n,d,p,m,y,g),xBe(i,d,p,a),bBe(i,d,p,a)}Tr==="TB"||Tr==="BT"?Os.set(d.id,{x:p.x,y:p.posWithOffset}):Os.set(d.id,{x:p.posWithOffset,y:p.y}),a=Tr==="BT"&&l?a+df:a+df+ff,a>hf&&(hf=a)})},"drawCommits"),EBe=o((t,e,r,n,i)=>{let s=(Tr==="TB"||Tr==="BT"?r.xh.branch===s,"isOnBranchToGetCurve"),u=o(h=>h.seq>t.seq&&h.sequ(h)&&l(h))},"shouldRerouteArrow"),lx=o((t,e,r=0)=>{let n=t+Math.abs(t-e)/2;if(r>5)return n;if(rE.every(s=>Math.abs(s-n)>=10))return rE.push(n),n;let a=Math.abs(t-e);return lx(t,e-a/5,r+1)},"findLane"),CBe=o((t,e,r,n)=>{let i=Os.get(e.id),a=Os.get(r.id);if(i===void 0||a===void 0)throw new Error(`Commit positions not found for commits ${e.id} and ${r.id}`);let s=EBe(e,r,i,a,n),l="",u="",h=0,f=0,d=Is.get(r.branch)?.index;r.type===Hr.MERGE&&e.id!==r.parents[0]&&(d=Is.get(e.branch)?.index);let p;if(s){l="A 10 10, 0, 0, 0,",u="A 10 10, 0, 0, 1,",h=10,f=10;let m=i.ya.x&&(l="A 20 20, 0, 0, 0,",u="A 20 20, 0, 0, 1,",h=20,f=20,r.type===Hr.MERGE&&e.id!==r.parents[0]?p=`M ${i.x} ${i.y} L ${i.x} ${a.y-h} ${u} ${i.x-f} ${a.y} L ${a.x} ${a.y}`:p=`M ${i.x} ${i.y} L ${a.x+h} ${i.y} ${l} ${a.x} ${i.y+f} L ${a.x} ${a.y}`),i.x===a.x&&(p=`M ${i.x} ${i.y} L ${a.x} ${a.y}`)):Tr==="BT"?(i.xa.x&&(l="A 20 20, 0, 0, 0,",u="A 20 20, 0, 0, 1,",h=20,f=20,r.type===Hr.MERGE&&e.id!==r.parents[0]?p=`M ${i.x} ${i.y} L ${i.x} ${a.y+h} ${l} ${i.x-f} ${a.y} L ${a.x} ${a.y}`:p=`M ${i.x} ${i.y} L ${a.x-h} ${i.y} ${l} ${a.x} ${i.y-f} L ${a.x} ${a.y}`),i.x===a.x&&(p=`M ${i.x} ${i.y} L ${a.x} ${a.y}`)):(i.ya.y&&(r.type===Hr.MERGE&&e.id!==r.parents[0]?p=`M ${i.x} ${i.y} L ${a.x-h} ${i.y} ${l} ${a.x} ${i.y-f} L ${a.x} ${a.y}`:p=`M ${i.x} ${i.y} L ${i.x} ${a.y+h} ${u} ${i.x+f} ${a.y} L ${a.x} ${a.y}`),i.y===a.y&&(p=`M ${i.x} ${i.y} L ${a.x} ${a.y}`));if(p===void 0)throw new Error("Line definition not found");t.append("path").attr("d",p).attr("class","arrow arrow"+d%E0)},"drawArrow"),SBe=o((t,e)=>{let r=t.append("g").attr("class","commit-arrows");[...e.keys()].forEach(n=>{let i=e.get(n);i.parents&&i.parents.length>0&&i.parents.forEach(a=>{CBe(r,e.get(a),i,e)})})},"drawArrows"),ABe=o((t,e)=>{let r=t.append("g");e.forEach((n,i)=>{let a=i%E0,s=Is.get(n.name)?.pos;if(s===void 0)throw new Error(`Position not found for branch ${n.name}`);let l=r.append("line");l.attr("x1",0),l.attr("y1",s),l.attr("x2",hf),l.attr("y2",s),l.attr("class","branch branch"+a),Tr==="TB"?(l.attr("y1",tE),l.attr("x1",s),l.attr("y2",hf),l.attr("x2",s)):Tr==="BT"&&(l.attr("y1",hf),l.attr("x1",s),l.attr("y2",tE),l.attr("x2",s)),rE.push(s);let u=n.name,h=Mle(u),f=r.insert("rect"),p=r.insert("g").attr("class","branchLabel").insert("g").attr("class","label branch-label"+a);p.node().appendChild(h);let m=h.getBBox();f.attr("class","branchLabelBkg label"+a).attr("rx",4).attr("ry",4).attr("x",-m.width-4-(Ko?.rotateCommitLabel===!0?30:0)).attr("y",-m.height/2+8).attr("width",m.width+18).attr("height",m.height+4),p.attr("transform","translate("+(-m.width-14-(Ko?.rotateCommitLabel===!0?30:0))+", "+(s-m.height/2-1)+")"),Tr==="TB"?(f.attr("x",s-m.width/2-10).attr("y",0),p.attr("transform","translate("+(s-m.width/2-5)+", 0)")):Tr==="BT"?(f.attr("x",s-m.width/2-10).attr("y",hf),p.attr("transform","translate("+(s-m.width/2-5)+", "+hf+")")):f.attr("transform","translate(-19, "+(s-m.height/2)+")")})},"drawBranches"),_Be=o(function(t,e,r,n,i){return Is.set(t,{pos:e,index:r}),e+=50+(i?40:0)+(Tr==="TB"||Tr==="BT"?n.width/2:0),e},"setBranchPosition"),LBe=o(function(t,e,r,n){if(hBe(),V.debug("in gitgraph renderer",t+` +`,"id:",e,r),!Ko)throw new Error("GitGraph config not found");let i=Ko.rotateCommitLabel??!1,a=n.db;ox=a.getCommits();let s=a.getBranchesAsObjArray();Tr=a.getDirection();let l=$e(`[id="${e}"]`),u=0;s.forEach((h,f)=>{let d=Mle(h.name),p=l.append("g"),m=p.insert("g").attr("class","branchLabel"),g=m.insert("g").attr("class","label branch-label");g.node()?.appendChild(d);let y=d.getBBox();u=_Be(h.name,u,f,y,i),g.remove(),m.remove(),p.remove()}),Nle(l,ox,!1),Ko.showBranches&&ABe(l,s),SBe(l,ox),Nle(l,ox,!0),Lt.insertTitle(l,"gitTitleText",Ko.titleTopMargin??0,a.getDiagramTitle()),aS(void 0,l,Ko.diagramPadding,Ko.useMaxWidth)},"draw"),Ole={draw:LBe}});var DBe,Ble,Fle=R(()=>{"use strict";DBe=o(t=>` + .commit-id, + .commit-msg, + .branch-label { + fill: lightgrey; + color: lightgrey; + font-family: 'trebuchet ms', verdana, arial, sans-serif; + font-family: var(--mermaid-font-family); + } + ${[0,1,2,3,4,5,6,7].map(e=>` + .branch-label${e} { fill: ${t["gitBranchLabel"+e]}; } + .commit${e} { stroke: ${t["git"+e]}; fill: ${t["git"+e]}; } + .commit-highlight${e} { stroke: ${t["gitInv"+e]}; fill: ${t["gitInv"+e]}; } + .label${e} { fill: ${t["git"+e]}; } + .arrow${e} { stroke: ${t["git"+e]}; } + `).join(` +`)} + + .branch { + stroke-width: 1; + stroke: ${t.lineColor}; + stroke-dasharray: 2; + } + .commit-label { font-size: ${t.commitLabelFontSize}; fill: ${t.commitLabelColor};} + .commit-label-bkg { font-size: ${t.commitLabelFontSize}; fill: ${t.commitLabelBackground}; opacity: 0.5; } + .tag-label { font-size: ${t.tagLabelFontSize}; fill: ${t.tagLabelColor};} + .tag-label-bkg { fill: ${t.tagLabelBackground}; stroke: ${t.tagLabelBorder}; } + .tag-hole { fill: ${t.textColor}; } + + .commit-merge { + stroke: ${t.primaryColor}; + fill: ${t.primaryColor}; + } + .commit-reverse { + stroke: ${t.primaryColor}; + fill: ${t.primaryColor}; + stroke-width: 3; + } + .commit-highlight-outer { + } + .commit-highlight-inner { + stroke: ${t.primaryColor}; + fill: ${t.primaryColor}; + } + + .arrow { stroke-width: 8; stroke-linecap: round; fill: none} + .gitTitleText { + text-anchor: middle; + font-size: 18px; + fill: ${t.textColor}; + } +`,"getStyles"),Ble=DBe});var zle={};hr(zle,{diagram:()=>RBe});var RBe,Gle=R(()=>{"use strict";Rle();EI();Ple();Fle();RBe={parser:Dle,db:eE,renderer:Ole,styles:Ble}});var CI,Ule,Hle=R(()=>{"use strict";CI=function(){var t=o(function(I,C,O,D){for(O=O||{},D=I.length;D--;O[I[D]]=C);return O},"o"),e=[6,8,10,12,13,14,15,16,17,18,20,21,22,23,24,25,26,27,28,29,30,31,33,35,36,38,40],r=[1,26],n=[1,27],i=[1,28],a=[1,29],s=[1,30],l=[1,31],u=[1,32],h=[1,33],f=[1,34],d=[1,9],p=[1,10],m=[1,11],g=[1,12],y=[1,13],v=[1,14],x=[1,15],b=[1,16],w=[1,19],S=[1,20],T=[1,21],E=[1,22],_=[1,23],A=[1,25],L=[1,35],M={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,start:3,gantt:4,document:5,EOF:6,line:7,SPACE:8,statement:9,NL:10,weekday:11,weekday_monday:12,weekday_tuesday:13,weekday_wednesday:14,weekday_thursday:15,weekday_friday:16,weekday_saturday:17,weekday_sunday:18,weekend:19,weekend_friday:20,weekend_saturday:21,dateFormat:22,inclusiveEndDates:23,topAxis:24,axisFormat:25,tickInterval:26,excludes:27,includes:28,todayMarker:29,title:30,acc_title:31,acc_title_value:32,acc_descr:33,acc_descr_value:34,acc_descr_multiline_value:35,section:36,clickStatement:37,taskTxt:38,taskData:39,click:40,callbackname:41,callbackargs:42,href:43,clickStatementDebug:44,$accept:0,$end:1},terminals_:{2:"error",4:"gantt",6:"EOF",8:"SPACE",10:"NL",12:"weekday_monday",13:"weekday_tuesday",14:"weekday_wednesday",15:"weekday_thursday",16:"weekday_friday",17:"weekday_saturday",18:"weekday_sunday",20:"weekend_friday",21:"weekend_saturday",22:"dateFormat",23:"inclusiveEndDates",24:"topAxis",25:"axisFormat",26:"tickInterval",27:"excludes",28:"includes",29:"todayMarker",30:"title",31:"acc_title",32:"acc_title_value",33:"acc_descr",34:"acc_descr_value",35:"acc_descr_multiline_value",36:"section",38:"taskTxt",39:"taskData",40:"click",41:"callbackname",42:"callbackargs",43:"href"},productions_:[0,[3,3],[5,0],[5,2],[7,2],[7,1],[7,1],[7,1],[11,1],[11,1],[11,1],[11,1],[11,1],[11,1],[11,1],[19,1],[19,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,1],[9,2],[9,2],[9,1],[9,1],[9,1],[9,2],[37,2],[37,3],[37,3],[37,4],[37,3],[37,4],[37,2],[44,2],[44,3],[44,3],[44,4],[44,3],[44,4],[44,2]],performAction:o(function(C,O,D,P,F,B,$){var z=B.length-1;switch(F){case 1:return B[z-1];case 2:this.$=[];break;case 3:B[z-1].push(B[z]),this.$=B[z-1];break;case 4:case 5:this.$=B[z];break;case 6:case 7:this.$=[];break;case 8:P.setWeekday("monday");break;case 9:P.setWeekday("tuesday");break;case 10:P.setWeekday("wednesday");break;case 11:P.setWeekday("thursday");break;case 12:P.setWeekday("friday");break;case 13:P.setWeekday("saturday");break;case 14:P.setWeekday("sunday");break;case 15:P.setWeekend("friday");break;case 16:P.setWeekend("saturday");break;case 17:P.setDateFormat(B[z].substr(11)),this.$=B[z].substr(11);break;case 18:P.enableInclusiveEndDates(),this.$=B[z].substr(18);break;case 19:P.TopAxis(),this.$=B[z].substr(8);break;case 20:P.setAxisFormat(B[z].substr(11)),this.$=B[z].substr(11);break;case 21:P.setTickInterval(B[z].substr(13)),this.$=B[z].substr(13);break;case 22:P.setExcludes(B[z].substr(9)),this.$=B[z].substr(9);break;case 23:P.setIncludes(B[z].substr(9)),this.$=B[z].substr(9);break;case 24:P.setTodayMarker(B[z].substr(12)),this.$=B[z].substr(12);break;case 27:P.setDiagramTitle(B[z].substr(6)),this.$=B[z].substr(6);break;case 28:this.$=B[z].trim(),P.setAccTitle(this.$);break;case 29:case 30:this.$=B[z].trim(),P.setAccDescription(this.$);break;case 31:P.addSection(B[z].substr(8)),this.$=B[z].substr(8);break;case 33:P.addTask(B[z-1],B[z]),this.$="task";break;case 34:this.$=B[z-1],P.setClickEvent(B[z-1],B[z],null);break;case 35:this.$=B[z-2],P.setClickEvent(B[z-2],B[z-1],B[z]);break;case 36:this.$=B[z-2],P.setClickEvent(B[z-2],B[z-1],null),P.setLink(B[z-2],B[z]);break;case 37:this.$=B[z-3],P.setClickEvent(B[z-3],B[z-2],B[z-1]),P.setLink(B[z-3],B[z]);break;case 38:this.$=B[z-2],P.setClickEvent(B[z-2],B[z],null),P.setLink(B[z-2],B[z-1]);break;case 39:this.$=B[z-3],P.setClickEvent(B[z-3],B[z-1],B[z]),P.setLink(B[z-3],B[z-2]);break;case 40:this.$=B[z-1],P.setLink(B[z-1],B[z]);break;case 41:case 47:this.$=B[z-1]+" "+B[z];break;case 42:case 43:case 45:this.$=B[z-2]+" "+B[z-1]+" "+B[z];break;case 44:case 46:this.$=B[z-3]+" "+B[z-2]+" "+B[z-1]+" "+B[z];break}},"anonymous"),table:[{3:1,4:[1,2]},{1:[3]},t(e,[2,2],{5:3}),{6:[1,4],7:5,8:[1,6],9:7,10:[1,8],11:17,12:r,13:n,14:i,15:a,16:s,17:l,18:u,19:18,20:h,21:f,22:d,23:p,24:m,25:g,26:y,27:v,28:x,29:b,30:w,31:S,33:T,35:E,36:_,37:24,38:A,40:L},t(e,[2,7],{1:[2,1]}),t(e,[2,3]),{9:36,11:17,12:r,13:n,14:i,15:a,16:s,17:l,18:u,19:18,20:h,21:f,22:d,23:p,24:m,25:g,26:y,27:v,28:x,29:b,30:w,31:S,33:T,35:E,36:_,37:24,38:A,40:L},t(e,[2,5]),t(e,[2,6]),t(e,[2,17]),t(e,[2,18]),t(e,[2,19]),t(e,[2,20]),t(e,[2,21]),t(e,[2,22]),t(e,[2,23]),t(e,[2,24]),t(e,[2,25]),t(e,[2,26]),t(e,[2,27]),{32:[1,37]},{34:[1,38]},t(e,[2,30]),t(e,[2,31]),t(e,[2,32]),{39:[1,39]},t(e,[2,8]),t(e,[2,9]),t(e,[2,10]),t(e,[2,11]),t(e,[2,12]),t(e,[2,13]),t(e,[2,14]),t(e,[2,15]),t(e,[2,16]),{41:[1,40],43:[1,41]},t(e,[2,4]),t(e,[2,28]),t(e,[2,29]),t(e,[2,33]),t(e,[2,34],{42:[1,42],43:[1,43]}),t(e,[2,40],{41:[1,44]}),t(e,[2,35],{43:[1,45]}),t(e,[2,36]),t(e,[2,38],{42:[1,46]}),t(e,[2,37]),t(e,[2,39])],defaultActions:{},parseError:o(function(C,O){if(O.recoverable)this.trace(C);else{var D=new Error(C);throw D.hash=O,D}},"parseError"),parse:o(function(C){var O=this,D=[0],P=[],F=[null],B=[],$=this.table,z="",Y=0,Q=0,X=0,ie=2,j=1,J=B.slice.call(arguments,1),Z=Object.create(this.lexer),H={yy:{}};for(var q in this.yy)Object.prototype.hasOwnProperty.call(this.yy,q)&&(H.yy[q]=this.yy[q]);Z.setInput(C,H.yy),H.yy.lexer=Z,H.yy.parser=this,typeof Z.yylloc>"u"&&(Z.yylloc={});var K=Z.yylloc;B.push(K);var se=Z.options&&Z.options.ranges;typeof H.yy.parseError=="function"?this.parseError=H.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function ce(ge){D.length=D.length-2*ge,F.length=F.length-ge,B.length=B.length-ge}o(ce,"popStack");function ue(){var ge;return ge=P.pop()||Z.lex()||j,typeof ge!="number"&&(ge instanceof Array&&(P=ge,ge=P.pop()),ge=O.symbols_[ge]||ge),ge}o(ue,"lex");for(var te,De,oe,ke,Ie,Se,Ue={},Pe,_e,me,W;;){if(oe=D[D.length-1],this.defaultActions[oe]?ke=this.defaultActions[oe]:((te===null||typeof te>"u")&&(te=ue()),ke=$[oe]&&$[oe][te]),typeof ke>"u"||!ke.length||!ke[0]){var fe="";W=[];for(Pe in $[oe])this.terminals_[Pe]&&Pe>ie&&W.push("'"+this.terminals_[Pe]+"'");Z.showPosition?fe="Parse error on line "+(Y+1)+`: +`+Z.showPosition()+` +Expecting `+W.join(", ")+", got '"+(this.terminals_[te]||te)+"'":fe="Parse error on line "+(Y+1)+": Unexpected "+(te==j?"end of input":"'"+(this.terminals_[te]||te)+"'"),this.parseError(fe,{text:Z.match,token:this.terminals_[te]||te,line:Z.yylineno,loc:K,expected:W})}if(ke[0]instanceof Array&&ke.length>1)throw new Error("Parse Error: multiple actions possible at state: "+oe+", token: "+te);switch(ke[0]){case 1:D.push(te),F.push(Z.yytext),B.push(Z.yylloc),D.push(ke[1]),te=null,De?(te=De,De=null):(Q=Z.yyleng,z=Z.yytext,Y=Z.yylineno,K=Z.yylloc,X>0&&X--);break;case 2:if(_e=this.productions_[ke[1]][1],Ue.$=F[F.length-_e],Ue._$={first_line:B[B.length-(_e||1)].first_line,last_line:B[B.length-1].last_line,first_column:B[B.length-(_e||1)].first_column,last_column:B[B.length-1].last_column},se&&(Ue._$.range=[B[B.length-(_e||1)].range[0],B[B.length-1].range[1]]),Se=this.performAction.apply(Ue,[z,Q,Y,H.yy,ke[1],F,B].concat(J)),typeof Se<"u")return Se;_e&&(D=D.slice(0,-1*_e*2),F=F.slice(0,-1*_e),B=B.slice(0,-1*_e)),D.push(this.productions_[ke[1]][0]),F.push(Ue.$),B.push(Ue._$),me=$[D[D.length-2]][D[D.length-1]],D.push(me);break;case 3:return!0}}return!0},"parse")},N=function(){var I={EOF:1,parseError:o(function(O,D){if(this.yy.parser)this.yy.parser.parseError(O,D);else throw new Error(O)},"parseError"),setInput:o(function(C,O){return this.yy=O||this.yy||{},this._input=C,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var C=this._input[0];this.yytext+=C,this.yyleng++,this.offset++,this.match+=C,this.matched+=C;var O=C.match(/(?:\r\n?|\n).*/g);return O?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),C},"input"),unput:o(function(C){var O=C.length,D=C.split(/(?:\r\n?|\n)/g);this._input=C+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-O),this.offset-=O;var P=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),D.length-1&&(this.yylineno-=D.length-1);var F=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:D?(D.length===P.length?this.yylloc.first_column:0)+P[P.length-D.length].length-D[0].length:this.yylloc.first_column-O},this.options.ranges&&(this.yylloc.range=[F[0],F[0]+this.yyleng-O]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(C){this.unput(this.match.slice(C))},"less"),pastInput:o(function(){var C=this.matched.substr(0,this.matched.length-this.match.length);return(C.length>20?"...":"")+C.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var C=this.match;return C.length<20&&(C+=this._input.substr(0,20-C.length)),(C.substr(0,20)+(C.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var C=this.pastInput(),O=new Array(C.length+1).join("-");return C+this.upcomingInput()+` +`+O+"^"},"showPosition"),test_match:o(function(C,O){var D,P,F;if(this.options.backtrack_lexer&&(F={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(F.yylloc.range=this.yylloc.range.slice(0))),P=C[0].match(/(?:\r\n?|\n).*/g),P&&(this.yylineno+=P.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:P?P[P.length-1].length-P[P.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+C[0].length},this.yytext+=C[0],this.match+=C[0],this.matches=C,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(C[0].length),this.matched+=C[0],D=this.performAction.call(this,this.yy,this,O,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),D)return D;if(this._backtrack){for(var B in F)this[B]=F[B];return!1}return!1},"test_match"),next:o(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var C,O,D,P;this._more||(this.yytext="",this.match="");for(var F=this._currentRules(),B=0;BO[0].length)){if(O=D,P=B,this.options.backtrack_lexer){if(C=this.test_match(D,F[B]),C!==!1)return C;if(this._backtrack){O=!1;continue}else return!1}else if(!this.options.flex)break}return O?(C=this.test_match(O,F[P]),C!==!1?C:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:o(function(){var O=this.next();return O||this.lex()},"lex"),begin:o(function(O){this.conditionStack.push(O)},"begin"),popState:o(function(){var O=this.conditionStack.length-1;return O>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:o(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:o(function(O){return O=this.conditionStack.length-1-Math.abs(O||0),O>=0?this.conditionStack[O]:"INITIAL"},"topState"),pushState:o(function(O){this.begin(O)},"pushState"),stateStackSize:o(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:o(function(O,D,P,F){var B=F;switch(P){case 0:return this.begin("open_directive"),"open_directive";break;case 1:return this.begin("acc_title"),31;break;case 2:return this.popState(),"acc_title_value";break;case 3:return this.begin("acc_descr"),33;break;case 4:return this.popState(),"acc_descr_value";break;case 5:this.begin("acc_descr_multiline");break;case 6:this.popState();break;case 7:return"acc_descr_multiline_value";case 8:break;case 9:break;case 10:break;case 11:return 10;case 12:break;case 13:break;case 14:this.begin("href");break;case 15:this.popState();break;case 16:return 43;case 17:this.begin("callbackname");break;case 18:this.popState();break;case 19:this.popState(),this.begin("callbackargs");break;case 20:return 41;case 21:this.popState();break;case 22:return 42;case 23:this.begin("click");break;case 24:this.popState();break;case 25:return 40;case 26:return 4;case 27:return 22;case 28:return 23;case 29:return 24;case 30:return 25;case 31:return 26;case 32:return 28;case 33:return 27;case 34:return 29;case 35:return 12;case 36:return 13;case 37:return 14;case 38:return 15;case 39:return 16;case 40:return 17;case 41:return 18;case 42:return 20;case 43:return 21;case 44:return"date";case 45:return 30;case 46:return"accDescription";case 47:return 36;case 48:return 38;case 49:return 39;case 50:return":";case 51:return 6;case 52:return"INVALID"}},"anonymous"),rules:[/^(?:%%\{)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?:%%(?!\{)*[^\n]*)/i,/^(?:[^\}]%%*[^\n]*)/i,/^(?:%%*[^\n]*[\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:%[^\n]*)/i,/^(?:href[\s]+["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:call[\s]+)/i,/^(?:\([\s]*\))/i,/^(?:\()/i,/^(?:[^(]*)/i,/^(?:\))/i,/^(?:[^)]*)/i,/^(?:click[\s]+)/i,/^(?:[\s\n])/i,/^(?:[^\s\n]*)/i,/^(?:gantt\b)/i,/^(?:dateFormat\s[^#\n;]+)/i,/^(?:inclusiveEndDates\b)/i,/^(?:topAxis\b)/i,/^(?:axisFormat\s[^#\n;]+)/i,/^(?:tickInterval\s[^#\n;]+)/i,/^(?:includes\s[^#\n;]+)/i,/^(?:excludes\s[^#\n;]+)/i,/^(?:todayMarker\s[^\n;]+)/i,/^(?:weekday\s+monday\b)/i,/^(?:weekday\s+tuesday\b)/i,/^(?:weekday\s+wednesday\b)/i,/^(?:weekday\s+thursday\b)/i,/^(?:weekday\s+friday\b)/i,/^(?:weekday\s+saturday\b)/i,/^(?:weekday\s+sunday\b)/i,/^(?:weekend\s+friday\b)/i,/^(?:weekend\s+saturday\b)/i,/^(?:\d\d\d\d-\d\d-\d\d\b)/i,/^(?:title\s[^\n]+)/i,/^(?:accDescription\s[^#\n;]+)/i,/^(?:section\s[^\n]+)/i,/^(?:[^:\n]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],conditions:{acc_descr_multiline:{rules:[6,7],inclusive:!1},acc_descr:{rules:[4],inclusive:!1},acc_title:{rules:[2],inclusive:!1},callbackargs:{rules:[21,22],inclusive:!1},callbackname:{rules:[18,19,20],inclusive:!1},href:{rules:[15,16],inclusive:!1},click:{rules:[24,25],inclusive:!1},INITIAL:{rules:[0,1,3,5,8,9,10,11,12,13,14,17,23,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52],inclusive:!0}}};return I}();M.lexer=N;function k(){this.yy={}}return o(k,"Parser"),k.prototype=M,M.Parser=k,new k}();CI.parser=CI;Ule=CI});var Yle=gi((SI,AI)=>{"use strict";(function(t,e){typeof SI=="object"&&typeof AI<"u"?AI.exports=e():typeof define=="function"&&define.amd?define(e):(t=typeof globalThis<"u"?globalThis:t||self).dayjs_plugin_isoWeek=e()})(SI,function(){"use strict";var t="day";return function(e,r,n){var i=o(function(l){return l.add(4-l.isoWeekday(),t)},"a"),a=r.prototype;a.isoWeekYear=function(){return i(this).year()},a.isoWeek=function(l){if(!this.$utils().u(l))return this.add(7*(l-this.isoWeek()),t);var u,h,f,d,p=i(this),m=(u=this.isoWeekYear(),h=this.$u,f=(h?n.utc:n)().year(u).startOf("year"),d=4-f.isoWeekday(),f.isoWeekday()>4&&(d+=7),f.add(d,t));return p.diff(m,"week")+1},a.isoWeekday=function(l){return this.$utils().u(l)?this.day()||7:this.day(this.day()%7?l:l-7)};var s=a.startOf;a.startOf=function(l,u){var h=this.$utils(),f=!!h.u(u)||u;return h.p(l)==="isoweek"?f?this.date(this.date()-(this.isoWeekday()-1)).startOf("day"):this.date(this.date()-1-(this.isoWeekday()-1)+7).endOf("day"):s.bind(this)(l,u)}}})});var Wle=gi((_I,LI)=>{"use strict";(function(t,e){typeof _I=="object"&&typeof LI<"u"?LI.exports=e():typeof define=="function"&&define.amd?define(e):(t=typeof globalThis<"u"?globalThis:t||self).dayjs_plugin_customParseFormat=e()})(_I,function(){"use strict";var t={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},e=/(\[[^[]*\])|([-_:/.,()\s]+)|(A|a|Q|YYYY|YY?|ww?|MM?M?M?|Do|DD?|hh?|HH?|mm?|ss?|S{1,3}|z|ZZ?)/g,r=/\d/,n=/\d\d/,i=/\d\d?/,a=/\d*[^-_:/,()\s\d]+/,s={},l=o(function(g){return(g=+g)+(g>68?1900:2e3)},"a"),u=o(function(g){return function(y){this[g]=+y}},"f"),h=[/[+-]\d\d:?(\d\d)?|Z/,function(g){(this.zone||(this.zone={})).offset=function(y){if(!y||y==="Z")return 0;var v=y.match(/([+-]|\d\d)/g),x=60*v[1]+(+v[2]||0);return x===0?0:v[0]==="+"?-x:x}(g)}],f=o(function(g){var y=s[g];return y&&(y.indexOf?y:y.s.concat(y.f))},"u"),d=o(function(g,y){var v,x=s.meridiem;if(x){for(var b=1;b<=24;b+=1)if(g.indexOf(x(b,0,y))>-1){v=b>12;break}}else v=g===(y?"pm":"PM");return v},"d"),p={A:[a,function(g){this.afternoon=d(g,!1)}],a:[a,function(g){this.afternoon=d(g,!0)}],Q:[r,function(g){this.month=3*(g-1)+1}],S:[r,function(g){this.milliseconds=100*+g}],SS:[n,function(g){this.milliseconds=10*+g}],SSS:[/\d{3}/,function(g){this.milliseconds=+g}],s:[i,u("seconds")],ss:[i,u("seconds")],m:[i,u("minutes")],mm:[i,u("minutes")],H:[i,u("hours")],h:[i,u("hours")],HH:[i,u("hours")],hh:[i,u("hours")],D:[i,u("day")],DD:[n,u("day")],Do:[a,function(g){var y=s.ordinal,v=g.match(/\d+/);if(this.day=v[0],y)for(var x=1;x<=31;x+=1)y(x).replace(/\[|\]/g,"")===g&&(this.day=x)}],w:[i,u("week")],ww:[n,u("week")],M:[i,u("month")],MM:[n,u("month")],MMM:[a,function(g){var y=f("months"),v=(f("monthsShort")||y.map(function(x){return x.slice(0,3)})).indexOf(g)+1;if(v<1)throw new Error;this.month=v%12||v}],MMMM:[a,function(g){var y=f("months").indexOf(g)+1;if(y<1)throw new Error;this.month=y%12||y}],Y:[/[+-]?\d+/,u("year")],YY:[n,function(g){this.year=l(g)}],YYYY:[/\d{4}/,u("year")],Z:h,ZZ:h};function m(g){var y,v;y=g,v=s&&s.formats;for(var x=(g=y.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g,function(A,L,M){var N=M&&M.toUpperCase();return L||v[M]||t[M]||v[N].replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g,function(k,I,C){return I||C.slice(1)})})).match(e),b=x.length,w=0;w-1)return new Date((D==="X"?1e3:1)*O);var B=m(D)(O),$=B.year,z=B.month,Y=B.day,Q=B.hours,X=B.minutes,ie=B.seconds,j=B.milliseconds,J=B.zone,Z=B.week,H=new Date,q=Y||($||z?1:H.getDate()),K=$||H.getFullYear(),se=0;$&&!z||(se=z>0?z-1:H.getMonth());var ce,ue=Q||0,te=X||0,De=ie||0,oe=j||0;return J?new Date(Date.UTC(K,se,q,ue,te,De,oe+60*J.offset*1e3)):P?new Date(Date.UTC(K,se,q,ue,te,De,oe)):(ce=new Date(K,se,q,ue,te,De,oe),Z&&(ce=F(ce).week(Z).toDate()),ce)}catch{return new Date("")}}(S,_,T,v),this.init(),N&&N!==!0&&(this.$L=this.locale(N).$L),M&&S!=this.format(_)&&(this.$d=new Date("")),s={}}else if(_ instanceof Array)for(var k=_.length,I=1;I<=k;I+=1){E[1]=_[I-1];var C=v.apply(this,E);if(C.isValid()){this.$d=C.$d,this.$L=C.$L,this.init();break}I===k&&(this.$d=new Date(""))}else b.call(this,w)}}})});var qle=gi((DI,RI)=>{"use strict";(function(t,e){typeof DI=="object"&&typeof RI<"u"?RI.exports=e():typeof define=="function"&&define.amd?define(e):(t=typeof globalThis<"u"?globalThis:t||self).dayjs_plugin_advancedFormat=e()})(DI,function(){"use strict";return function(t,e){var r=e.prototype,n=r.format;r.format=function(i){var a=this,s=this.$locale();if(!this.isValid())return n.bind(this)(i);var l=this.$utils(),u=(i||"YYYY-MM-DDTHH:mm:ssZ").replace(/\[([^\]]+)]|Q|wo|ww|w|WW|W|zzz|z|gggg|GGGG|Do|X|x|k{1,2}|S/g,function(h){switch(h){case"Q":return Math.ceil((a.$M+1)/3);case"Do":return s.ordinal(a.$D);case"gggg":return a.weekYear();case"GGGG":return a.isoWeekYear();case"wo":return s.ordinal(a.week(),"W");case"w":case"ww":return l.s(a.week(),h==="w"?1:2,"0");case"W":case"WW":return l.s(a.isoWeek(),h==="W"?1:2,"0");case"k":case"kk":return l.s(String(a.$H===0?24:a.$H),h==="k"?1:2,"0");case"X":return Math.floor(a.$d.getTime()/1e3);case"x":return a.$d.getTime();case"z":return"["+a.offsetName()+"]";case"zzz":return"["+a.offsetName("long")+"]";default:return h}});return n.bind(this)(u)}}})});function cce(t,e,r){let n=!0;for(;n;)n=!1,r.forEach(function(i){let a="^\\s*"+i+"\\s*$",s=new RegExp(a);t[0].match(s)&&(e[i]=!0,t.shift(1),n=!0)})}var Kle,yo,Qle,Zle,Jle,Xle,Pc,OI,PI,BI,cx,ux,FI,zI,aE,Rg,GI,ece,$I,hx,VI,UI,sE,NI,OBe,PBe,BBe,FBe,zBe,GBe,$Be,VBe,UBe,HBe,YBe,WBe,qBe,XBe,jBe,KBe,QBe,ZBe,JBe,eFe,tFe,rFe,nFe,tce,iFe,aFe,sFe,rce,oFe,MI,nce,ice,nE,Dg,lFe,cFe,II,iE,zi,ace,uFe,C0,hFe,jle,fFe,sce,dFe,oce,pFe,mFe,lce,uce=R(()=>{"use strict";Kle=Xi(Up(),1),yo=Xi(Nb(),1),Qle=Xi(Yle(),1),Zle=Xi(Wle(),1),Jle=Xi(qle(),1);ut();_t();xr();bi();yo.default.extend(Qle.default);yo.default.extend(Zle.default);yo.default.extend(Jle.default);Xle={friday:5,saturday:6},Pc="",OI="",BI="",cx=[],ux=[],FI=new Map,zI=[],aE=[],Rg="",GI="",ece=["active","done","crit","milestone"],$I=[],hx=!1,VI=!1,UI="sunday",sE="saturday",NI=0,OBe=o(function(){zI=[],aE=[],Rg="",$I=[],nE=0,II=void 0,iE=void 0,zi=[],Pc="",OI="",GI="",PI=void 0,BI="",cx=[],ux=[],hx=!1,VI=!1,NI=0,FI=new Map,vr(),UI="sunday",sE="saturday"},"clear"),PBe=o(function(t){OI=t},"setAxisFormat"),BBe=o(function(){return OI},"getAxisFormat"),FBe=o(function(t){PI=t},"setTickInterval"),zBe=o(function(){return PI},"getTickInterval"),GBe=o(function(t){BI=t},"setTodayMarker"),$Be=o(function(){return BI},"getTodayMarker"),VBe=o(function(t){Pc=t},"setDateFormat"),UBe=o(function(){hx=!0},"enableInclusiveEndDates"),HBe=o(function(){return hx},"endDatesAreInclusive"),YBe=o(function(){VI=!0},"enableTopAxis"),WBe=o(function(){return VI},"topAxisEnabled"),qBe=o(function(t){GI=t},"setDisplayMode"),XBe=o(function(){return GI},"getDisplayMode"),jBe=o(function(){return Pc},"getDateFormat"),KBe=o(function(t){cx=t.toLowerCase().split(/[\s,]+/)},"setIncludes"),QBe=o(function(){return cx},"getIncludes"),ZBe=o(function(t){ux=t.toLowerCase().split(/[\s,]+/)},"setExcludes"),JBe=o(function(){return ux},"getExcludes"),eFe=o(function(){return FI},"getLinks"),tFe=o(function(t){Rg=t,zI.push(t)},"addSection"),rFe=o(function(){return zI},"getSections"),nFe=o(function(){let t=jle(),e=10,r=0;for(;!t&&r[\d\w- ]+)/.exec(r);if(i!==null){let s=null;for(let u of i.groups.ids.split(" ")){let h=C0(u);h!==void 0&&(!s||h.endTime>s.endTime)&&(s=h)}if(s)return s.endTime;let l=new Date;return l.setHours(0,0,0,0),l}let a=(0,yo.default)(r,e.trim(),!0);if(a.isValid())return a.toDate();{V.debug("Invalid date:"+r),V.debug("With date format:"+e.trim());let s=new Date(r);if(s===void 0||isNaN(s.getTime())||s.getFullYear()<-1e4||s.getFullYear()>1e4)throw new Error("Invalid date:"+r);return s}},"getStartDate"),nce=o(function(t){let e=/^(\d+(?:\.\d+)?)([Mdhmswy]|ms)$/.exec(t.trim());return e!==null?[Number.parseFloat(e[1]),e[2]]:[NaN,"ms"]},"parseDuration"),ice=o(function(t,e,r,n=!1){r=r.trim();let a=/^until\s+(?[\d\w- ]+)/.exec(r);if(a!==null){let f=null;for(let p of a.groups.ids.split(" ")){let m=C0(p);m!==void 0&&(!f||m.startTime{window.open(r,"_self")}),FI.set(n,r))}),sce(t,"clickable")},"setLink"),sce=o(function(t,e){t.split(",").forEach(function(r){let n=C0(r);n!==void 0&&n.classes.push(e)})},"setClass"),dFe=o(function(t,e,r){if(de().securityLevel!=="loose"||e===void 0)return;let n=[];if(typeof r=="string"){n=r.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(let a=0;a{Lt.runFunc(e,...n)})},"setClickFun"),oce=o(function(t,e){$I.push(function(){let r=document.querySelector(`[id="${t}"]`);r!==null&&r.addEventListener("click",function(){e()})},function(){let r=document.querySelector(`[id="${t}-text"]`);r!==null&&r.addEventListener("click",function(){e()})})},"pushFun"),pFe=o(function(t,e,r){t.split(",").forEach(function(n){dFe(n,e,r)}),sce(t,"clickable")},"setClickEvent"),mFe=o(function(t){$I.forEach(function(e){e(t)})},"bindFunctions"),lce={getConfig:o(()=>de().gantt,"getConfig"),clear:OBe,setDateFormat:VBe,getDateFormat:jBe,enableInclusiveEndDates:UBe,endDatesAreInclusive:HBe,enableTopAxis:YBe,topAxisEnabled:WBe,setAxisFormat:PBe,getAxisFormat:BBe,setTickInterval:FBe,getTickInterval:zBe,setTodayMarker:GBe,getTodayMarker:$Be,setAccTitle:kr,getAccTitle:Ar,setDiagramTitle:nn,getDiagramTitle:Xr,setDisplayMode:qBe,getDisplayMode:XBe,setAccDescription:_r,getAccDescription:Lr,addSection:tFe,getSections:rFe,getTasks:nFe,addTask:uFe,findTaskById:C0,addTaskOrg:hFe,setIncludes:KBe,getIncludes:QBe,setExcludes:ZBe,getExcludes:JBe,setClickEvent:pFe,setLink:fFe,getLinks:eFe,bindFunctions:mFe,parseDuration:nce,isInvalidDate:tce,setWeekday:iFe,getWeekday:aFe,setWeekend:sFe};o(cce,"getTaskTags")});var oE,gFe,hce,yFe,Yu,vFe,fce,dce=R(()=>{"use strict";oE=Xi(Nb(),1);ut();Zt();rr();_t();Yn();gFe=o(function(){V.debug("Something is calling, setConf, remove the call")},"setConf"),hce={monday:_h,tuesday:k3,wednesday:E3,thursday:cc,friday:C3,saturday:S3,sunday:yl},yFe=o((t,e)=>{let r=[...t].map(()=>-1/0),n=[...t].sort((a,s)=>a.startTime-s.startTime||a.order-s.order),i=0;for(let a of n)for(let s=0;s=r[s]){r[s]=a.endTime,a.order=s+e,s>i&&(i=s);break}return i},"getMaxIntersections"),vFe=o(function(t,e,r,n){let i=de().gantt,a=de().securityLevel,s;a==="sandbox"&&(s=$e("#i"+e));let l=a==="sandbox"?$e(s.nodes()[0].contentDocument.body):$e("body"),u=a==="sandbox"?s.nodes()[0].contentDocument:document,h=u.getElementById(e);Yu=h.parentElement.offsetWidth,Yu===void 0&&(Yu=1200),i.useWidth!==void 0&&(Yu=i.useWidth);let f=n.db.getTasks(),d=[];for(let A of f)d.push(A.type);d=_(d);let p={},m=2*i.topPadding;if(n.db.getDisplayMode()==="compact"||i.displayMode==="compact"){let A={};for(let M of f)A[M.section]===void 0?A[M.section]=[M]:A[M.section].push(M);let L=0;for(let M of Object.keys(A)){let N=yFe(A[M],L)+1;L+=N,m+=N*(i.barHeight+i.barGap),p[M]=N}}else{m+=f.length*(i.barHeight+i.barGap);for(let A of d)p[A]=f.filter(L=>L.type===A).length}h.setAttribute("viewBox","0 0 "+Yu+" "+m);let g=l.select(`[id="${e}"]`),y=L3().domain([I4(f,function(A){return A.startTime}),M4(f,function(A){return A.endTime})]).rangeRound([0,Yu-i.leftPadding-i.rightPadding]);function v(A,L){let M=A.startTime,N=L.startTime,k=0;return M>N?k=1:M$.order))].map($=>A.find(z=>z.order===$));g.append("g").selectAll("rect").data(D).enter().append("rect").attr("x",0).attr("y",function($,z){return z=$.order,z*L+M-2}).attr("width",function(){return C-i.rightPadding/2}).attr("height",L).attr("class",function($){for(let[z,Y]of d.entries())if($.type===Y)return"section section"+z%i.numberSectionStyles;return"section section0"});let P=g.append("g").selectAll("rect").data(A).enter(),F=n.db.getLinks();if(P.append("rect").attr("id",function($){return $.id}).attr("rx",3).attr("ry",3).attr("x",function($){return $.milestone?y($.startTime)+N+.5*(y($.endTime)-y($.startTime))-.5*k:y($.startTime)+N}).attr("y",function($,z){return z=$.order,z*L+M}).attr("width",function($){return $.milestone?k:y($.renderEndTime||$.endTime)-y($.startTime)}).attr("height",k).attr("transform-origin",function($,z){return z=$.order,(y($.startTime)+N+.5*(y($.endTime)-y($.startTime))).toString()+"px "+(z*L+M+.5*k).toString()+"px"}).attr("class",function($){let z="task",Y="";$.classes.length>0&&(Y=$.classes.join(" "));let Q=0;for(let[ie,j]of d.entries())$.type===j&&(Q=ie%i.numberSectionStyles);let X="";return $.active?$.crit?X+=" activeCrit":X=" active":$.done?$.crit?X=" doneCrit":X=" done":$.crit&&(X+=" crit"),X.length===0&&(X=" task"),$.milestone&&(X=" milestone "+X),X+=Q,X+=" "+Y,z+X}),P.append("text").attr("id",function($){return $.id+"-text"}).text(function($){return $.task}).attr("font-size",i.fontSize).attr("x",function($){let z=y($.startTime),Y=y($.renderEndTime||$.endTime);$.milestone&&(z+=.5*(y($.endTime)-y($.startTime))-.5*k),$.milestone&&(Y=z+k);let Q=this.getBBox().width;return Q>Y-z?Y+Q+1.5*i.leftPadding>C?z+N-5:Y+N+5:(Y-z)/2+z+N}).attr("y",function($,z){return z=$.order,z*L+i.barHeight/2+(i.fontSize/2-2)+M}).attr("text-height",k).attr("class",function($){let z=y($.startTime),Y=y($.endTime);$.milestone&&(Y=z+k);let Q=this.getBBox().width,X="";$.classes.length>0&&(X=$.classes.join(" "));let ie=0;for(let[J,Z]of d.entries())$.type===Z&&(ie=J%i.numberSectionStyles);let j="";return $.active&&($.crit?j="activeCritText"+ie:j="activeText"+ie),$.done?$.crit?j=j+" doneCritText"+ie:j=j+" doneText"+ie:$.crit&&(j=j+" critText"+ie),$.milestone&&(j+=" milestoneText"),Q>Y-z?Y+Q+1.5*i.leftPadding>C?X+" taskTextOutsideLeft taskTextOutside"+ie+" "+j:X+" taskTextOutsideRight taskTextOutside"+ie+" "+j+" width-"+Q:X+" taskText taskText"+ie+" "+j+" width-"+Q}),de().securityLevel==="sandbox"){let $;$=$e("#i"+e);let z=$.nodes()[0].contentDocument;P.filter(function(Y){return F.has(Y.id)}).each(function(Y){var Q=z.querySelector("#"+Y.id),X=z.querySelector("#"+Y.id+"-text");let ie=Q.parentNode;var j=z.createElement("a");j.setAttribute("xlink:href",F.get(Y.id)),j.setAttribute("target","_top"),ie.appendChild(j),j.appendChild(Q),j.appendChild(X)})}}o(b,"drawRects");function w(A,L,M,N,k,I,C,O){if(C.length===0&&O.length===0)return;let D,P;for(let{startTime:Q,endTime:X}of I)(D===void 0||QP)&&(P=X);if(!D||!P)return;if((0,oE.default)(P).diff((0,oE.default)(D),"year")>5){V.warn("The difference between the min and max time is more than 5 years. This will cause performance issues. Skipping drawing exclude days.");return}let F=n.db.getDateFormat(),B=[],$=null,z=(0,oE.default)(D);for(;z.valueOf()<=P;)n.db.isInvalidDate(z,F,C,O)?$?$.end=z:$={start:z,end:z}:$&&(B.push($),$=null),z=z.add(1,"d");g.append("g").selectAll("rect").data(B).enter().append("rect").attr("id",function(Q){return"exclude-"+Q.start.format("YYYY-MM-DD")}).attr("x",function(Q){return y(Q.start)+M}).attr("y",i.gridLineStartPadding).attr("width",function(Q){let X=Q.end.add(1,"day");return y(X)-y(Q.start)}).attr("height",k-L-i.gridLineStartPadding).attr("transform-origin",function(Q,X){return(y(Q.start)+M+.5*(y(Q.end)-y(Q.start))).toString()+"px "+(X*A+.5*k).toString()+"px"}).attr("class","exclude-range")}o(w,"drawExcludeDays");function S(A,L,M,N){let k=vS(y).tickSize(-N+L+i.gridLineStartPadding).tickFormat(md(n.db.getAxisFormat()||i.axisFormat||"%Y-%m-%d")),C=/^([1-9]\d*)(millisecond|second|minute|hour|day|week|month)$/.exec(n.db.getTickInterval()||i.tickInterval);if(C!==null){let O=C[1],D=C[2],P=n.db.getWeekday()||i.weekday;switch(D){case"millisecond":k.ticks(oc.every(O));break;case"second":k.ticks(Ks.every(O));break;case"minute":k.ticks(gu.every(O));break;case"hour":k.ticks(yu.every(O));break;case"day":k.ticks(Do.every(O));break;case"week":k.ticks(hce[P].every(O));break;case"month":k.ticks(vu.every(O));break}}if(g.append("g").attr("class","grid").attr("transform","translate("+A+", "+(N-50)+")").call(k).selectAll("text").style("text-anchor","middle").attr("fill","#000").attr("stroke","none").attr("font-size",10).attr("dy","1em"),n.db.topAxisEnabled()||i.topAxis){let O=yS(y).tickSize(-N+L+i.gridLineStartPadding).tickFormat(md(n.db.getAxisFormat()||i.axisFormat||"%Y-%m-%d"));if(C!==null){let D=C[1],P=C[2],F=n.db.getWeekday()||i.weekday;switch(P){case"millisecond":O.ticks(oc.every(D));break;case"second":O.ticks(Ks.every(D));break;case"minute":O.ticks(gu.every(D));break;case"hour":O.ticks(yu.every(D));break;case"day":O.ticks(Do.every(D));break;case"week":O.ticks(hce[F].every(D));break;case"month":O.ticks(vu.every(D));break}}g.append("g").attr("class","grid").attr("transform","translate("+A+", "+L+")").call(O).selectAll("text").style("text-anchor","middle").attr("fill","#000").attr("stroke","none").attr("font-size",10)}}o(S,"makeGrid");function T(A,L){let M=0,N=Object.keys(p).map(k=>[k,p[k]]);g.append("g").selectAll("text").data(N).enter().append(function(k){let I=k[0].split(We.lineBreakRegex),C=-(I.length-1)/2,O=u.createElementNS("http://www.w3.org/2000/svg","text");O.setAttribute("dy",C+"em");for(let[D,P]of I.entries()){let F=u.createElementNS("http://www.w3.org/2000/svg","tspan");F.setAttribute("alignment-baseline","central"),F.setAttribute("x","10"),D>0&&F.setAttribute("dy","1em"),F.textContent=P,O.appendChild(F)}return O}).attr("x",10).attr("y",function(k,I){if(I>0)for(let C=0;C{"use strict";xFe=o(t=>` + .mermaid-main-font { + font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif); + } + + .exclude-range { + fill: ${t.excludeBkgColor}; + } + + .section { + stroke: none; + opacity: 0.2; + } + + .section0 { + fill: ${t.sectionBkgColor}; + } + + .section2 { + fill: ${t.sectionBkgColor2}; + } + + .section1, + .section3 { + fill: ${t.altSectionBkgColor}; + opacity: 0.2; + } + + .sectionTitle0 { + fill: ${t.titleColor}; + } + + .sectionTitle1 { + fill: ${t.titleColor}; + } + + .sectionTitle2 { + fill: ${t.titleColor}; + } + + .sectionTitle3 { + fill: ${t.titleColor}; + } + + .sectionTitle { + text-anchor: start; + font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif); + } + + + /* Grid and axis */ + + .grid .tick { + stroke: ${t.gridColor}; + opacity: 0.8; + shape-rendering: crispEdges; + } + + .grid .tick text { + font-family: ${t.fontFamily}; + fill: ${t.textColor}; + } + + .grid path { + stroke-width: 0; + } + + + /* Today line */ + + .today { + fill: none; + stroke: ${t.todayLineColor}; + stroke-width: 2px; + } + + + /* Task styling */ + + /* Default task */ + + .task { + stroke-width: 2; + } + + .taskText { + text-anchor: middle; + font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif); + } + + .taskTextOutsideRight { + fill: ${t.taskTextDarkColor}; + text-anchor: start; + font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif); + } + + .taskTextOutsideLeft { + fill: ${t.taskTextDarkColor}; + text-anchor: end; + } + + + /* Special case clickable */ + + .task.clickable { + cursor: pointer; + } + + .taskText.clickable { + cursor: pointer; + fill: ${t.taskTextClickableColor} !important; + font-weight: bold; + } + + .taskTextOutsideLeft.clickable { + cursor: pointer; + fill: ${t.taskTextClickableColor} !important; + font-weight: bold; + } + + .taskTextOutsideRight.clickable { + cursor: pointer; + fill: ${t.taskTextClickableColor} !important; + font-weight: bold; + } + + + /* Specific task settings for the sections*/ + + .taskText0, + .taskText1, + .taskText2, + .taskText3 { + fill: ${t.taskTextColor}; + } + + .task0, + .task1, + .task2, + .task3 { + fill: ${t.taskBkgColor}; + stroke: ${t.taskBorderColor}; + } + + .taskTextOutside0, + .taskTextOutside2 + { + fill: ${t.taskTextOutsideColor}; + } + + .taskTextOutside1, + .taskTextOutside3 { + fill: ${t.taskTextOutsideColor}; + } + + + /* Active task */ + + .active0, + .active1, + .active2, + .active3 { + fill: ${t.activeTaskBkgColor}; + stroke: ${t.activeTaskBorderColor}; + } + + .activeText0, + .activeText1, + .activeText2, + .activeText3 { + fill: ${t.taskTextDarkColor} !important; + } + + + /* Completed task */ + + .done0, + .done1, + .done2, + .done3 { + stroke: ${t.doneTaskBorderColor}; + fill: ${t.doneTaskBkgColor}; + stroke-width: 2; + } + + .doneText0, + .doneText1, + .doneText2, + .doneText3 { + fill: ${t.taskTextDarkColor} !important; + } + + + /* Tasks on the critical line */ + + .crit0, + .crit1, + .crit2, + .crit3 { + stroke: ${t.critBorderColor}; + fill: ${t.critBkgColor}; + stroke-width: 2; + } + + .activeCrit0, + .activeCrit1, + .activeCrit2, + .activeCrit3 { + stroke: ${t.critBorderColor}; + fill: ${t.activeTaskBkgColor}; + stroke-width: 2; + } + + .doneCrit0, + .doneCrit1, + .doneCrit2, + .doneCrit3 { + stroke: ${t.critBorderColor}; + fill: ${t.doneTaskBkgColor}; + stroke-width: 2; + cursor: pointer; + shape-rendering: crispEdges; + } + + .milestone { + transform: rotate(45deg) scale(0.8,0.8); + } + + .milestoneText { + font-style: italic; + } + .doneCritText0, + .doneCritText1, + .doneCritText2, + .doneCritText3 { + fill: ${t.taskTextDarkColor} !important; + } + + .activeCritText0, + .activeCritText1, + .activeCritText2, + .activeCritText3 { + fill: ${t.taskTextDarkColor} !important; + } + + .titleText { + text-anchor: middle; + font-size: 18px; + fill: ${t.titleColor||t.textColor}; + font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif); + } +`,"getStyles"),pce=xFe});var gce={};hr(gce,{diagram:()=>bFe});var bFe,yce=R(()=>{"use strict";Hle();uce();dce();mce();bFe={parser:Ule,db:lce,renderer:fce,styles:pce}});var bce,wce=R(()=>{"use strict";Lg();ut();bce={parse:o(async t=>{let e=await Fl("info",t);V.debug(e)},"parse")}});var fx,HI=R(()=>{fx="11.2.1"});var CFe,SFe,Tce,kce=R(()=>{"use strict";HI();CFe={version:fx},SFe=o(()=>CFe.version,"getVersion"),Tce={getVersion:SFe}});var Ps,pf=R(()=>{"use strict";Zt();_t();Ps=o(t=>{let{securityLevel:e}=de(),r=$e("body");if(e==="sandbox"){let a=$e(`#i${t}`).node()?.contentDocument??document;r=$e(a.body)}return r.select(`#${t}`)},"selectSvgElement")});var AFe,Ece,Cce=R(()=>{"use strict";ut();pf();Yn();AFe=o((t,e,r)=>{V.debug(`rendering info diagram +`+t);let n=Ps(e);Sr(n,100,400,!0),n.append("g").append("text").attr("x",100).attr("y",40).attr("class","version").attr("font-size",32).style("text-anchor","middle").text(`v${r}`)},"draw"),Ece={draw:AFe}});var Sce={};hr(Sce,{diagram:()=>_Fe});var _Fe,Ace=R(()=>{"use strict";wce();kce();Cce();_Fe={parser:bce,db:Tce,renderer:Ece}});var Dce,YI,lE,WI,RFe,NFe,MFe,IFe,OFe,PFe,BFe,cE,qI=R(()=>{"use strict";ut();bi();sl();Dce=mr.pie,YI={sections:new Map,showData:!1,config:Dce},lE=YI.sections,WI=YI.showData,RFe=structuredClone(Dce),NFe=o(()=>structuredClone(RFe),"getConfig"),MFe=o(()=>{lE=new Map,WI=YI.showData,vr()},"clear"),IFe=o(({label:t,value:e})=>{lE.has(t)||(lE.set(t,e),V.debug(`added new section: ${t}, with value: ${e}`))},"addSection"),OFe=o(()=>lE,"getSections"),PFe=o(t=>{WI=t},"setShowData"),BFe=o(()=>WI,"getShowData"),cE={getConfig:NFe,clear:MFe,setDiagramTitle:nn,getDiagramTitle:Xr,setAccTitle:kr,getAccTitle:Ar,setAccDescription:_r,getAccDescription:Lr,addSection:IFe,getSections:OFe,setShowData:PFe,getShowData:BFe}});var FFe,Rce,Nce=R(()=>{"use strict";Lg();ut();sx();qI();FFe=o((t,e)=>{cf(t,e),e.setShowData(t.showData),t.sections.map(e.addSection)},"populateDb"),Rce={parse:o(async t=>{let e=await Fl("pie",t);V.debug(e),FFe(e,cE)},"parse")}});var zFe,Mce,Ice=R(()=>{"use strict";zFe=o(t=>` + .pieCircle{ + stroke: ${t.pieStrokeColor}; + stroke-width : ${t.pieStrokeWidth}; + opacity : ${t.pieOpacity}; + } + .pieOuterCircle{ + stroke: ${t.pieOuterStrokeColor}; + stroke-width: ${t.pieOuterStrokeWidth}; + fill: none; + } + .pieTitleText { + text-anchor: middle; + font-size: ${t.pieTitleTextSize}; + fill: ${t.pieTitleTextColor}; + font-family: ${t.fontFamily}; + } + .slice { + font-family: ${t.fontFamily}; + fill: ${t.pieSectionTextColor}; + font-size:${t.pieSectionTextSize}; + // fill: white; + } + .legend text { + fill: ${t.pieLegendTextColor}; + font-family: ${t.fontFamily}; + font-size: ${t.pieLegendTextSize}; + } +`,"getStyles"),Mce=zFe});var GFe,$Fe,Oce,Pce=R(()=>{"use strict";Zt();_t();ut();pf();Yn();xr();GFe=o(t=>{let e=[...t.entries()].map(n=>({label:n[0],value:n[1]})).sort((n,i)=>i.value-n.value);return O3().value(n=>n.value)(e)},"createPieArcs"),$Fe=o((t,e,r,n)=>{V.debug(`rendering pie chart +`+t);let i=n.db,a=de(),s=Ts(i.getConfig(),a.pie),l=40,u=18,h=4,f=450,d=f,p=Ps(e),m=p.append("g");m.attr("transform","translate("+d/2+","+f/2+")");let{themeVariables:g}=a,[y]=mc(g.pieOuterStrokeWidth);y??=2;let v=s.textPosition,x=Math.min(d,f)/2-l,b=bl().innerRadius(0).outerRadius(x),w=bl().innerRadius(x*v).outerRadius(x*v);m.append("circle").attr("cx",0).attr("cy",0).attr("r",x+y/2).attr("class","pieOuterCircle");let S=i.getSections(),T=GFe(S),E=[g.pie1,g.pie2,g.pie3,g.pie4,g.pie5,g.pie6,g.pie7,g.pie8,g.pie9,g.pie10,g.pie11,g.pie12],_=pu(E);m.selectAll("mySlices").data(T).enter().append("path").attr("d",b).attr("fill",k=>_(k.data.label)).attr("class","pieCircle");let A=0;S.forEach(k=>{A+=k}),m.selectAll("mySlices").data(T).enter().append("text").text(k=>(k.data.value/A*100).toFixed(0)+"%").attr("transform",k=>"translate("+w.centroid(k)+")").style("text-anchor","middle").attr("class","slice"),m.append("text").text(i.getDiagramTitle()).attr("x",0).attr("y",-(f-50)/2).attr("class","pieTitleText");let L=m.selectAll(".legend").data(_.domain()).enter().append("g").attr("class","legend").attr("transform",(k,I)=>{let C=u+h,O=C*_.domain().length/2,D=12*u,P=I*C-O;return"translate("+D+","+P+")"});L.append("rect").attr("width",u).attr("height",u).style("fill",_).style("stroke",_),L.data(T).append("text").attr("x",u+h).attr("y",u-h).text(k=>{let{label:I,value:C}=k.data;return i.getShowData()?`${I} [${C}]`:I});let M=Math.max(...L.selectAll("text").nodes().map(k=>k?.getBoundingClientRect().width??0)),N=d+l+u+h+M;p.attr("viewBox",`0 0 ${N} ${f}`),Sr(p,f,N,s.useMaxWidth)},"draw"),Oce={draw:$Fe}});var Bce={};hr(Bce,{diagram:()=>VFe});var VFe,Fce=R(()=>{"use strict";Nce();qI();Ice();Pce();VFe={parser:Rce,db:cE,renderer:Oce,styles:Mce}});var XI,$ce,Vce=R(()=>{"use strict";XI=function(){var t=o(function(we,Te,Ce,Ae){for(Ce=Ce||{},Ae=we.length;Ae--;Ce[we[Ae]]=Te);return Ce},"o"),e=[1,3],r=[1,4],n=[1,5],i=[1,6],a=[1,7],s=[1,4,5,10,12,13,14,18,25,35,37,39,41,42,48,50,51,52,53,54,55,56,57,60,61,63,64,65,66,67],l=[1,4,5,10,12,13,14,18,25,28,35,37,39,41,42,48,50,51,52,53,54,55,56,57,60,61,63,64,65,66,67],u=[55,56,57],h=[2,36],f=[1,37],d=[1,36],p=[1,38],m=[1,35],g=[1,43],y=[1,41],v=[1,14],x=[1,23],b=[1,18],w=[1,19],S=[1,20],T=[1,21],E=[1,22],_=[1,24],A=[1,25],L=[1,26],M=[1,27],N=[1,28],k=[1,29],I=[1,32],C=[1,33],O=[1,34],D=[1,39],P=[1,40],F=[1,42],B=[1,44],$=[1,62],z=[1,61],Y=[4,5,8,10,12,13,14,18,44,47,49,55,56,57,63,64,65,66,67],Q=[1,65],X=[1,66],ie=[1,67],j=[1,68],J=[1,69],Z=[1,70],H=[1,71],q=[1,72],K=[1,73],se=[1,74],ce=[1,75],ue=[1,76],te=[4,5,6,7,8,9,10,11,12,13,14,15,18],De=[1,90],oe=[1,91],ke=[1,92],Ie=[1,99],Se=[1,93],Ue=[1,96],Pe=[1,94],_e=[1,95],me=[1,97],W=[1,98],fe=[1,102],ge=[10,55,56,57],re=[4,5,6,8,10,11,13,17,18,19,20,55,56,57],he={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,idStringToken:3,ALPHA:4,NUM:5,NODE_STRING:6,DOWN:7,MINUS:8,DEFAULT:9,COMMA:10,COLON:11,AMP:12,BRKT:13,MULT:14,UNICODE_TEXT:15,styleComponent:16,UNIT:17,SPACE:18,STYLE:19,PCT:20,idString:21,style:22,stylesOpt:23,classDefStatement:24,CLASSDEF:25,start:26,eol:27,QUADRANT:28,document:29,line:30,statement:31,axisDetails:32,quadrantDetails:33,points:34,title:35,title_value:36,acc_title:37,acc_title_value:38,acc_descr:39,acc_descr_value:40,acc_descr_multiline_value:41,section:42,text:43,point_start:44,point_x:45,point_y:46,class_name:47,"X-AXIS":48,"AXIS-TEXT-DELIMITER":49,"Y-AXIS":50,QUADRANT_1:51,QUADRANT_2:52,QUADRANT_3:53,QUADRANT_4:54,NEWLINE:55,SEMI:56,EOF:57,alphaNumToken:58,textNoTagsToken:59,STR:60,MD_STR:61,alphaNum:62,PUNCTUATION:63,PLUS:64,EQUALS:65,DOT:66,UNDERSCORE:67,$accept:0,$end:1},terminals_:{2:"error",4:"ALPHA",5:"NUM",6:"NODE_STRING",7:"DOWN",8:"MINUS",9:"DEFAULT",10:"COMMA",11:"COLON",12:"AMP",13:"BRKT",14:"MULT",15:"UNICODE_TEXT",17:"UNIT",18:"SPACE",19:"STYLE",20:"PCT",25:"CLASSDEF",28:"QUADRANT",35:"title",36:"title_value",37:"acc_title",38:"acc_title_value",39:"acc_descr",40:"acc_descr_value",41:"acc_descr_multiline_value",42:"section",44:"point_start",45:"point_x",46:"point_y",47:"class_name",48:"X-AXIS",49:"AXIS-TEXT-DELIMITER",50:"Y-AXIS",51:"QUADRANT_1",52:"QUADRANT_2",53:"QUADRANT_3",54:"QUADRANT_4",55:"NEWLINE",56:"SEMI",57:"EOF",60:"STR",61:"MD_STR",63:"PUNCTUATION",64:"PLUS",65:"EQUALS",66:"DOT",67:"UNDERSCORE"},productions_:[0,[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[16,1],[16,1],[16,1],[16,1],[16,1],[16,1],[16,1],[16,1],[16,1],[16,1],[21,1],[21,2],[22,1],[22,2],[23,1],[23,3],[24,5],[26,2],[26,2],[26,2],[29,0],[29,2],[30,2],[31,0],[31,1],[31,2],[31,1],[31,1],[31,1],[31,2],[31,2],[31,2],[31,1],[31,1],[34,4],[34,5],[34,5],[34,6],[32,4],[32,3],[32,2],[32,4],[32,3],[32,2],[33,2],[33,2],[33,2],[33,2],[27,1],[27,1],[27,1],[43,1],[43,2],[43,1],[43,1],[62,1],[62,2],[58,1],[58,1],[58,1],[58,1],[58,1],[58,1],[58,1],[58,1],[58,1],[58,1],[58,1],[59,1],[59,1],[59,1]],performAction:o(function(Te,Ce,Ae,Ge,Me,ye,He){var ze=ye.length-1;switch(Me){case 23:this.$=ye[ze];break;case 24:this.$=ye[ze-1]+""+ye[ze];break;case 26:this.$=ye[ze-1]+ye[ze];break;case 27:this.$=[ye[ze].trim()];break;case 28:ye[ze-2].push(ye[ze].trim()),this.$=ye[ze-2];break;case 29:this.$=ye[ze-4],Ge.addClass(ye[ze-2],ye[ze]);break;case 37:this.$=[];break;case 42:this.$=ye[ze].trim(),Ge.setDiagramTitle(this.$);break;case 43:this.$=ye[ze].trim(),Ge.setAccTitle(this.$);break;case 44:case 45:this.$=ye[ze].trim(),Ge.setAccDescription(this.$);break;case 46:Ge.addSection(ye[ze].substr(8)),this.$=ye[ze].substr(8);break;case 47:Ge.addPoint(ye[ze-3],"",ye[ze-1],ye[ze],[]);break;case 48:Ge.addPoint(ye[ze-4],ye[ze-3],ye[ze-1],ye[ze],[]);break;case 49:Ge.addPoint(ye[ze-4],"",ye[ze-2],ye[ze-1],ye[ze]);break;case 50:Ge.addPoint(ye[ze-5],ye[ze-4],ye[ze-2],ye[ze-1],ye[ze]);break;case 51:Ge.setXAxisLeftText(ye[ze-2]),Ge.setXAxisRightText(ye[ze]);break;case 52:ye[ze-1].text+=" \u27F6 ",Ge.setXAxisLeftText(ye[ze-1]);break;case 53:Ge.setXAxisLeftText(ye[ze]);break;case 54:Ge.setYAxisBottomText(ye[ze-2]),Ge.setYAxisTopText(ye[ze]);break;case 55:ye[ze-1].text+=" \u27F6 ",Ge.setYAxisBottomText(ye[ze-1]);break;case 56:Ge.setYAxisBottomText(ye[ze]);break;case 57:Ge.setQuadrant1Text(ye[ze]);break;case 58:Ge.setQuadrant2Text(ye[ze]);break;case 59:Ge.setQuadrant3Text(ye[ze]);break;case 60:Ge.setQuadrant4Text(ye[ze]);break;case 64:this.$={text:ye[ze],type:"text"};break;case 65:this.$={text:ye[ze-1].text+""+ye[ze],type:ye[ze-1].type};break;case 66:this.$={text:ye[ze],type:"text"};break;case 67:this.$={text:ye[ze],type:"markdown"};break;case 68:this.$=ye[ze];break;case 69:this.$=ye[ze-1]+""+ye[ze];break}},"anonymous"),table:[{18:e,26:1,27:2,28:r,55:n,56:i,57:a},{1:[3]},{18:e,26:8,27:2,28:r,55:n,56:i,57:a},{18:e,26:9,27:2,28:r,55:n,56:i,57:a},t(s,[2,33],{29:10}),t(l,[2,61]),t(l,[2,62]),t(l,[2,63]),{1:[2,30]},{1:[2,31]},t(u,h,{30:11,31:12,24:13,32:15,33:16,34:17,43:30,58:31,1:[2,32],4:f,5:d,10:p,12:m,13:g,14:y,18:v,25:x,35:b,37:w,39:S,41:T,42:E,48:_,50:A,51:L,52:M,53:N,54:k,60:I,61:C,63:O,64:D,65:P,66:F,67:B}),t(s,[2,34]),{27:45,55:n,56:i,57:a},t(u,[2,37]),t(u,h,{24:13,32:15,33:16,34:17,43:30,58:31,31:46,4:f,5:d,10:p,12:m,13:g,14:y,18:v,25:x,35:b,37:w,39:S,41:T,42:E,48:_,50:A,51:L,52:M,53:N,54:k,60:I,61:C,63:O,64:D,65:P,66:F,67:B}),t(u,[2,39]),t(u,[2,40]),t(u,[2,41]),{36:[1,47]},{38:[1,48]},{40:[1,49]},t(u,[2,45]),t(u,[2,46]),{18:[1,50]},{4:f,5:d,10:p,12:m,13:g,14:y,43:51,58:31,60:I,61:C,63:O,64:D,65:P,66:F,67:B},{4:f,5:d,10:p,12:m,13:g,14:y,43:52,58:31,60:I,61:C,63:O,64:D,65:P,66:F,67:B},{4:f,5:d,10:p,12:m,13:g,14:y,43:53,58:31,60:I,61:C,63:O,64:D,65:P,66:F,67:B},{4:f,5:d,10:p,12:m,13:g,14:y,43:54,58:31,60:I,61:C,63:O,64:D,65:P,66:F,67:B},{4:f,5:d,10:p,12:m,13:g,14:y,43:55,58:31,60:I,61:C,63:O,64:D,65:P,66:F,67:B},{4:f,5:d,10:p,12:m,13:g,14:y,43:56,58:31,60:I,61:C,63:O,64:D,65:P,66:F,67:B},{4:f,5:d,8:$,10:p,12:m,13:g,14:y,18:z,44:[1,57],47:[1,58],58:60,59:59,63:O,64:D,65:P,66:F,67:B},t(Y,[2,64]),t(Y,[2,66]),t(Y,[2,67]),t(Y,[2,70]),t(Y,[2,71]),t(Y,[2,72]),t(Y,[2,73]),t(Y,[2,74]),t(Y,[2,75]),t(Y,[2,76]),t(Y,[2,77]),t(Y,[2,78]),t(Y,[2,79]),t(Y,[2,80]),t(s,[2,35]),t(u,[2,38]),t(u,[2,42]),t(u,[2,43]),t(u,[2,44]),{3:64,4:Q,5:X,6:ie,7:j,8:J,9:Z,10:H,11:q,12:K,13:se,14:ce,15:ue,21:63},t(u,[2,53],{59:59,58:60,4:f,5:d,8:$,10:p,12:m,13:g,14:y,18:z,49:[1,77],63:O,64:D,65:P,66:F,67:B}),t(u,[2,56],{59:59,58:60,4:f,5:d,8:$,10:p,12:m,13:g,14:y,18:z,49:[1,78],63:O,64:D,65:P,66:F,67:B}),t(u,[2,57],{59:59,58:60,4:f,5:d,8:$,10:p,12:m,13:g,14:y,18:z,63:O,64:D,65:P,66:F,67:B}),t(u,[2,58],{59:59,58:60,4:f,5:d,8:$,10:p,12:m,13:g,14:y,18:z,63:O,64:D,65:P,66:F,67:B}),t(u,[2,59],{59:59,58:60,4:f,5:d,8:$,10:p,12:m,13:g,14:y,18:z,63:O,64:D,65:P,66:F,67:B}),t(u,[2,60],{59:59,58:60,4:f,5:d,8:$,10:p,12:m,13:g,14:y,18:z,63:O,64:D,65:P,66:F,67:B}),{45:[1,79]},{44:[1,80]},t(Y,[2,65]),t(Y,[2,81]),t(Y,[2,82]),t(Y,[2,83]),{3:82,4:Q,5:X,6:ie,7:j,8:J,9:Z,10:H,11:q,12:K,13:se,14:ce,15:ue,18:[1,81]},t(te,[2,23]),t(te,[2,1]),t(te,[2,2]),t(te,[2,3]),t(te,[2,4]),t(te,[2,5]),t(te,[2,6]),t(te,[2,7]),t(te,[2,8]),t(te,[2,9]),t(te,[2,10]),t(te,[2,11]),t(te,[2,12]),t(u,[2,52],{58:31,43:83,4:f,5:d,10:p,12:m,13:g,14:y,60:I,61:C,63:O,64:D,65:P,66:F,67:B}),t(u,[2,55],{58:31,43:84,4:f,5:d,10:p,12:m,13:g,14:y,60:I,61:C,63:O,64:D,65:P,66:F,67:B}),{46:[1,85]},{45:[1,86]},{4:De,5:oe,6:ke,8:Ie,11:Se,13:Ue,16:89,17:Pe,18:_e,19:me,20:W,22:88,23:87},t(te,[2,24]),t(u,[2,51],{59:59,58:60,4:f,5:d,8:$,10:p,12:m,13:g,14:y,18:z,63:O,64:D,65:P,66:F,67:B}),t(u,[2,54],{59:59,58:60,4:f,5:d,8:$,10:p,12:m,13:g,14:y,18:z,63:O,64:D,65:P,66:F,67:B}),t(u,[2,47],{22:88,16:89,23:100,4:De,5:oe,6:ke,8:Ie,11:Se,13:Ue,17:Pe,18:_e,19:me,20:W}),{46:[1,101]},t(u,[2,29],{10:fe}),t(ge,[2,27],{16:103,4:De,5:oe,6:ke,8:Ie,11:Se,13:Ue,17:Pe,18:_e,19:me,20:W}),t(re,[2,25]),t(re,[2,13]),t(re,[2,14]),t(re,[2,15]),t(re,[2,16]),t(re,[2,17]),t(re,[2,18]),t(re,[2,19]),t(re,[2,20]),t(re,[2,21]),t(re,[2,22]),t(u,[2,49],{10:fe}),t(u,[2,48],{22:88,16:89,23:104,4:De,5:oe,6:ke,8:Ie,11:Se,13:Ue,17:Pe,18:_e,19:me,20:W}),{4:De,5:oe,6:ke,8:Ie,11:Se,13:Ue,16:89,17:Pe,18:_e,19:me,20:W,22:105},t(re,[2,26]),t(u,[2,50],{10:fe}),t(ge,[2,28],{16:103,4:De,5:oe,6:ke,8:Ie,11:Se,13:Ue,17:Pe,18:_e,19:me,20:W})],defaultActions:{8:[2,30],9:[2,31]},parseError:o(function(Te,Ce){if(Ce.recoverable)this.trace(Te);else{var Ae=new Error(Te);throw Ae.hash=Ce,Ae}},"parseError"),parse:o(function(Te){var Ce=this,Ae=[0],Ge=[],Me=[null],ye=[],He=this.table,ze="",Ze=0,gt=0,yt=0,tt=2,Ye=1,Je=ye.slice.call(arguments,1),Ve=Object.create(this.lexer),je={yy:{}};for(var kt in this.yy)Object.prototype.hasOwnProperty.call(this.yy,kt)&&(je.yy[kt]=this.yy[kt]);Ve.setInput(Te,je.yy),je.yy.lexer=Ve,je.yy.parser=this,typeof Ve.yylloc>"u"&&(Ve.yylloc={});var at=Ve.yylloc;ye.push(at);var xt=Ve.options&&Ve.options.ranges;typeof je.yy.parseError=="function"?this.parseError=je.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function it(on){Ae.length=Ae.length-2*on,Me.length=Me.length-on,ye.length=ye.length-on}o(it,"popStack");function dt(){var on;return on=Ge.pop()||Ve.lex()||Ye,typeof on!="number"&&(on instanceof Array&&(Ge=on,on=Ge.pop()),on=Ce.symbols_[on]||on),on}o(dt,"lex");for(var lt,It,mt,St,gr,xn,jt={},rn,Er,Kn,hn;;){if(mt=Ae[Ae.length-1],this.defaultActions[mt]?St=this.defaultActions[mt]:((lt===null||typeof lt>"u")&&(lt=dt()),St=He[mt]&&He[mt][lt]),typeof St>"u"||!St.length||!St[0]){var Qn="";hn=[];for(rn in He[mt])this.terminals_[rn]&&rn>tt&&hn.push("'"+this.terminals_[rn]+"'");Ve.showPosition?Qn="Parse error on line "+(Ze+1)+`: +`+Ve.showPosition()+` +Expecting `+hn.join(", ")+", got '"+(this.terminals_[lt]||lt)+"'":Qn="Parse error on line "+(Ze+1)+": Unexpected "+(lt==Ye?"end of input":"'"+(this.terminals_[lt]||lt)+"'"),this.parseError(Qn,{text:Ve.match,token:this.terminals_[lt]||lt,line:Ve.yylineno,loc:at,expected:hn})}if(St[0]instanceof Array&&St.length>1)throw new Error("Parse Error: multiple actions possible at state: "+mt+", token: "+lt);switch(St[0]){case 1:Ae.push(lt),Me.push(Ve.yytext),ye.push(Ve.yylloc),Ae.push(St[1]),lt=null,It?(lt=It,It=null):(gt=Ve.yyleng,ze=Ve.yytext,Ze=Ve.yylineno,at=Ve.yylloc,yt>0&&yt--);break;case 2:if(Er=this.productions_[St[1]][1],jt.$=Me[Me.length-Er],jt._$={first_line:ye[ye.length-(Er||1)].first_line,last_line:ye[ye.length-1].last_line,first_column:ye[ye.length-(Er||1)].first_column,last_column:ye[ye.length-1].last_column},xt&&(jt._$.range=[ye[ye.length-(Er||1)].range[0],ye[ye.length-1].range[1]]),xn=this.performAction.apply(jt,[ze,gt,Ze,je.yy,St[1],Me,ye].concat(Je)),typeof xn<"u")return xn;Er&&(Ae=Ae.slice(0,-1*Er*2),Me=Me.slice(0,-1*Er),ye=ye.slice(0,-1*Er)),Ae.push(this.productions_[St[1]][0]),Me.push(jt.$),ye.push(jt._$),Kn=He[Ae[Ae.length-2]][Ae[Ae.length-1]],Ae.push(Kn);break;case 3:return!0}}return!0},"parse")},ne=function(){var we={EOF:1,parseError:o(function(Ce,Ae){if(this.yy.parser)this.yy.parser.parseError(Ce,Ae);else throw new Error(Ce)},"parseError"),setInput:o(function(Te,Ce){return this.yy=Ce||this.yy||{},this._input=Te,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var Te=this._input[0];this.yytext+=Te,this.yyleng++,this.offset++,this.match+=Te,this.matched+=Te;var Ce=Te.match(/(?:\r\n?|\n).*/g);return Ce?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),Te},"input"),unput:o(function(Te){var Ce=Te.length,Ae=Te.split(/(?:\r\n?|\n)/g);this._input=Te+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-Ce),this.offset-=Ce;var Ge=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),Ae.length-1&&(this.yylineno-=Ae.length-1);var Me=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:Ae?(Ae.length===Ge.length?this.yylloc.first_column:0)+Ge[Ge.length-Ae.length].length-Ae[0].length:this.yylloc.first_column-Ce},this.options.ranges&&(this.yylloc.range=[Me[0],Me[0]+this.yyleng-Ce]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(Te){this.unput(this.match.slice(Te))},"less"),pastInput:o(function(){var Te=this.matched.substr(0,this.matched.length-this.match.length);return(Te.length>20?"...":"")+Te.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var Te=this.match;return Te.length<20&&(Te+=this._input.substr(0,20-Te.length)),(Te.substr(0,20)+(Te.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var Te=this.pastInput(),Ce=new Array(Te.length+1).join("-");return Te+this.upcomingInput()+` +`+Ce+"^"},"showPosition"),test_match:o(function(Te,Ce){var Ae,Ge,Me;if(this.options.backtrack_lexer&&(Me={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(Me.yylloc.range=this.yylloc.range.slice(0))),Ge=Te[0].match(/(?:\r\n?|\n).*/g),Ge&&(this.yylineno+=Ge.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:Ge?Ge[Ge.length-1].length-Ge[Ge.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+Te[0].length},this.yytext+=Te[0],this.match+=Te[0],this.matches=Te,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(Te[0].length),this.matched+=Te[0],Ae=this.performAction.call(this,this.yy,this,Ce,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),Ae)return Ae;if(this._backtrack){for(var ye in Me)this[ye]=Me[ye];return!1}return!1},"test_match"),next:o(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var Te,Ce,Ae,Ge;this._more||(this.yytext="",this.match="");for(var Me=this._currentRules(),ye=0;yeCe[0].length)){if(Ce=Ae,Ge=ye,this.options.backtrack_lexer){if(Te=this.test_match(Ae,Me[ye]),Te!==!1)return Te;if(this._backtrack){Ce=!1;continue}else return!1}else if(!this.options.flex)break}return Ce?(Te=this.test_match(Ce,Me[Ge]),Te!==!1?Te:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:o(function(){var Ce=this.next();return Ce||this.lex()},"lex"),begin:o(function(Ce){this.conditionStack.push(Ce)},"begin"),popState:o(function(){var Ce=this.conditionStack.length-1;return Ce>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:o(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:o(function(Ce){return Ce=this.conditionStack.length-1-Math.abs(Ce||0),Ce>=0?this.conditionStack[Ce]:"INITIAL"},"topState"),pushState:o(function(Ce){this.begin(Ce)},"pushState"),stateStackSize:o(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:o(function(Ce,Ae,Ge,Me){var ye=Me;switch(Ge){case 0:break;case 1:break;case 2:return 55;case 3:break;case 4:return this.begin("title"),35;break;case 5:return this.popState(),"title_value";break;case 6:return this.begin("acc_title"),37;break;case 7:return this.popState(),"acc_title_value";break;case 8:return this.begin("acc_descr"),39;break;case 9:return this.popState(),"acc_descr_value";break;case 10:this.begin("acc_descr_multiline");break;case 11:this.popState();break;case 12:return"acc_descr_multiline_value";case 13:return 48;case 14:return 50;case 15:return 49;case 16:return 51;case 17:return 52;case 18:return 53;case 19:return 54;case 20:return 25;case 21:this.begin("md_string");break;case 22:return"MD_STR";case 23:this.popState();break;case 24:this.begin("string");break;case 25:this.popState();break;case 26:return"STR";case 27:this.begin("class_name");break;case 28:return this.popState(),47;break;case 29:return this.begin("point_start"),44;break;case 30:return this.begin("point_x"),45;break;case 31:this.popState();break;case 32:this.popState(),this.begin("point_y");break;case 33:return this.popState(),46;break;case 34:return 28;case 35:return 4;case 36:return 11;case 37:return 64;case 38:return 10;case 39:return 65;case 40:return 65;case 41:return 14;case 42:return 13;case 43:return 67;case 44:return 66;case 45:return 12;case 46:return 8;case 47:return 5;case 48:return 18;case 49:return 56;case 50:return 63;case 51:return 57}},"anonymous"),rules:[/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n\r]+)/i,/^(?:%%[^\n]*)/i,/^(?:title\b)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?: *x-axis *)/i,/^(?: *y-axis *)/i,/^(?: *--+> *)/i,/^(?: *quadrant-1 *)/i,/^(?: *quadrant-2 *)/i,/^(?: *quadrant-3 *)/i,/^(?: *quadrant-4 *)/i,/^(?:classDef\b)/i,/^(?:["][`])/i,/^(?:[^`"]+)/i,/^(?:[`]["])/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?::::)/i,/^(?:^\w+)/i,/^(?:\s*:\s*\[\s*)/i,/^(?:(1)|(0(.\d+)?))/i,/^(?:\s*\] *)/i,/^(?:\s*,\s*)/i,/^(?:(1)|(0(.\d+)?))/i,/^(?: *quadrantChart *)/i,/^(?:[A-Za-z]+)/i,/^(?::)/i,/^(?:\+)/i,/^(?:,)/i,/^(?:=)/i,/^(?:=)/i,/^(?:\*)/i,/^(?:#)/i,/^(?:[\_])/i,/^(?:\.)/i,/^(?:&)/i,/^(?:-)/i,/^(?:[0-9]+)/i,/^(?:\s)/i,/^(?:;)/i,/^(?:[!"#$%&'*+,-.`?\\_/])/i,/^(?:$)/i],conditions:{class_name:{rules:[28],inclusive:!1},point_y:{rules:[33],inclusive:!1},point_x:{rules:[32],inclusive:!1},point_start:{rules:[30,31],inclusive:!1},acc_descr_multiline:{rules:[11,12],inclusive:!1},acc_descr:{rules:[9],inclusive:!1},acc_title:{rules:[7],inclusive:!1},title:{rules:[5],inclusive:!1},md_string:{rules:[22,23],inclusive:!1},string:{rules:[25,26],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,6,8,10,13,14,15,16,17,18,19,20,21,24,27,29,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51],inclusive:!0}}};return we}();he.lexer=ne;function ae(){this.yy={}}return o(ae,"Parser"),ae.prototype=he,he.Parser=ae,new ae}();XI.parser=XI;$ce=XI});var os,uE,Uce=R(()=>{"use strict";Zt();sl();ut();jb();os=hp(),uE=class{constructor(){this.classes=new Map;this.config=this.getDefaultConfig(),this.themeConfig=this.getDefaultThemeConfig(),this.data=this.getDefaultData()}static{o(this,"QuadrantBuilder")}getDefaultData(){return{titleText:"",quadrant1Text:"",quadrant2Text:"",quadrant3Text:"",quadrant4Text:"",xAxisLeftText:"",xAxisRightText:"",yAxisBottomText:"",yAxisTopText:"",points:[]}}getDefaultConfig(){return{showXAxis:!0,showYAxis:!0,showTitle:!0,chartHeight:mr.quadrantChart?.chartWidth||500,chartWidth:mr.quadrantChart?.chartHeight||500,titlePadding:mr.quadrantChart?.titlePadding||10,titleFontSize:mr.quadrantChart?.titleFontSize||20,quadrantPadding:mr.quadrantChart?.quadrantPadding||5,xAxisLabelPadding:mr.quadrantChart?.xAxisLabelPadding||5,yAxisLabelPadding:mr.quadrantChart?.yAxisLabelPadding||5,xAxisLabelFontSize:mr.quadrantChart?.xAxisLabelFontSize||16,yAxisLabelFontSize:mr.quadrantChart?.yAxisLabelFontSize||16,quadrantLabelFontSize:mr.quadrantChart?.quadrantLabelFontSize||16,quadrantTextTopPadding:mr.quadrantChart?.quadrantTextTopPadding||5,pointTextPadding:mr.quadrantChart?.pointTextPadding||5,pointLabelFontSize:mr.quadrantChart?.pointLabelFontSize||12,pointRadius:mr.quadrantChart?.pointRadius||5,xAxisPosition:mr.quadrantChart?.xAxisPosition||"top",yAxisPosition:mr.quadrantChart?.yAxisPosition||"left",quadrantInternalBorderStrokeWidth:mr.quadrantChart?.quadrantInternalBorderStrokeWidth||1,quadrantExternalBorderStrokeWidth:mr.quadrantChart?.quadrantExternalBorderStrokeWidth||2}}getDefaultThemeConfig(){return{quadrant1Fill:os.quadrant1Fill,quadrant2Fill:os.quadrant2Fill,quadrant3Fill:os.quadrant3Fill,quadrant4Fill:os.quadrant4Fill,quadrant1TextFill:os.quadrant1TextFill,quadrant2TextFill:os.quadrant2TextFill,quadrant3TextFill:os.quadrant3TextFill,quadrant4TextFill:os.quadrant4TextFill,quadrantPointFill:os.quadrantPointFill,quadrantPointTextFill:os.quadrantPointTextFill,quadrantXAxisTextFill:os.quadrantXAxisTextFill,quadrantYAxisTextFill:os.quadrantYAxisTextFill,quadrantTitleFill:os.quadrantTitleFill,quadrantInternalBorderStrokeFill:os.quadrantInternalBorderStrokeFill,quadrantExternalBorderStrokeFill:os.quadrantExternalBorderStrokeFill}}clear(){this.config=this.getDefaultConfig(),this.themeConfig=this.getDefaultThemeConfig(),this.data=this.getDefaultData(),this.classes=new Map,V.info("clear called")}setData(e){this.data={...this.data,...e}}addPoints(e){this.data.points=[...e,...this.data.points]}addClass(e,r){this.classes.set(e,r)}setConfig(e){V.trace("setConfig called with: ",e),this.config={...this.config,...e}}setThemeConfig(e){V.trace("setThemeConfig called with: ",e),this.themeConfig={...this.themeConfig,...e}}calculateSpace(e,r,n,i){let a=this.config.xAxisLabelPadding*2+this.config.xAxisLabelFontSize,s={top:e==="top"&&r?a:0,bottom:e==="bottom"&&r?a:0},l=this.config.yAxisLabelPadding*2+this.config.yAxisLabelFontSize,u={left:this.config.yAxisPosition==="left"&&n?l:0,right:this.config.yAxisPosition==="right"&&n?l:0},h=this.config.titleFontSize+this.config.titlePadding*2,f={top:i?h:0},d=this.config.quadrantPadding+u.left,p=this.config.quadrantPadding+s.top+f.top,m=this.config.chartWidth-this.config.quadrantPadding*2-u.left-u.right,g=this.config.chartHeight-this.config.quadrantPadding*2-s.top-s.bottom-f.top,y=m/2,v=g/2;return{xAxisSpace:s,yAxisSpace:u,titleSpace:f,quadrantSpace:{quadrantLeft:d,quadrantTop:p,quadrantWidth:m,quadrantHalfWidth:y,quadrantHeight:g,quadrantHalfHeight:v}}}getAxisLabels(e,r,n,i){let{quadrantSpace:a,titleSpace:s}=i,{quadrantHalfHeight:l,quadrantHeight:u,quadrantLeft:h,quadrantHalfWidth:f,quadrantTop:d,quadrantWidth:p}=a,m=!!this.data.xAxisRightText,g=!!this.data.yAxisTopText,y=[];return this.data.xAxisLeftText&&r&&y.push({text:this.data.xAxisLeftText,fill:this.themeConfig.quadrantXAxisTextFill,x:h+(m?f/2:0),y:e==="top"?this.config.xAxisLabelPadding+s.top:this.config.xAxisLabelPadding+d+u+this.config.quadrantPadding,fontSize:this.config.xAxisLabelFontSize,verticalPos:m?"center":"left",horizontalPos:"top",rotation:0}),this.data.xAxisRightText&&r&&y.push({text:this.data.xAxisRightText,fill:this.themeConfig.quadrantXAxisTextFill,x:h+f+(m?f/2:0),y:e==="top"?this.config.xAxisLabelPadding+s.top:this.config.xAxisLabelPadding+d+u+this.config.quadrantPadding,fontSize:this.config.xAxisLabelFontSize,verticalPos:m?"center":"left",horizontalPos:"top",rotation:0}),this.data.yAxisBottomText&&n&&y.push({text:this.data.yAxisBottomText,fill:this.themeConfig.quadrantYAxisTextFill,x:this.config.yAxisPosition==="left"?this.config.yAxisLabelPadding:this.config.yAxisLabelPadding+h+p+this.config.quadrantPadding,y:d+u-(g?l/2:0),fontSize:this.config.yAxisLabelFontSize,verticalPos:g?"center":"left",horizontalPos:"top",rotation:-90}),this.data.yAxisTopText&&n&&y.push({text:this.data.yAxisTopText,fill:this.themeConfig.quadrantYAxisTextFill,x:this.config.yAxisPosition==="left"?this.config.yAxisLabelPadding:this.config.yAxisLabelPadding+h+p+this.config.quadrantPadding,y:d+l-(g?l/2:0),fontSize:this.config.yAxisLabelFontSize,verticalPos:g?"center":"left",horizontalPos:"top",rotation:-90}),y}getQuadrants(e){let{quadrantSpace:r}=e,{quadrantHalfHeight:n,quadrantLeft:i,quadrantHalfWidth:a,quadrantTop:s}=r,l=[{text:{text:this.data.quadrant1Text,fill:this.themeConfig.quadrant1TextFill,x:0,y:0,fontSize:this.config.quadrantLabelFontSize,verticalPos:"center",horizontalPos:"middle",rotation:0},x:i+a,y:s,width:a,height:n,fill:this.themeConfig.quadrant1Fill},{text:{text:this.data.quadrant2Text,fill:this.themeConfig.quadrant2TextFill,x:0,y:0,fontSize:this.config.quadrantLabelFontSize,verticalPos:"center",horizontalPos:"middle",rotation:0},x:i,y:s,width:a,height:n,fill:this.themeConfig.quadrant2Fill},{text:{text:this.data.quadrant3Text,fill:this.themeConfig.quadrant3TextFill,x:0,y:0,fontSize:this.config.quadrantLabelFontSize,verticalPos:"center",horizontalPos:"middle",rotation:0},x:i,y:s+n,width:a,height:n,fill:this.themeConfig.quadrant3Fill},{text:{text:this.data.quadrant4Text,fill:this.themeConfig.quadrant4TextFill,x:0,y:0,fontSize:this.config.quadrantLabelFontSize,verticalPos:"center",horizontalPos:"middle",rotation:0},x:i+a,y:s+n,width:a,height:n,fill:this.themeConfig.quadrant4Fill}];for(let u of l)u.text.x=u.x+u.width/2,this.data.points.length===0?(u.text.y=u.y+u.height/2,u.text.horizontalPos="middle"):(u.text.y=u.y+this.config.quadrantTextTopPadding,u.text.horizontalPos="top");return l}getQuadrantPoints(e){let{quadrantSpace:r}=e,{quadrantHeight:n,quadrantLeft:i,quadrantTop:a,quadrantWidth:s}=r,l=gl().domain([0,1]).range([i,s+i]),u=gl().domain([0,1]).range([n+a,a]);return this.data.points.map(f=>{let d=this.classes.get(f.className);return d&&(f={...d,...f}),{x:l(f.x),y:u(f.y),fill:f.color??this.themeConfig.quadrantPointFill,radius:f.radius??this.config.pointRadius,text:{text:f.text,fill:this.themeConfig.quadrantPointTextFill,x:l(f.x),y:u(f.y)+this.config.pointTextPadding,verticalPos:"center",horizontalPos:"top",fontSize:this.config.pointLabelFontSize,rotation:0},strokeColor:f.strokeColor??this.themeConfig.quadrantPointFill,strokeWidth:f.strokeWidth??"0px"}})}getBorders(e){let r=this.config.quadrantExternalBorderStrokeWidth/2,{quadrantSpace:n}=e,{quadrantHalfHeight:i,quadrantHeight:a,quadrantLeft:s,quadrantHalfWidth:l,quadrantTop:u,quadrantWidth:h}=n;return[{strokeFill:this.themeConfig.quadrantExternalBorderStrokeFill,strokeWidth:this.config.quadrantExternalBorderStrokeWidth,x1:s-r,y1:u,x2:s+h+r,y2:u},{strokeFill:this.themeConfig.quadrantExternalBorderStrokeFill,strokeWidth:this.config.quadrantExternalBorderStrokeWidth,x1:s+h,y1:u+r,x2:s+h,y2:u+a-r},{strokeFill:this.themeConfig.quadrantExternalBorderStrokeFill,strokeWidth:this.config.quadrantExternalBorderStrokeWidth,x1:s-r,y1:u+a,x2:s+h+r,y2:u+a},{strokeFill:this.themeConfig.quadrantExternalBorderStrokeFill,strokeWidth:this.config.quadrantExternalBorderStrokeWidth,x1:s,y1:u+r,x2:s,y2:u+a-r},{strokeFill:this.themeConfig.quadrantInternalBorderStrokeFill,strokeWidth:this.config.quadrantInternalBorderStrokeWidth,x1:s+l,y1:u+r,x2:s+l,y2:u+a-r},{strokeFill:this.themeConfig.quadrantInternalBorderStrokeFill,strokeWidth:this.config.quadrantInternalBorderStrokeWidth,x1:s+r,y1:u+i,x2:s+h-r,y2:u+i}]}getTitle(e){if(e)return{text:this.data.titleText,fill:this.themeConfig.quadrantTitleFill,fontSize:this.config.titleFontSize,horizontalPos:"top",verticalPos:"center",rotation:0,y:this.config.titlePadding,x:this.config.chartWidth/2}}build(){let e=this.config.showXAxis&&!!(this.data.xAxisLeftText||this.data.xAxisRightText),r=this.config.showYAxis&&!!(this.data.yAxisTopText||this.data.yAxisBottomText),n=this.config.showTitle&&!!this.data.titleText,i=this.data.points.length>0?"bottom":this.config.xAxisPosition,a=this.calculateSpace(i,e,r,n);return{points:this.getQuadrantPoints(a),quadrants:this.getQuadrants(a),axisLabels:this.getAxisLabels(i,e,r,a),borderLines:this.getBorders(a),title:this.getTitle(n)}}}});function jI(t){return!/^#?([\dA-Fa-f]{6}|[\dA-Fa-f]{3})$/.test(t)}function Hce(t){return!/^\d+$/.test(t)}function Yce(t){return!/^\d+px$/.test(t)}var S0,Wce=R(()=>{"use strict";S0=class extends Error{static{o(this,"InvalidStyleError")}constructor(e,r,n){super(`value for ${e} ${r} is invalid, please use a valid ${n}`),this.name="InvalidStyleError"}};o(jI,"validateHexCode");o(Hce,"validateNumber");o(Yce,"validateSizeInPixels")});function Wu(t){return qr(t.trim(),YFe)}function WFe(t){wa.setData({quadrant1Text:Wu(t.text)})}function qFe(t){wa.setData({quadrant2Text:Wu(t.text)})}function XFe(t){wa.setData({quadrant3Text:Wu(t.text)})}function jFe(t){wa.setData({quadrant4Text:Wu(t.text)})}function KFe(t){wa.setData({xAxisLeftText:Wu(t.text)})}function QFe(t){wa.setData({xAxisRightText:Wu(t.text)})}function ZFe(t){wa.setData({yAxisTopText:Wu(t.text)})}function JFe(t){wa.setData({yAxisBottomText:Wu(t.text)})}function KI(t){let e={};for(let r of t){let[n,i]=r.trim().split(/\s*:\s*/);if(n==="radius"){if(Hce(i))throw new S0(n,i,"number");e.radius=parseInt(i)}else if(n==="color"){if(jI(i))throw new S0(n,i,"hex code");e.color=i}else if(n==="stroke-color"){if(jI(i))throw new S0(n,i,"hex code");e.strokeColor=i}else if(n==="stroke-width"){if(Yce(i))throw new S0(n,i,"number of pixels (eg. 10px)");e.strokeWidth=i}else throw new Error(`style named ${n} is not supported.`)}return e}function eze(t,e,r,n,i){let a=KI(i);wa.addPoints([{x:r,y:n,text:Wu(t.text),className:e,...a}])}function tze(t,e){wa.addClass(t,KI(e))}function rze(t){wa.setConfig({chartWidth:t})}function nze(t){wa.setConfig({chartHeight:t})}function ize(){let t=de(),{themeVariables:e,quadrantChart:r}=t;return r&&wa.setConfig(r),wa.setThemeConfig({quadrant1Fill:e.quadrant1Fill,quadrant2Fill:e.quadrant2Fill,quadrant3Fill:e.quadrant3Fill,quadrant4Fill:e.quadrant4Fill,quadrant1TextFill:e.quadrant1TextFill,quadrant2TextFill:e.quadrant2TextFill,quadrant3TextFill:e.quadrant3TextFill,quadrant4TextFill:e.quadrant4TextFill,quadrantPointFill:e.quadrantPointFill,quadrantPointTextFill:e.quadrantPointTextFill,quadrantXAxisTextFill:e.quadrantXAxisTextFill,quadrantYAxisTextFill:e.quadrantYAxisTextFill,quadrantExternalBorderStrokeFill:e.quadrantExternalBorderStrokeFill,quadrantInternalBorderStrokeFill:e.quadrantInternalBorderStrokeFill,quadrantTitleFill:e.quadrantTitleFill}),wa.setData({titleText:Xr()}),wa.build()}var YFe,wa,aze,qce,Xce=R(()=>{"use strict";_t();rr();bi();Uce();Wce();YFe=de();o(Wu,"textSanitizer");wa=new uE;o(WFe,"setQuadrant1Text");o(qFe,"setQuadrant2Text");o(XFe,"setQuadrant3Text");o(jFe,"setQuadrant4Text");o(KFe,"setXAxisLeftText");o(QFe,"setXAxisRightText");o(ZFe,"setYAxisTopText");o(JFe,"setYAxisBottomText");o(KI,"parseStyles");o(eze,"addPoint");o(tze,"addClass");o(rze,"setWidth");o(nze,"setHeight");o(ize,"getQuadrantData");aze=o(function(){wa.clear(),vr()},"clear"),qce={setWidth:rze,setHeight:nze,setQuadrant1Text:WFe,setQuadrant2Text:qFe,setQuadrant3Text:XFe,setQuadrant4Text:jFe,setXAxisLeftText:KFe,setXAxisRightText:QFe,setYAxisTopText:ZFe,setYAxisBottomText:JFe,parseStyles:KI,addPoint:eze,addClass:tze,getQuadrantData:ize,clear:aze,setAccTitle:kr,getAccTitle:Ar,setDiagramTitle:nn,getDiagramTitle:Xr,getAccDescription:Lr,setAccDescription:_r}});var sze,jce,Kce=R(()=>{"use strict";Zt();_t();ut();Yn();sze=o((t,e,r,n)=>{function i(A){return A==="top"?"hanging":"middle"}o(i,"getDominantBaseLine");function a(A){return A==="left"?"start":"middle"}o(a,"getTextAnchor");function s(A){return`translate(${A.x}, ${A.y}) rotate(${A.rotation||0})`}o(s,"getTransformation");let l=de();V.debug(`Rendering quadrant chart +`+t);let u=l.securityLevel,h;u==="sandbox"&&(h=$e("#i"+e));let d=(u==="sandbox"?$e(h.nodes()[0].contentDocument.body):$e("body")).select(`[id="${e}"]`),p=d.append("g").attr("class","main"),m=l.quadrantChart?.chartWidth??500,g=l.quadrantChart?.chartHeight??500;Sr(d,g,m,l.quadrantChart?.useMaxWidth??!0),d.attr("viewBox","0 0 "+m+" "+g),n.db.setHeight(g),n.db.setWidth(m);let y=n.db.getQuadrantData(),v=p.append("g").attr("class","quadrants"),x=p.append("g").attr("class","border"),b=p.append("g").attr("class","data-points"),w=p.append("g").attr("class","labels"),S=p.append("g").attr("class","title");y.title&&S.append("text").attr("x",0).attr("y",0).attr("fill",y.title.fill).attr("font-size",y.title.fontSize).attr("dominant-baseline",i(y.title.horizontalPos)).attr("text-anchor",a(y.title.verticalPos)).attr("transform",s(y.title)).text(y.title.text),y.borderLines&&x.selectAll("line").data(y.borderLines).enter().append("line").attr("x1",A=>A.x1).attr("y1",A=>A.y1).attr("x2",A=>A.x2).attr("y2",A=>A.y2).style("stroke",A=>A.strokeFill).style("stroke-width",A=>A.strokeWidth);let T=v.selectAll("g.quadrant").data(y.quadrants).enter().append("g").attr("class","quadrant");T.append("rect").attr("x",A=>A.x).attr("y",A=>A.y).attr("width",A=>A.width).attr("height",A=>A.height).attr("fill",A=>A.fill),T.append("text").attr("x",0).attr("y",0).attr("fill",A=>A.text.fill).attr("font-size",A=>A.text.fontSize).attr("dominant-baseline",A=>i(A.text.horizontalPos)).attr("text-anchor",A=>a(A.text.verticalPos)).attr("transform",A=>s(A.text)).text(A=>A.text.text),w.selectAll("g.label").data(y.axisLabels).enter().append("g").attr("class","label").append("text").attr("x",0).attr("y",0).text(A=>A.text).attr("fill",A=>A.fill).attr("font-size",A=>A.fontSize).attr("dominant-baseline",A=>i(A.horizontalPos)).attr("text-anchor",A=>a(A.verticalPos)).attr("transform",A=>s(A));let _=b.selectAll("g.data-point").data(y.points).enter().append("g").attr("class","data-point");_.append("circle").attr("cx",A=>A.x).attr("cy",A=>A.y).attr("r",A=>A.radius).attr("fill",A=>A.fill).attr("stroke",A=>A.strokeColor).attr("stroke-width",A=>A.strokeWidth),_.append("text").attr("x",0).attr("y",0).text(A=>A.text.text).attr("fill",A=>A.text.fill).attr("font-size",A=>A.text.fontSize).attr("dominant-baseline",A=>i(A.text.horizontalPos)).attr("text-anchor",A=>a(A.text.verticalPos)).attr("transform",A=>s(A.text))},"draw"),jce={draw:sze}});var Qce={};hr(Qce,{diagram:()=>oze});var oze,Zce=R(()=>{"use strict";Vce();Xce();Kce();oze={parser:$ce,db:qce,renderer:jce,styles:o(()=>"","styles")}});var QI,tue,rue=R(()=>{"use strict";QI=function(){var t=o(function(O,D,P,F){for(P=P||{},F=O.length;F--;P[O[F]]=D);return P},"o"),e=[1,10,12,14,16,18,19,21,23],r=[2,6],n=[1,3],i=[1,5],a=[1,6],s=[1,7],l=[1,5,10,12,14,16,18,19,21,23,34,35,36],u=[1,25],h=[1,26],f=[1,28],d=[1,29],p=[1,30],m=[1,31],g=[1,32],y=[1,33],v=[1,34],x=[1,35],b=[1,36],w=[1,37],S=[1,43],T=[1,42],E=[1,47],_=[1,50],A=[1,10,12,14,16,18,19,21,23,34,35,36],L=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36],M=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36,41,42,43,44,45,46,47,48,49,50],N=[1,64],k={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,start:3,eol:4,XYCHART:5,chartConfig:6,document:7,CHART_ORIENTATION:8,statement:9,title:10,text:11,X_AXIS:12,parseXAxis:13,Y_AXIS:14,parseYAxis:15,LINE:16,plotData:17,BAR:18,acc_title:19,acc_title_value:20,acc_descr:21,acc_descr_value:22,acc_descr_multiline_value:23,SQUARE_BRACES_START:24,commaSeparatedNumbers:25,SQUARE_BRACES_END:26,NUMBER_WITH_DECIMAL:27,COMMA:28,xAxisData:29,bandData:30,ARROW_DELIMITER:31,commaSeparatedTexts:32,yAxisData:33,NEWLINE:34,SEMI:35,EOF:36,alphaNum:37,STR:38,MD_STR:39,alphaNumToken:40,AMP:41,NUM:42,ALPHA:43,PLUS:44,EQUALS:45,MULT:46,DOT:47,BRKT:48,MINUS:49,UNDERSCORE:50,$accept:0,$end:1},terminals_:{2:"error",5:"XYCHART",8:"CHART_ORIENTATION",10:"title",12:"X_AXIS",14:"Y_AXIS",16:"LINE",18:"BAR",19:"acc_title",20:"acc_title_value",21:"acc_descr",22:"acc_descr_value",23:"acc_descr_multiline_value",24:"SQUARE_BRACES_START",26:"SQUARE_BRACES_END",27:"NUMBER_WITH_DECIMAL",28:"COMMA",31:"ARROW_DELIMITER",34:"NEWLINE",35:"SEMI",36:"EOF",38:"STR",39:"MD_STR",41:"AMP",42:"NUM",43:"ALPHA",44:"PLUS",45:"EQUALS",46:"MULT",47:"DOT",48:"BRKT",49:"MINUS",50:"UNDERSCORE"},productions_:[0,[3,2],[3,3],[3,2],[3,1],[6,1],[7,0],[7,2],[9,2],[9,2],[9,2],[9,2],[9,2],[9,3],[9,2],[9,3],[9,2],[9,2],[9,1],[17,3],[25,3],[25,1],[13,1],[13,2],[13,1],[29,1],[29,3],[30,3],[32,3],[32,1],[15,1],[15,2],[15,1],[33,3],[4,1],[4,1],[4,1],[11,1],[11,1],[11,1],[37,1],[37,2],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1]],performAction:o(function(D,P,F,B,$,z,Y){var Q=z.length-1;switch($){case 5:B.setOrientation(z[Q]);break;case 9:B.setDiagramTitle(z[Q].text.trim());break;case 12:B.setLineData({text:"",type:"text"},z[Q]);break;case 13:B.setLineData(z[Q-1],z[Q]);break;case 14:B.setBarData({text:"",type:"text"},z[Q]);break;case 15:B.setBarData(z[Q-1],z[Q]);break;case 16:this.$=z[Q].trim(),B.setAccTitle(this.$);break;case 17:case 18:this.$=z[Q].trim(),B.setAccDescription(this.$);break;case 19:this.$=z[Q-1];break;case 20:this.$=[Number(z[Q-2]),...z[Q]];break;case 21:this.$=[Number(z[Q])];break;case 22:B.setXAxisTitle(z[Q]);break;case 23:B.setXAxisTitle(z[Q-1]);break;case 24:B.setXAxisTitle({type:"text",text:""});break;case 25:B.setXAxisBand(z[Q]);break;case 26:B.setXAxisRangeData(Number(z[Q-2]),Number(z[Q]));break;case 27:this.$=z[Q-1];break;case 28:this.$=[z[Q-2],...z[Q]];break;case 29:this.$=[z[Q]];break;case 30:B.setYAxisTitle(z[Q]);break;case 31:B.setYAxisTitle(z[Q-1]);break;case 32:B.setYAxisTitle({type:"text",text:""});break;case 33:B.setYAxisRangeData(Number(z[Q-2]),Number(z[Q]));break;case 37:this.$={text:z[Q],type:"text"};break;case 38:this.$={text:z[Q],type:"text"};break;case 39:this.$={text:z[Q],type:"markdown"};break;case 40:this.$=z[Q];break;case 41:this.$=z[Q-1]+""+z[Q];break}},"anonymous"),table:[t(e,r,{3:1,4:2,7:4,5:n,34:i,35:a,36:s}),{1:[3]},t(e,r,{4:2,7:4,3:8,5:n,34:i,35:a,36:s}),t(e,r,{4:2,7:4,6:9,3:10,5:n,8:[1,11],34:i,35:a,36:s}),{1:[2,4],9:12,10:[1,13],12:[1,14],14:[1,15],16:[1,16],18:[1,17],19:[1,18],21:[1,19],23:[1,20]},t(l,[2,34]),t(l,[2,35]),t(l,[2,36]),{1:[2,1]},t(e,r,{4:2,7:4,3:21,5:n,34:i,35:a,36:s}),{1:[2,3]},t(l,[2,5]),t(e,[2,7],{4:22,34:i,35:a,36:s}),{11:23,37:24,38:u,39:h,40:27,41:f,42:d,43:p,44:m,45:g,46:y,47:v,48:x,49:b,50:w},{11:39,13:38,24:S,27:T,29:40,30:41,37:24,38:u,39:h,40:27,41:f,42:d,43:p,44:m,45:g,46:y,47:v,48:x,49:b,50:w},{11:45,15:44,27:E,33:46,37:24,38:u,39:h,40:27,41:f,42:d,43:p,44:m,45:g,46:y,47:v,48:x,49:b,50:w},{11:49,17:48,24:_,37:24,38:u,39:h,40:27,41:f,42:d,43:p,44:m,45:g,46:y,47:v,48:x,49:b,50:w},{11:52,17:51,24:_,37:24,38:u,39:h,40:27,41:f,42:d,43:p,44:m,45:g,46:y,47:v,48:x,49:b,50:w},{20:[1,53]},{22:[1,54]},t(A,[2,18]),{1:[2,2]},t(A,[2,8]),t(A,[2,9]),t(L,[2,37],{40:55,41:f,42:d,43:p,44:m,45:g,46:y,47:v,48:x,49:b,50:w}),t(L,[2,38]),t(L,[2,39]),t(M,[2,40]),t(M,[2,42]),t(M,[2,43]),t(M,[2,44]),t(M,[2,45]),t(M,[2,46]),t(M,[2,47]),t(M,[2,48]),t(M,[2,49]),t(M,[2,50]),t(M,[2,51]),t(A,[2,10]),t(A,[2,22],{30:41,29:56,24:S,27:T}),t(A,[2,24]),t(A,[2,25]),{31:[1,57]},{11:59,32:58,37:24,38:u,39:h,40:27,41:f,42:d,43:p,44:m,45:g,46:y,47:v,48:x,49:b,50:w},t(A,[2,11]),t(A,[2,30],{33:60,27:E}),t(A,[2,32]),{31:[1,61]},t(A,[2,12]),{17:62,24:_},{25:63,27:N},t(A,[2,14]),{17:65,24:_},t(A,[2,16]),t(A,[2,17]),t(M,[2,41]),t(A,[2,23]),{27:[1,66]},{26:[1,67]},{26:[2,29],28:[1,68]},t(A,[2,31]),{27:[1,69]},t(A,[2,13]),{26:[1,70]},{26:[2,21],28:[1,71]},t(A,[2,15]),t(A,[2,26]),t(A,[2,27]),{11:59,32:72,37:24,38:u,39:h,40:27,41:f,42:d,43:p,44:m,45:g,46:y,47:v,48:x,49:b,50:w},t(A,[2,33]),t(A,[2,19]),{25:73,27:N},{26:[2,28]},{26:[2,20]}],defaultActions:{8:[2,1],10:[2,3],21:[2,2],72:[2,28],73:[2,20]},parseError:o(function(D,P){if(P.recoverable)this.trace(D);else{var F=new Error(D);throw F.hash=P,F}},"parseError"),parse:o(function(D){var P=this,F=[0],B=[],$=[null],z=[],Y=this.table,Q="",X=0,ie=0,j=0,J=2,Z=1,H=z.slice.call(arguments,1),q=Object.create(this.lexer),K={yy:{}};for(var se in this.yy)Object.prototype.hasOwnProperty.call(this.yy,se)&&(K.yy[se]=this.yy[se]);q.setInput(D,K.yy),K.yy.lexer=q,K.yy.parser=this,typeof q.yylloc>"u"&&(q.yylloc={});var ce=q.yylloc;z.push(ce);var ue=q.options&&q.options.ranges;typeof K.yy.parseError=="function"?this.parseError=K.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function te(he){F.length=F.length-2*he,$.length=$.length-he,z.length=z.length-he}o(te,"popStack");function De(){var he;return he=B.pop()||q.lex()||Z,typeof he!="number"&&(he instanceof Array&&(B=he,he=B.pop()),he=P.symbols_[he]||he),he}o(De,"lex");for(var oe,ke,Ie,Se,Ue,Pe,_e={},me,W,fe,ge;;){if(Ie=F[F.length-1],this.defaultActions[Ie]?Se=this.defaultActions[Ie]:((oe===null||typeof oe>"u")&&(oe=De()),Se=Y[Ie]&&Y[Ie][oe]),typeof Se>"u"||!Se.length||!Se[0]){var re="";ge=[];for(me in Y[Ie])this.terminals_[me]&&me>J&&ge.push("'"+this.terminals_[me]+"'");q.showPosition?re="Parse error on line "+(X+1)+`: +`+q.showPosition()+` +Expecting `+ge.join(", ")+", got '"+(this.terminals_[oe]||oe)+"'":re="Parse error on line "+(X+1)+": Unexpected "+(oe==Z?"end of input":"'"+(this.terminals_[oe]||oe)+"'"),this.parseError(re,{text:q.match,token:this.terminals_[oe]||oe,line:q.yylineno,loc:ce,expected:ge})}if(Se[0]instanceof Array&&Se.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Ie+", token: "+oe);switch(Se[0]){case 1:F.push(oe),$.push(q.yytext),z.push(q.yylloc),F.push(Se[1]),oe=null,ke?(oe=ke,ke=null):(ie=q.yyleng,Q=q.yytext,X=q.yylineno,ce=q.yylloc,j>0&&j--);break;case 2:if(W=this.productions_[Se[1]][1],_e.$=$[$.length-W],_e._$={first_line:z[z.length-(W||1)].first_line,last_line:z[z.length-1].last_line,first_column:z[z.length-(W||1)].first_column,last_column:z[z.length-1].last_column},ue&&(_e._$.range=[z[z.length-(W||1)].range[0],z[z.length-1].range[1]]),Pe=this.performAction.apply(_e,[Q,ie,X,K.yy,Se[1],$,z].concat(H)),typeof Pe<"u")return Pe;W&&(F=F.slice(0,-1*W*2),$=$.slice(0,-1*W),z=z.slice(0,-1*W)),F.push(this.productions_[Se[1]][0]),$.push(_e.$),z.push(_e._$),fe=Y[F[F.length-2]][F[F.length-1]],F.push(fe);break;case 3:return!0}}return!0},"parse")},I=function(){var O={EOF:1,parseError:o(function(P,F){if(this.yy.parser)this.yy.parser.parseError(P,F);else throw new Error(P)},"parseError"),setInput:o(function(D,P){return this.yy=P||this.yy||{},this._input=D,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var D=this._input[0];this.yytext+=D,this.yyleng++,this.offset++,this.match+=D,this.matched+=D;var P=D.match(/(?:\r\n?|\n).*/g);return P?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),D},"input"),unput:o(function(D){var P=D.length,F=D.split(/(?:\r\n?|\n)/g);this._input=D+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-P),this.offset-=P;var B=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),F.length-1&&(this.yylineno-=F.length-1);var $=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:F?(F.length===B.length?this.yylloc.first_column:0)+B[B.length-F.length].length-F[0].length:this.yylloc.first_column-P},this.options.ranges&&(this.yylloc.range=[$[0],$[0]+this.yyleng-P]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(D){this.unput(this.match.slice(D))},"less"),pastInput:o(function(){var D=this.matched.substr(0,this.matched.length-this.match.length);return(D.length>20?"...":"")+D.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var D=this.match;return D.length<20&&(D+=this._input.substr(0,20-D.length)),(D.substr(0,20)+(D.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var D=this.pastInput(),P=new Array(D.length+1).join("-");return D+this.upcomingInput()+` +`+P+"^"},"showPosition"),test_match:o(function(D,P){var F,B,$;if(this.options.backtrack_lexer&&($={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&($.yylloc.range=this.yylloc.range.slice(0))),B=D[0].match(/(?:\r\n?|\n).*/g),B&&(this.yylineno+=B.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:B?B[B.length-1].length-B[B.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+D[0].length},this.yytext+=D[0],this.match+=D[0],this.matches=D,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(D[0].length),this.matched+=D[0],F=this.performAction.call(this,this.yy,this,P,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),F)return F;if(this._backtrack){for(var z in $)this[z]=$[z];return!1}return!1},"test_match"),next:o(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var D,P,F,B;this._more||(this.yytext="",this.match="");for(var $=this._currentRules(),z=0;z<$.length;z++)if(F=this._input.match(this.rules[$[z]]),F&&(!P||F[0].length>P[0].length)){if(P=F,B=z,this.options.backtrack_lexer){if(D=this.test_match(F,$[z]),D!==!1)return D;if(this._backtrack){P=!1;continue}else return!1}else if(!this.options.flex)break}return P?(D=this.test_match(P,$[B]),D!==!1?D:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:o(function(){var P=this.next();return P||this.lex()},"lex"),begin:o(function(P){this.conditionStack.push(P)},"begin"),popState:o(function(){var P=this.conditionStack.length-1;return P>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:o(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:o(function(P){return P=this.conditionStack.length-1-Math.abs(P||0),P>=0?this.conditionStack[P]:"INITIAL"},"topState"),pushState:o(function(P){this.begin(P)},"pushState"),stateStackSize:o(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:o(function(P,F,B,$){var z=$;switch(B){case 0:break;case 1:break;case 2:return this.popState(),34;break;case 3:return this.popState(),34;break;case 4:return 34;case 5:break;case 6:return 10;case 7:return this.pushState("acc_title"),19;break;case 8:return this.popState(),"acc_title_value";break;case 9:return this.pushState("acc_descr"),21;break;case 10:return this.popState(),"acc_descr_value";break;case 11:this.pushState("acc_descr_multiline");break;case 12:this.popState();break;case 13:return"acc_descr_multiline_value";case 14:return 5;case 15:return 8;case 16:return this.pushState("axis_data"),"X_AXIS";break;case 17:return this.pushState("axis_data"),"Y_AXIS";break;case 18:return this.pushState("axis_band_data"),24;break;case 19:return 31;case 20:return this.pushState("data"),16;break;case 21:return this.pushState("data"),18;break;case 22:return this.pushState("data_inner"),24;break;case 23:return 27;case 24:return this.popState(),26;break;case 25:this.popState();break;case 26:this.pushState("string");break;case 27:this.popState();break;case 28:return"STR";case 29:return 24;case 30:return 26;case 31:return 43;case 32:return"COLON";case 33:return 44;case 34:return 28;case 35:return 45;case 36:return 46;case 37:return 48;case 38:return 50;case 39:return 47;case 40:return 41;case 41:return 49;case 42:return 42;case 43:break;case 44:return 35;case 45:return 36}},"anonymous"),rules:[/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:(\r?\n))/i,/^(?:(\r?\n))/i,/^(?:[\n\r]+)/i,/^(?:%%[^\n]*)/i,/^(?:title\b)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:\{)/i,/^(?:[^\}]*)/i,/^(?:xychart-beta\b)/i,/^(?:(?:vertical|horizontal))/i,/^(?:x-axis\b)/i,/^(?:y-axis\b)/i,/^(?:\[)/i,/^(?:-->)/i,/^(?:line\b)/i,/^(?:bar\b)/i,/^(?:\[)/i,/^(?:[+-]?(?:\d+(?:\.\d+)?|\.\d+))/i,/^(?:\])/i,/^(?:(?:`\) \{ this\.pushState\(md_string\); \}\n\(\?:\(\?!`"\)\.\)\+ \{ return MD_STR; \}\n\(\?:`))/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:\[)/i,/^(?:\])/i,/^(?:[A-Za-z]+)/i,/^(?::)/i,/^(?:\+)/i,/^(?:,)/i,/^(?:=)/i,/^(?:\*)/i,/^(?:#)/i,/^(?:[\_])/i,/^(?:\.)/i,/^(?:&)/i,/^(?:-)/i,/^(?:[0-9]+)/i,/^(?:\s+)/i,/^(?:;)/i,/^(?:$)/i],conditions:{data_inner:{rules:[0,1,4,5,6,7,9,11,14,15,16,17,20,21,23,24,25,26,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45],inclusive:!0},data:{rules:[0,1,3,4,5,6,7,9,11,14,15,16,17,20,21,22,25,26,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45],inclusive:!0},axis_band_data:{rules:[0,1,4,5,6,7,9,11,14,15,16,17,20,21,24,25,26,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45],inclusive:!0},axis_data:{rules:[0,1,2,4,5,6,7,9,11,14,15,16,17,18,19,20,21,23,25,26,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45],inclusive:!0},acc_descr_multiline:{rules:[12,13],inclusive:!1},acc_descr:{rules:[10],inclusive:!1},acc_title:{rules:[8],inclusive:!1},title:{rules:[],inclusive:!1},md_string:{rules:[],inclusive:!1},string:{rules:[27,28],inclusive:!1},INITIAL:{rules:[0,1,4,5,6,7,9,11,14,15,16,17,20,21,25,26,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45],inclusive:!0}}};return O}();k.lexer=I;function C(){this.yy={}}return o(C,"Parser"),C.prototype=k,k.Parser=C,new C}();QI.parser=QI;tue=QI});function ZI(t){return t.type==="bar"}function hE(t){return t.type==="band"}function Ng(t){return t.type==="linear"}var fE=R(()=>{"use strict";o(ZI,"isBarPlot");o(hE,"isBandAxisData");o(Ng,"isLinearAxisData")});var Mg,JI=R(()=>{"use strict";Al();Mg=class{constructor(e){this.parentGroup=e}static{o(this,"TextDimensionCalculatorWithFont")}getMaxDimension(e,r){if(!this.parentGroup)return{width:e.reduce((a,s)=>Math.max(s.length,a),0)*r,height:r};let n={width:0,height:0},i=this.parentGroup.append("g").attr("visibility","hidden").attr("font-size",r);for(let a of e){let s=bj(i,1,a),l=s?s.width:a.length*r,u=s?s.height:r;n.width=Math.max(n.width,l),n.height=Math.max(n.height,u)}return i.remove(),n}}});var Ig,eO=R(()=>{"use strict";Ig=class{constructor(e,r,n,i){this.axisConfig=e;this.title=r;this.textDimensionCalculator=n;this.axisThemeConfig=i;this.boundingRect={x:0,y:0,width:0,height:0};this.axisPosition="left";this.showTitle=!1;this.showLabel=!1;this.showTick=!1;this.showAxisLine=!1;this.outerPadding=0;this.titleTextHeight=0;this.labelTextHeight=0;this.range=[0,10],this.boundingRect={x:0,y:0,width:0,height:0},this.axisPosition="left"}static{o(this,"BaseAxis")}setRange(e){this.range=e,this.axisPosition==="left"||this.axisPosition==="right"?this.boundingRect.height=e[1]-e[0]:this.boundingRect.width=e[1]-e[0],this.recalculateScale()}getRange(){return[this.range[0]+this.outerPadding,this.range[1]-this.outerPadding]}setAxisPosition(e){this.axisPosition=e,this.setRange(this.range)}getTickDistance(){let e=this.getRange();return Math.abs(e[0]-e[1])/this.getTickValues().length}getAxisOuterPadding(){return this.outerPadding}getLabelDimension(){return this.textDimensionCalculator.getMaxDimension(this.getTickValues().map(e=>e.toString()),this.axisConfig.labelFontSize)}recalculateOuterPaddingToDrawBar(){.7*this.getTickDistance()>this.outerPadding*2&&(this.outerPadding=Math.floor(.7*this.getTickDistance()/2)),this.recalculateScale()}calculateSpaceIfDrawnHorizontally(e){let r=e.height;if(this.axisConfig.showAxisLine&&r>this.axisConfig.axisLineWidth&&(r-=this.axisConfig.axisLineWidth,this.showAxisLine=!0),this.axisConfig.showLabel){let n=this.getLabelDimension(),i=.2*e.width;this.outerPadding=Math.min(n.width/2,i);let a=n.height+this.axisConfig.labelPadding*2;this.labelTextHeight=n.height,a<=r&&(r-=a,this.showLabel=!0)}if(this.axisConfig.showTick&&r>=this.axisConfig.tickLength&&(this.showTick=!0,r-=this.axisConfig.tickLength),this.axisConfig.showTitle&&this.title){let n=this.textDimensionCalculator.getMaxDimension([this.title],this.axisConfig.titleFontSize),i=n.height+this.axisConfig.titlePadding*2;this.titleTextHeight=n.height,i<=r&&(r-=i,this.showTitle=!0)}this.boundingRect.width=e.width,this.boundingRect.height=e.height-r}calculateSpaceIfDrawnVertical(e){let r=e.width;if(this.axisConfig.showAxisLine&&r>this.axisConfig.axisLineWidth&&(r-=this.axisConfig.axisLineWidth,this.showAxisLine=!0),this.axisConfig.showLabel){let n=this.getLabelDimension(),i=.2*e.height;this.outerPadding=Math.min(n.height/2,i);let a=n.width+this.axisConfig.labelPadding*2;a<=r&&(r-=a,this.showLabel=!0)}if(this.axisConfig.showTick&&r>=this.axisConfig.tickLength&&(this.showTick=!0,r-=this.axisConfig.tickLength),this.axisConfig.showTitle&&this.title){let n=this.textDimensionCalculator.getMaxDimension([this.title],this.axisConfig.titleFontSize),i=n.height+this.axisConfig.titlePadding*2;this.titleTextHeight=n.height,i<=r&&(r-=i,this.showTitle=!0)}this.boundingRect.width=e.width-r,this.boundingRect.height=e.height}calculateSpace(e){return this.axisPosition==="left"||this.axisPosition==="right"?this.calculateSpaceIfDrawnVertical(e):this.calculateSpaceIfDrawnHorizontally(e),this.recalculateScale(),{width:this.boundingRect.width,height:this.boundingRect.height}}setBoundingBoxXY(e){this.boundingRect.x=e.x,this.boundingRect.y=e.y}getDrawableElementsForLeftAxis(){let e=[];if(this.showAxisLine){let r=this.boundingRect.x+this.boundingRect.width-this.axisConfig.axisLineWidth/2;e.push({type:"path",groupTexts:["left-axis","axisl-line"],data:[{path:`M ${r},${this.boundingRect.y} L ${r},${this.boundingRect.y+this.boundingRect.height} `,strokeFill:this.axisThemeConfig.axisLineColor,strokeWidth:this.axisConfig.axisLineWidth}]})}if(this.showLabel&&e.push({type:"text",groupTexts:["left-axis","label"],data:this.getTickValues().map(r=>({text:r.toString(),x:this.boundingRect.x+this.boundingRect.width-(this.showLabel?this.axisConfig.labelPadding:0)-(this.showTick?this.axisConfig.tickLength:0)-(this.showAxisLine?this.axisConfig.axisLineWidth:0),y:this.getScaleValue(r),fill:this.axisThemeConfig.labelColor,fontSize:this.axisConfig.labelFontSize,rotation:0,verticalPos:"middle",horizontalPos:"right"}))}),this.showTick){let r=this.boundingRect.x+this.boundingRect.width-(this.showAxisLine?this.axisConfig.axisLineWidth:0);e.push({type:"path",groupTexts:["left-axis","ticks"],data:this.getTickValues().map(n=>({path:`M ${r},${this.getScaleValue(n)} L ${r-this.axisConfig.tickLength},${this.getScaleValue(n)}`,strokeFill:this.axisThemeConfig.tickColor,strokeWidth:this.axisConfig.tickWidth}))})}return this.showTitle&&e.push({type:"text",groupTexts:["left-axis","title"],data:[{text:this.title,x:this.boundingRect.x+this.axisConfig.titlePadding,y:this.boundingRect.y+this.boundingRect.height/2,fill:this.axisThemeConfig.titleColor,fontSize:this.axisConfig.titleFontSize,rotation:270,verticalPos:"top",horizontalPos:"center"}]}),e}getDrawableElementsForBottomAxis(){let e=[];if(this.showAxisLine){let r=this.boundingRect.y+this.axisConfig.axisLineWidth/2;e.push({type:"path",groupTexts:["bottom-axis","axis-line"],data:[{path:`M ${this.boundingRect.x},${r} L ${this.boundingRect.x+this.boundingRect.width},${r}`,strokeFill:this.axisThemeConfig.axisLineColor,strokeWidth:this.axisConfig.axisLineWidth}]})}if(this.showLabel&&e.push({type:"text",groupTexts:["bottom-axis","label"],data:this.getTickValues().map(r=>({text:r.toString(),x:this.getScaleValue(r),y:this.boundingRect.y+this.axisConfig.labelPadding+(this.showTick?this.axisConfig.tickLength:0)+(this.showAxisLine?this.axisConfig.axisLineWidth:0),fill:this.axisThemeConfig.labelColor,fontSize:this.axisConfig.labelFontSize,rotation:0,verticalPos:"top",horizontalPos:"center"}))}),this.showTick){let r=this.boundingRect.y+(this.showAxisLine?this.axisConfig.axisLineWidth:0);e.push({type:"path",groupTexts:["bottom-axis","ticks"],data:this.getTickValues().map(n=>({path:`M ${this.getScaleValue(n)},${r} L ${this.getScaleValue(n)},${r+this.axisConfig.tickLength}`,strokeFill:this.axisThemeConfig.tickColor,strokeWidth:this.axisConfig.tickWidth}))})}return this.showTitle&&e.push({type:"text",groupTexts:["bottom-axis","title"],data:[{text:this.title,x:this.range[0]+(this.range[1]-this.range[0])/2,y:this.boundingRect.y+this.boundingRect.height-this.axisConfig.titlePadding-this.titleTextHeight,fill:this.axisThemeConfig.titleColor,fontSize:this.axisConfig.titleFontSize,rotation:0,verticalPos:"top",horizontalPos:"center"}]}),e}getDrawableElementsForTopAxis(){let e=[];if(this.showAxisLine){let r=this.boundingRect.y+this.boundingRect.height-this.axisConfig.axisLineWidth/2;e.push({type:"path",groupTexts:["top-axis","axis-line"],data:[{path:`M ${this.boundingRect.x},${r} L ${this.boundingRect.x+this.boundingRect.width},${r}`,strokeFill:this.axisThemeConfig.axisLineColor,strokeWidth:this.axisConfig.axisLineWidth}]})}if(this.showLabel&&e.push({type:"text",groupTexts:["top-axis","label"],data:this.getTickValues().map(r=>({text:r.toString(),x:this.getScaleValue(r),y:this.boundingRect.y+(this.showTitle?this.titleTextHeight+this.axisConfig.titlePadding*2:0)+this.axisConfig.labelPadding,fill:this.axisThemeConfig.labelColor,fontSize:this.axisConfig.labelFontSize,rotation:0,verticalPos:"top",horizontalPos:"center"}))}),this.showTick){let r=this.boundingRect.y;e.push({type:"path",groupTexts:["top-axis","ticks"],data:this.getTickValues().map(n=>({path:`M ${this.getScaleValue(n)},${r+this.boundingRect.height-(this.showAxisLine?this.axisConfig.axisLineWidth:0)} L ${this.getScaleValue(n)},${r+this.boundingRect.height-this.axisConfig.tickLength-(this.showAxisLine?this.axisConfig.axisLineWidth:0)}`,strokeFill:this.axisThemeConfig.tickColor,strokeWidth:this.axisConfig.tickWidth}))})}return this.showTitle&&e.push({type:"text",groupTexts:["top-axis","title"],data:[{text:this.title,x:this.boundingRect.x+this.boundingRect.width/2,y:this.boundingRect.y+this.axisConfig.titlePadding,fill:this.axisThemeConfig.titleColor,fontSize:this.axisConfig.titleFontSize,rotation:0,verticalPos:"top",horizontalPos:"center"}]}),e}getDrawableElements(){if(this.axisPosition==="left")return this.getDrawableElementsForLeftAxis();if(this.axisPosition==="right")throw Error("Drawing of right axis is not implemented");return this.axisPosition==="bottom"?this.getDrawableElementsForBottomAxis():this.axisPosition==="top"?this.getDrawableElementsForTopAxis():[]}}});var dE,nue=R(()=>{"use strict";Zt();ut();eO();dE=class extends Ig{static{o(this,"BandAxis")}constructor(e,r,n,i,a){super(e,i,a,r),this.categories=n,this.scale=Op().domain(this.categories).range(this.getRange())}setRange(e){super.setRange(e)}recalculateScale(){this.scale=Op().domain(this.categories).range(this.getRange()).paddingInner(1).paddingOuter(0).align(.5),V.trace("BandAxis axis final categories, range: ",this.categories,this.getRange())}getTickValues(){return this.categories}getScaleValue(e){return this.scale(e)??this.getRange()[0]}}});var pE,iue=R(()=>{"use strict";Zt();eO();pE=class extends Ig{static{o(this,"LinearAxis")}constructor(e,r,n,i,a){super(e,i,a,r),this.domain=n,this.scale=gl().domain(this.domain).range(this.getRange())}getTickValues(){return this.scale.ticks()}recalculateScale(){let e=[...this.domain];this.axisPosition==="left"&&e.reverse(),this.scale=gl().domain(e).range(this.getRange())}getScaleValue(e){return this.scale(e)}}});function tO(t,e,r,n){let i=new Mg(n);return hE(t)?new dE(e,r,t.categories,t.title,i):new pE(e,r,[t.min,t.max],t.title,i)}var aue=R(()=>{"use strict";fE();JI();nue();iue();o(tO,"getAxis")});function sue(t,e,r,n){let i=new Mg(n);return new rO(i,t,e,r)}var rO,oue=R(()=>{"use strict";JI();rO=class{constructor(e,r,n,i){this.textDimensionCalculator=e;this.chartConfig=r;this.chartData=n;this.chartThemeConfig=i;this.boundingRect={x:0,y:0,width:0,height:0},this.showChartTitle=!1}static{o(this,"ChartTitle")}setBoundingBoxXY(e){this.boundingRect.x=e.x,this.boundingRect.y=e.y}calculateSpace(e){let r=this.textDimensionCalculator.getMaxDimension([this.chartData.title],this.chartConfig.titleFontSize),n=Math.max(r.width,e.width),i=r.height+2*this.chartConfig.titlePadding;return r.width<=n&&r.height<=i&&this.chartConfig.showTitle&&this.chartData.title&&(this.boundingRect.width=n,this.boundingRect.height=i,this.showChartTitle=!0),{width:this.boundingRect.width,height:this.boundingRect.height}}getDrawableElements(){let e=[];return this.showChartTitle&&e.push({groupTexts:["chart-title"],type:"text",data:[{fontSize:this.chartConfig.titleFontSize,text:this.chartData.title,verticalPos:"middle",horizontalPos:"center",x:this.boundingRect.x+this.boundingRect.width/2,y:this.boundingRect.y+this.boundingRect.height/2,fill:this.chartThemeConfig.titleColor,rotation:0}]}),e}};o(sue,"getChartTitleComponent")});var mE,lue=R(()=>{"use strict";Zt();mE=class{constructor(e,r,n,i,a){this.plotData=e;this.xAxis=r;this.yAxis=n;this.orientation=i;this.plotIndex=a}static{o(this,"LinePlot")}getDrawableElement(){let e=this.plotData.data.map(n=>[this.xAxis.getScaleValue(n[0]),this.yAxis.getScaleValue(n[1])]),r;return this.orientation==="horizontal"?r=ha().y(n=>n[0]).x(n=>n[1])(e):r=ha().x(n=>n[0]).y(n=>n[1])(e),r?[{groupTexts:["plot",`line-plot-${this.plotIndex}`],type:"path",data:[{path:r,strokeFill:this.plotData.strokeFill,strokeWidth:this.plotData.strokeWidth}]}]:[]}}});var gE,cue=R(()=>{"use strict";gE=class{constructor(e,r,n,i,a,s){this.barData=e;this.boundingRect=r;this.xAxis=n;this.yAxis=i;this.orientation=a;this.plotIndex=s}static{o(this,"BarPlot")}getDrawableElement(){let e=this.barData.data.map(a=>[this.xAxis.getScaleValue(a[0]),this.yAxis.getScaleValue(a[1])]),n=Math.min(this.xAxis.getAxisOuterPadding()*2,this.xAxis.getTickDistance())*(1-.05),i=n/2;return this.orientation==="horizontal"?[{groupTexts:["plot",`bar-plot-${this.plotIndex}`],type:"rect",data:e.map(a=>({x:this.boundingRect.x,y:a[0]-i,height:n,width:a[1]-this.boundingRect.x,fill:this.barData.fill,strokeWidth:0,strokeFill:this.barData.fill}))}]:[{groupTexts:["plot",`bar-plot-${this.plotIndex}`],type:"rect",data:e.map(a=>({x:a[0]-i,y:a[1],width:n,height:this.boundingRect.y+this.boundingRect.height-a[1],fill:this.barData.fill,strokeWidth:0,strokeFill:this.barData.fill}))}]}}});function uue(t,e,r){return new nO(t,e,r)}var nO,hue=R(()=>{"use strict";lue();cue();nO=class{constructor(e,r,n){this.chartConfig=e;this.chartData=r;this.chartThemeConfig=n;this.boundingRect={x:0,y:0,width:0,height:0}}static{o(this,"BasePlot")}setAxes(e,r){this.xAxis=e,this.yAxis=r}setBoundingBoxXY(e){this.boundingRect.x=e.x,this.boundingRect.y=e.y}calculateSpace(e){return this.boundingRect.width=e.width,this.boundingRect.height=e.height,{width:this.boundingRect.width,height:this.boundingRect.height}}getDrawableElements(){if(!(this.xAxis&&this.yAxis))throw Error("Axes must be passed to render Plots");let e=[];for(let[r,n]of this.chartData.plots.entries())switch(n.type){case"line":{let i=new mE(n,this.xAxis,this.yAxis,this.chartConfig.chartOrientation,r);e.push(...i.getDrawableElement())}break;case"bar":{let i=new gE(n,this.boundingRect,this.xAxis,this.yAxis,this.chartConfig.chartOrientation,r);e.push(...i.getDrawableElement())}break}return e}};o(uue,"getPlotComponent")});var yE,fue=R(()=>{"use strict";aue();oue();hue();fE();yE=class{constructor(e,r,n,i){this.chartConfig=e;this.chartData=r;this.componentStore={title:sue(e,r,n,i),plot:uue(e,r,n),xAxis:tO(r.xAxis,e.xAxis,{titleColor:n.xAxisTitleColor,labelColor:n.xAxisLabelColor,tickColor:n.xAxisTickColor,axisLineColor:n.xAxisLineColor},i),yAxis:tO(r.yAxis,e.yAxis,{titleColor:n.yAxisTitleColor,labelColor:n.yAxisLabelColor,tickColor:n.yAxisTickColor,axisLineColor:n.yAxisLineColor},i)}}static{o(this,"Orchestrator")}calculateVerticalSpace(){let e=this.chartConfig.width,r=this.chartConfig.height,n=0,i=0,a=Math.floor(e*this.chartConfig.plotReservedSpacePercent/100),s=Math.floor(r*this.chartConfig.plotReservedSpacePercent/100),l=this.componentStore.plot.calculateSpace({width:a,height:s});e-=l.width,r-=l.height,l=this.componentStore.title.calculateSpace({width:this.chartConfig.width,height:r}),i=l.height,r-=l.height,this.componentStore.xAxis.setAxisPosition("bottom"),l=this.componentStore.xAxis.calculateSpace({width:e,height:r}),r-=l.height,this.componentStore.yAxis.setAxisPosition("left"),l=this.componentStore.yAxis.calculateSpace({width:e,height:r}),n=l.width,e-=l.width,e>0&&(a+=e,e=0),r>0&&(s+=r,r=0),this.componentStore.plot.calculateSpace({width:a,height:s}),this.componentStore.plot.setBoundingBoxXY({x:n,y:i}),this.componentStore.xAxis.setRange([n,n+a]),this.componentStore.xAxis.setBoundingBoxXY({x:n,y:i+s}),this.componentStore.yAxis.setRange([i,i+s]),this.componentStore.yAxis.setBoundingBoxXY({x:0,y:i}),this.chartData.plots.some(u=>ZI(u))&&this.componentStore.xAxis.recalculateOuterPaddingToDrawBar()}calculateHorizontalSpace(){let e=this.chartConfig.width,r=this.chartConfig.height,n=0,i=0,a=0,s=Math.floor(e*this.chartConfig.plotReservedSpacePercent/100),l=Math.floor(r*this.chartConfig.plotReservedSpacePercent/100),u=this.componentStore.plot.calculateSpace({width:s,height:l});e-=u.width,r-=u.height,u=this.componentStore.title.calculateSpace({width:this.chartConfig.width,height:r}),n=u.height,r-=u.height,this.componentStore.xAxis.setAxisPosition("left"),u=this.componentStore.xAxis.calculateSpace({width:e,height:r}),e-=u.width,i=u.width,this.componentStore.yAxis.setAxisPosition("top"),u=this.componentStore.yAxis.calculateSpace({width:e,height:r}),r-=u.height,a=n+u.height,e>0&&(s+=e,e=0),r>0&&(l+=r,r=0),this.componentStore.plot.calculateSpace({width:s,height:l}),this.componentStore.plot.setBoundingBoxXY({x:i,y:a}),this.componentStore.yAxis.setRange([i,i+s]),this.componentStore.yAxis.setBoundingBoxXY({x:i,y:n}),this.componentStore.xAxis.setRange([a,a+l]),this.componentStore.xAxis.setBoundingBoxXY({x:0,y:a}),this.chartData.plots.some(h=>ZI(h))&&this.componentStore.xAxis.recalculateOuterPaddingToDrawBar()}calculateSpace(){this.chartConfig.chartOrientation==="horizontal"?this.calculateHorizontalSpace():this.calculateVerticalSpace()}getDrawableElement(){this.calculateSpace();let e=[];this.componentStore.plot.setAxes(this.componentStore.xAxis,this.componentStore.yAxis);for(let r of Object.values(this.componentStore))e.push(...r.getDrawableElements());return e}}});var vE,due=R(()=>{"use strict";fue();vE=class{static{o(this,"XYChartBuilder")}static build(e,r,n,i){return new yE(e,r,n,i).getDrawableElement()}}});function mue(){let t=hp(),e=Or();return Ts(t.xyChart,e.themeVariables.xyChart)}function gue(){let t=Or();return Ts(mr.xyChart,t.xyChart)}function yue(){return{yAxis:{type:"linear",title:"",min:1/0,max:-1/0},xAxis:{type:"band",title:"",categories:[]},title:"",plots:[]}}function sO(t){let e=Or();return qr(t.trim(),e)}function hze(t){pue=t}function fze(t){t==="horizontal"?px.chartOrientation="horizontal":px.chartOrientation="vertical"}function dze(t){sn.xAxis.title=sO(t.text)}function vue(t,e){sn.xAxis={type:"linear",title:sn.xAxis.title,min:t,max:e},xE=!0}function pze(t){sn.xAxis={type:"band",title:sn.xAxis.title,categories:t.map(e=>sO(e.text))},xE=!0}function mze(t){sn.yAxis.title=sO(t.text)}function gze(t,e){sn.yAxis={type:"linear",title:sn.yAxis.title,min:t,max:e},aO=!0}function yze(t){let e=Math.min(...t),r=Math.max(...t),n=Ng(sn.yAxis)?sn.yAxis.min:1/0,i=Ng(sn.yAxis)?sn.yAxis.max:-1/0;sn.yAxis={type:"linear",title:sn.yAxis.title,min:Math.min(n,e),max:Math.max(i,r)}}function xue(t){let e=[];if(t.length===0)return e;if(!xE){let r=Ng(sn.xAxis)?sn.xAxis.min:1/0,n=Ng(sn.xAxis)?sn.xAxis.max:-1/0;vue(Math.min(r,1),Math.max(n,t.length))}if(aO||yze(t),hE(sn.xAxis)&&(e=sn.xAxis.categories.map((r,n)=>[r,t[n]])),Ng(sn.xAxis)){let r=sn.xAxis.min,n=sn.xAxis.max,i=(n-r)/(t.length-1),a=[];for(let s=r;s<=n;s+=i)a.push(`${s}`);e=a.map((s,l)=>[s,t[l]])}return e}function bue(t){return iO[t===0?0:t%iO.length]}function vze(t,e){let r=xue(e);sn.plots.push({type:"line",strokeFill:bue(dx),strokeWidth:2,data:r}),dx++}function xze(t,e){let r=xue(e);sn.plots.push({type:"bar",fill:bue(dx),data:r}),dx++}function bze(){if(sn.plots.length===0)throw Error("No Plot to render, please provide a plot with some data");return sn.title=Xr(),vE.build(px,sn,mx,pue)}function wze(){return mx}function Tze(){return px}var dx,pue,px,mx,sn,iO,xE,aO,kze,wue,Tue=R(()=>{"use strict";qs();sl();jb();xr();rr();bi();due();fE();dx=0,px=gue(),mx=mue(),sn=yue(),iO=mx.plotColorPalette.split(",").map(t=>t.trim()),xE=!1,aO=!1;o(mue,"getChartDefaultThemeConfig");o(gue,"getChartDefaultConfig");o(yue,"getChartDefaultData");o(sO,"textSanitizer");o(hze,"setTmpSVGG");o(fze,"setOrientation");o(dze,"setXAxisTitle");o(vue,"setXAxisRangeData");o(pze,"setXAxisBand");o(mze,"setYAxisTitle");o(gze,"setYAxisRangeData");o(yze,"setYAxisRangeFromPlotData");o(xue,"transformDataWithoutCategory");o(bue,"getPlotColorFromPalette");o(vze,"setLineData");o(xze,"setBarData");o(bze,"getDrawableElem");o(wze,"getChartThemeConfig");o(Tze,"getChartConfig");kze=o(function(){vr(),dx=0,px=gue(),sn=yue(),mx=mue(),iO=mx.plotColorPalette.split(",").map(t=>t.trim()),xE=!1,aO=!1},"clear"),wue={getDrawableElem:bze,clear:kze,setAccTitle:kr,getAccTitle:Ar,setDiagramTitle:nn,getDiagramTitle:Xr,getAccDescription:Lr,setAccDescription:_r,setOrientation:fze,setXAxisTitle:dze,setXAxisRangeData:vue,setXAxisBand:pze,setYAxisTitle:mze,setYAxisRangeData:gze,setLineData:vze,setBarData:xze,setTmpSVGG:hze,getChartThemeConfig:wze,getChartConfig:Tze}});var Eze,kue,Eue=R(()=>{"use strict";ut();pf();Yn();Eze=o((t,e,r,n)=>{let i=n.db,a=i.getChartThemeConfig(),s=i.getChartConfig();function l(v){return v==="top"?"text-before-edge":"middle"}o(l,"getDominantBaseLine");function u(v){return v==="left"?"start":v==="right"?"end":"middle"}o(u,"getTextAnchor");function h(v){return`translate(${v.x}, ${v.y}) rotate(${v.rotation||0})`}o(h,"getTextTransformation"),V.debug(`Rendering xychart chart +`+t);let f=Ps(e),d=f.append("g").attr("class","main"),p=d.append("rect").attr("width",s.width).attr("height",s.height).attr("class","background");Sr(f,s.height,s.width,!0),f.attr("viewBox",`0 0 ${s.width} ${s.height}`),p.attr("fill",a.backgroundColor),i.setTmpSVGG(f.append("g").attr("class","mermaid-tmp-group"));let m=i.getDrawableElem(),g={};function y(v){let x=d,b="";for(let[w]of v.entries()){let S=d;w>0&&g[b]&&(S=g[b]),b+=v[w],x=g[b],x||(x=g[b]=S.append("g").attr("class",v[w]))}return x}o(y,"getGroup");for(let v of m){if(v.data.length===0)continue;let x=y(v.groupTexts);switch(v.type){case"rect":x.selectAll("rect").data(v.data).enter().append("rect").attr("x",b=>b.x).attr("y",b=>b.y).attr("width",b=>b.width).attr("height",b=>b.height).attr("fill",b=>b.fill).attr("stroke",b=>b.strokeFill).attr("stroke-width",b=>b.strokeWidth);break;case"text":x.selectAll("text").data(v.data).enter().append("text").attr("x",0).attr("y",0).attr("fill",b=>b.fill).attr("font-size",b=>b.fontSize).attr("dominant-baseline",b=>l(b.verticalPos)).attr("text-anchor",b=>u(b.horizontalPos)).attr("transform",b=>h(b)).text(b=>b.text);break;case"path":x.selectAll("path").data(v.data).enter().append("path").attr("d",b=>b.path).attr("fill",b=>b.fill?b.fill:"none").attr("stroke",b=>b.strokeFill).attr("stroke-width",b=>b.strokeWidth);break}}},"draw"),kue={draw:Eze}});var Cue={};hr(Cue,{diagram:()=>Cze});var Cze,Sue=R(()=>{"use strict";rue();Tue();Eue();Cze={parser:tue,db:wue,renderer:kue}});var oO,Lue,Due=R(()=>{"use strict";oO=function(){var t=o(function(ie,j,J,Z){for(J=J||{},Z=ie.length;Z--;J[ie[Z]]=j);return J},"o"),e=[1,3],r=[1,4],n=[1,5],i=[1,6],a=[5,6,8,9,11,13,31,32,33,34,35,36,44,62,63],s=[1,18],l=[2,7],u=[1,22],h=[1,23],f=[1,24],d=[1,25],p=[1,26],m=[1,27],g=[1,20],y=[1,28],v=[1,29],x=[62,63],b=[5,8,9,11,13,31,32,33,34,35,36,44,51,53,62,63],w=[1,47],S=[1,48],T=[1,49],E=[1,50],_=[1,51],A=[1,52],L=[1,53],M=[53,54],N=[1,64],k=[1,60],I=[1,61],C=[1,62],O=[1,63],D=[1,65],P=[1,69],F=[1,70],B=[1,67],$=[1,68],z=[5,8,9,11,13,31,32,33,34,35,36,44,62,63],Y={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,start:3,directive:4,NEWLINE:5,RD:6,diagram:7,EOF:8,acc_title:9,acc_title_value:10,acc_descr:11,acc_descr_value:12,acc_descr_multiline_value:13,requirementDef:14,elementDef:15,relationshipDef:16,requirementType:17,requirementName:18,STRUCT_START:19,requirementBody:20,ID:21,COLONSEP:22,id:23,TEXT:24,text:25,RISK:26,riskLevel:27,VERIFYMTHD:28,verifyType:29,STRUCT_STOP:30,REQUIREMENT:31,FUNCTIONAL_REQUIREMENT:32,INTERFACE_REQUIREMENT:33,PERFORMANCE_REQUIREMENT:34,PHYSICAL_REQUIREMENT:35,DESIGN_CONSTRAINT:36,LOW_RISK:37,MED_RISK:38,HIGH_RISK:39,VERIFY_ANALYSIS:40,VERIFY_DEMONSTRATION:41,VERIFY_INSPECTION:42,VERIFY_TEST:43,ELEMENT:44,elementName:45,elementBody:46,TYPE:47,type:48,DOCREF:49,ref:50,END_ARROW_L:51,relationship:52,LINE:53,END_ARROW_R:54,CONTAINS:55,COPIES:56,DERIVES:57,SATISFIES:58,VERIFIES:59,REFINES:60,TRACES:61,unqString:62,qString:63,$accept:0,$end:1},terminals_:{2:"error",5:"NEWLINE",6:"RD",8:"EOF",9:"acc_title",10:"acc_title_value",11:"acc_descr",12:"acc_descr_value",13:"acc_descr_multiline_value",19:"STRUCT_START",21:"ID",22:"COLONSEP",24:"TEXT",26:"RISK",28:"VERIFYMTHD",30:"STRUCT_STOP",31:"REQUIREMENT",32:"FUNCTIONAL_REQUIREMENT",33:"INTERFACE_REQUIREMENT",34:"PERFORMANCE_REQUIREMENT",35:"PHYSICAL_REQUIREMENT",36:"DESIGN_CONSTRAINT",37:"LOW_RISK",38:"MED_RISK",39:"HIGH_RISK",40:"VERIFY_ANALYSIS",41:"VERIFY_DEMONSTRATION",42:"VERIFY_INSPECTION",43:"VERIFY_TEST",44:"ELEMENT",47:"TYPE",49:"DOCREF",51:"END_ARROW_L",53:"LINE",54:"END_ARROW_R",55:"CONTAINS",56:"COPIES",57:"DERIVES",58:"SATISFIES",59:"VERIFIES",60:"REFINES",61:"TRACES",62:"unqString",63:"qString"},productions_:[0,[3,3],[3,2],[3,4],[4,2],[4,2],[4,1],[7,0],[7,2],[7,2],[7,2],[7,2],[7,2],[14,5],[20,5],[20,5],[20,5],[20,5],[20,2],[20,1],[17,1],[17,1],[17,1],[17,1],[17,1],[17,1],[27,1],[27,1],[27,1],[29,1],[29,1],[29,1],[29,1],[15,5],[46,5],[46,5],[46,2],[46,1],[16,5],[16,5],[52,1],[52,1],[52,1],[52,1],[52,1],[52,1],[52,1],[18,1],[18,1],[23,1],[23,1],[25,1],[25,1],[45,1],[45,1],[48,1],[48,1],[50,1],[50,1]],performAction:o(function(j,J,Z,H,q,K,se){var ce=K.length-1;switch(q){case 4:this.$=K[ce].trim(),H.setAccTitle(this.$);break;case 5:case 6:this.$=K[ce].trim(),H.setAccDescription(this.$);break;case 7:this.$=[];break;case 13:H.addRequirement(K[ce-3],K[ce-4]);break;case 14:H.setNewReqId(K[ce-2]);break;case 15:H.setNewReqText(K[ce-2]);break;case 16:H.setNewReqRisk(K[ce-2]);break;case 17:H.setNewReqVerifyMethod(K[ce-2]);break;case 20:this.$=H.RequirementType.REQUIREMENT;break;case 21:this.$=H.RequirementType.FUNCTIONAL_REQUIREMENT;break;case 22:this.$=H.RequirementType.INTERFACE_REQUIREMENT;break;case 23:this.$=H.RequirementType.PERFORMANCE_REQUIREMENT;break;case 24:this.$=H.RequirementType.PHYSICAL_REQUIREMENT;break;case 25:this.$=H.RequirementType.DESIGN_CONSTRAINT;break;case 26:this.$=H.RiskLevel.LOW_RISK;break;case 27:this.$=H.RiskLevel.MED_RISK;break;case 28:this.$=H.RiskLevel.HIGH_RISK;break;case 29:this.$=H.VerifyType.VERIFY_ANALYSIS;break;case 30:this.$=H.VerifyType.VERIFY_DEMONSTRATION;break;case 31:this.$=H.VerifyType.VERIFY_INSPECTION;break;case 32:this.$=H.VerifyType.VERIFY_TEST;break;case 33:H.addElement(K[ce-3]);break;case 34:H.setNewElementType(K[ce-2]);break;case 35:H.setNewElementDocRef(K[ce-2]);break;case 38:H.addRelationship(K[ce-2],K[ce],K[ce-4]);break;case 39:H.addRelationship(K[ce-2],K[ce-4],K[ce]);break;case 40:this.$=H.Relationships.CONTAINS;break;case 41:this.$=H.Relationships.COPIES;break;case 42:this.$=H.Relationships.DERIVES;break;case 43:this.$=H.Relationships.SATISFIES;break;case 44:this.$=H.Relationships.VERIFIES;break;case 45:this.$=H.Relationships.REFINES;break;case 46:this.$=H.Relationships.TRACES;break}},"anonymous"),table:[{3:1,4:2,6:e,9:r,11:n,13:i},{1:[3]},{3:8,4:2,5:[1,7],6:e,9:r,11:n,13:i},{5:[1,9]},{10:[1,10]},{12:[1,11]},t(a,[2,6]),{3:12,4:2,6:e,9:r,11:n,13:i},{1:[2,2]},{4:17,5:s,7:13,8:l,9:r,11:n,13:i,14:14,15:15,16:16,17:19,23:21,31:u,32:h,33:f,34:d,35:p,36:m,44:g,62:y,63:v},t(a,[2,4]),t(a,[2,5]),{1:[2,1]},{8:[1,30]},{4:17,5:s,7:31,8:l,9:r,11:n,13:i,14:14,15:15,16:16,17:19,23:21,31:u,32:h,33:f,34:d,35:p,36:m,44:g,62:y,63:v},{4:17,5:s,7:32,8:l,9:r,11:n,13:i,14:14,15:15,16:16,17:19,23:21,31:u,32:h,33:f,34:d,35:p,36:m,44:g,62:y,63:v},{4:17,5:s,7:33,8:l,9:r,11:n,13:i,14:14,15:15,16:16,17:19,23:21,31:u,32:h,33:f,34:d,35:p,36:m,44:g,62:y,63:v},{4:17,5:s,7:34,8:l,9:r,11:n,13:i,14:14,15:15,16:16,17:19,23:21,31:u,32:h,33:f,34:d,35:p,36:m,44:g,62:y,63:v},{4:17,5:s,7:35,8:l,9:r,11:n,13:i,14:14,15:15,16:16,17:19,23:21,31:u,32:h,33:f,34:d,35:p,36:m,44:g,62:y,63:v},{18:36,62:[1,37],63:[1,38]},{45:39,62:[1,40],63:[1,41]},{51:[1,42],53:[1,43]},t(x,[2,20]),t(x,[2,21]),t(x,[2,22]),t(x,[2,23]),t(x,[2,24]),t(x,[2,25]),t(b,[2,49]),t(b,[2,50]),{1:[2,3]},{8:[2,8]},{8:[2,9]},{8:[2,10]},{8:[2,11]},{8:[2,12]},{19:[1,44]},{19:[2,47]},{19:[2,48]},{19:[1,45]},{19:[2,53]},{19:[2,54]},{52:46,55:w,56:S,57:T,58:E,59:_,60:A,61:L},{52:54,55:w,56:S,57:T,58:E,59:_,60:A,61:L},{5:[1,55]},{5:[1,56]},{53:[1,57]},t(M,[2,40]),t(M,[2,41]),t(M,[2,42]),t(M,[2,43]),t(M,[2,44]),t(M,[2,45]),t(M,[2,46]),{54:[1,58]},{5:N,20:59,21:k,24:I,26:C,28:O,30:D},{5:P,30:F,46:66,47:B,49:$},{23:71,62:y,63:v},{23:72,62:y,63:v},t(z,[2,13]),{22:[1,73]},{22:[1,74]},{22:[1,75]},{22:[1,76]},{5:N,20:77,21:k,24:I,26:C,28:O,30:D},t(z,[2,19]),t(z,[2,33]),{22:[1,78]},{22:[1,79]},{5:P,30:F,46:80,47:B,49:$},t(z,[2,37]),t(z,[2,38]),t(z,[2,39]),{23:81,62:y,63:v},{25:82,62:[1,83],63:[1,84]},{27:85,37:[1,86],38:[1,87],39:[1,88]},{29:89,40:[1,90],41:[1,91],42:[1,92],43:[1,93]},t(z,[2,18]),{48:94,62:[1,95],63:[1,96]},{50:97,62:[1,98],63:[1,99]},t(z,[2,36]),{5:[1,100]},{5:[1,101]},{5:[2,51]},{5:[2,52]},{5:[1,102]},{5:[2,26]},{5:[2,27]},{5:[2,28]},{5:[1,103]},{5:[2,29]},{5:[2,30]},{5:[2,31]},{5:[2,32]},{5:[1,104]},{5:[2,55]},{5:[2,56]},{5:[1,105]},{5:[2,57]},{5:[2,58]},{5:N,20:106,21:k,24:I,26:C,28:O,30:D},{5:N,20:107,21:k,24:I,26:C,28:O,30:D},{5:N,20:108,21:k,24:I,26:C,28:O,30:D},{5:N,20:109,21:k,24:I,26:C,28:O,30:D},{5:P,30:F,46:110,47:B,49:$},{5:P,30:F,46:111,47:B,49:$},t(z,[2,14]),t(z,[2,15]),t(z,[2,16]),t(z,[2,17]),t(z,[2,34]),t(z,[2,35])],defaultActions:{8:[2,2],12:[2,1],30:[2,3],31:[2,8],32:[2,9],33:[2,10],34:[2,11],35:[2,12],37:[2,47],38:[2,48],40:[2,53],41:[2,54],83:[2,51],84:[2,52],86:[2,26],87:[2,27],88:[2,28],90:[2,29],91:[2,30],92:[2,31],93:[2,32],95:[2,55],96:[2,56],98:[2,57],99:[2,58]},parseError:o(function(j,J){if(J.recoverable)this.trace(j);else{var Z=new Error(j);throw Z.hash=J,Z}},"parseError"),parse:o(function(j){var J=this,Z=[0],H=[],q=[null],K=[],se=this.table,ce="",ue=0,te=0,De=0,oe=2,ke=1,Ie=K.slice.call(arguments,1),Se=Object.create(this.lexer),Ue={yy:{}};for(var Pe in this.yy)Object.prototype.hasOwnProperty.call(this.yy,Pe)&&(Ue.yy[Pe]=this.yy[Pe]);Se.setInput(j,Ue.yy),Ue.yy.lexer=Se,Ue.yy.parser=this,typeof Se.yylloc>"u"&&(Se.yylloc={});var _e=Se.yylloc;K.push(_e);var me=Se.options&&Se.options.ranges;typeof Ue.yy.parseError=="function"?this.parseError=Ue.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function W(He){Z.length=Z.length-2*He,q.length=q.length-He,K.length=K.length-He}o(W,"popStack");function fe(){var He;return He=H.pop()||Se.lex()||ke,typeof He!="number"&&(He instanceof Array&&(H=He,He=H.pop()),He=J.symbols_[He]||He),He}o(fe,"lex");for(var ge,re,he,ne,ae,we,Te={},Ce,Ae,Ge,Me;;){if(he=Z[Z.length-1],this.defaultActions[he]?ne=this.defaultActions[he]:((ge===null||typeof ge>"u")&&(ge=fe()),ne=se[he]&&se[he][ge]),typeof ne>"u"||!ne.length||!ne[0]){var ye="";Me=[];for(Ce in se[he])this.terminals_[Ce]&&Ce>oe&&Me.push("'"+this.terminals_[Ce]+"'");Se.showPosition?ye="Parse error on line "+(ue+1)+`: +`+Se.showPosition()+` +Expecting `+Me.join(", ")+", got '"+(this.terminals_[ge]||ge)+"'":ye="Parse error on line "+(ue+1)+": Unexpected "+(ge==ke?"end of input":"'"+(this.terminals_[ge]||ge)+"'"),this.parseError(ye,{text:Se.match,token:this.terminals_[ge]||ge,line:Se.yylineno,loc:_e,expected:Me})}if(ne[0]instanceof Array&&ne.length>1)throw new Error("Parse Error: multiple actions possible at state: "+he+", token: "+ge);switch(ne[0]){case 1:Z.push(ge),q.push(Se.yytext),K.push(Se.yylloc),Z.push(ne[1]),ge=null,re?(ge=re,re=null):(te=Se.yyleng,ce=Se.yytext,ue=Se.yylineno,_e=Se.yylloc,De>0&&De--);break;case 2:if(Ae=this.productions_[ne[1]][1],Te.$=q[q.length-Ae],Te._$={first_line:K[K.length-(Ae||1)].first_line,last_line:K[K.length-1].last_line,first_column:K[K.length-(Ae||1)].first_column,last_column:K[K.length-1].last_column},me&&(Te._$.range=[K[K.length-(Ae||1)].range[0],K[K.length-1].range[1]]),we=this.performAction.apply(Te,[ce,te,ue,Ue.yy,ne[1],q,K].concat(Ie)),typeof we<"u")return we;Ae&&(Z=Z.slice(0,-1*Ae*2),q=q.slice(0,-1*Ae),K=K.slice(0,-1*Ae)),Z.push(this.productions_[ne[1]][0]),q.push(Te.$),K.push(Te._$),Ge=se[Z[Z.length-2]][Z[Z.length-1]],Z.push(Ge);break;case 3:return!0}}return!0},"parse")},Q=function(){var ie={EOF:1,parseError:o(function(J,Z){if(this.yy.parser)this.yy.parser.parseError(J,Z);else throw new Error(J)},"parseError"),setInput:o(function(j,J){return this.yy=J||this.yy||{},this._input=j,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var j=this._input[0];this.yytext+=j,this.yyleng++,this.offset++,this.match+=j,this.matched+=j;var J=j.match(/(?:\r\n?|\n).*/g);return J?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),j},"input"),unput:o(function(j){var J=j.length,Z=j.split(/(?:\r\n?|\n)/g);this._input=j+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-J),this.offset-=J;var H=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),Z.length-1&&(this.yylineno-=Z.length-1);var q=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:Z?(Z.length===H.length?this.yylloc.first_column:0)+H[H.length-Z.length].length-Z[0].length:this.yylloc.first_column-J},this.options.ranges&&(this.yylloc.range=[q[0],q[0]+this.yyleng-J]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(j){this.unput(this.match.slice(j))},"less"),pastInput:o(function(){var j=this.matched.substr(0,this.matched.length-this.match.length);return(j.length>20?"...":"")+j.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var j=this.match;return j.length<20&&(j+=this._input.substr(0,20-j.length)),(j.substr(0,20)+(j.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var j=this.pastInput(),J=new Array(j.length+1).join("-");return j+this.upcomingInput()+` +`+J+"^"},"showPosition"),test_match:o(function(j,J){var Z,H,q;if(this.options.backtrack_lexer&&(q={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(q.yylloc.range=this.yylloc.range.slice(0))),H=j[0].match(/(?:\r\n?|\n).*/g),H&&(this.yylineno+=H.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:H?H[H.length-1].length-H[H.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+j[0].length},this.yytext+=j[0],this.match+=j[0],this.matches=j,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(j[0].length),this.matched+=j[0],Z=this.performAction.call(this,this.yy,this,J,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),Z)return Z;if(this._backtrack){for(var K in q)this[K]=q[K];return!1}return!1},"test_match"),next:o(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var j,J,Z,H;this._more||(this.yytext="",this.match="");for(var q=this._currentRules(),K=0;KJ[0].length)){if(J=Z,H=K,this.options.backtrack_lexer){if(j=this.test_match(Z,q[K]),j!==!1)return j;if(this._backtrack){J=!1;continue}else return!1}else if(!this.options.flex)break}return J?(j=this.test_match(J,q[H]),j!==!1?j:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:o(function(){var J=this.next();return J||this.lex()},"lex"),begin:o(function(J){this.conditionStack.push(J)},"begin"),popState:o(function(){var J=this.conditionStack.length-1;return J>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:o(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:o(function(J){return J=this.conditionStack.length-1-Math.abs(J||0),J>=0?this.conditionStack[J]:"INITIAL"},"topState"),pushState:o(function(J){this.begin(J)},"pushState"),stateStackSize:o(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:o(function(J,Z,H,q){var K=q;switch(H){case 0:return"title";case 1:return this.begin("acc_title"),9;break;case 2:return this.popState(),"acc_title_value";break;case 3:return this.begin("acc_descr"),11;break;case 4:return this.popState(),"acc_descr_value";break;case 5:this.begin("acc_descr_multiline");break;case 6:this.popState();break;case 7:return"acc_descr_multiline_value";case 8:return 5;case 9:break;case 10:break;case 11:break;case 12:return 8;case 13:return 6;case 14:return 19;case 15:return 30;case 16:return 22;case 17:return 21;case 18:return 24;case 19:return 26;case 20:return 28;case 21:return 31;case 22:return 32;case 23:return 33;case 24:return 34;case 25:return 35;case 26:return 36;case 27:return 37;case 28:return 38;case 29:return 39;case 30:return 40;case 31:return 41;case 32:return 42;case 33:return 43;case 34:return 44;case 35:return 55;case 36:return 56;case 37:return 57;case 38:return 58;case 39:return 59;case 40:return 60;case 41:return 61;case 42:return 47;case 43:return 49;case 44:return 51;case 45:return 54;case 46:return 53;case 47:this.begin("string");break;case 48:this.popState();break;case 49:return"qString";case 50:return Z.yytext=Z.yytext.trim(),62;break}},"anonymous"),rules:[/^(?:title\s[^#\n;]+)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?:(\r?\n)+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:$)/i,/^(?:requirementDiagram\b)/i,/^(?:\{)/i,/^(?:\})/i,/^(?::)/i,/^(?:id\b)/i,/^(?:text\b)/i,/^(?:risk\b)/i,/^(?:verifyMethod\b)/i,/^(?:requirement\b)/i,/^(?:functionalRequirement\b)/i,/^(?:interfaceRequirement\b)/i,/^(?:performanceRequirement\b)/i,/^(?:physicalRequirement\b)/i,/^(?:designConstraint\b)/i,/^(?:low\b)/i,/^(?:medium\b)/i,/^(?:high\b)/i,/^(?:analysis\b)/i,/^(?:demonstration\b)/i,/^(?:inspection\b)/i,/^(?:test\b)/i,/^(?:element\b)/i,/^(?:contains\b)/i,/^(?:copies\b)/i,/^(?:derives\b)/i,/^(?:satisfies\b)/i,/^(?:verifies\b)/i,/^(?:refines\b)/i,/^(?:traces\b)/i,/^(?:type\b)/i,/^(?:docref\b)/i,/^(?:<-)/i,/^(?:->)/i,/^(?:-)/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[\w][^\r\n\{\<\>\-\=]*)/i],conditions:{acc_descr_multiline:{rules:[6,7],inclusive:!1},acc_descr:{rules:[4],inclusive:!1},acc_title:{rules:[2],inclusive:!1},unqString:{rules:[],inclusive:!1},token:{rules:[],inclusive:!1},string:{rules:[48,49],inclusive:!1},INITIAL:{rules:[0,1,3,5,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,50],inclusive:!0}}};return ie}();Y.lexer=Q;function X(){this.yy={}}return o(X,"Parser"),X.prototype=Y,Y.Parser=X,new X}();oO.parser=oO;Lue=oO});var lO,Bs,gx,mf,yx,Lze,Dze,Rze,Nze,Mze,Ize,Oze,Pze,Bze,Fze,zze,Gze,$ze,Vze,Uze,Hze,Yze,Rue,Nue=R(()=>{"use strict";_t();ut();bi();lO=[],Bs={},gx=new Map,mf={},yx=new Map,Lze={REQUIREMENT:"Requirement",FUNCTIONAL_REQUIREMENT:"Functional Requirement",INTERFACE_REQUIREMENT:"Interface Requirement",PERFORMANCE_REQUIREMENT:"Performance Requirement",PHYSICAL_REQUIREMENT:"Physical Requirement",DESIGN_CONSTRAINT:"Design Constraint"},Dze={LOW_RISK:"Low",MED_RISK:"Medium",HIGH_RISK:"High"},Rze={VERIFY_ANALYSIS:"Analysis",VERIFY_DEMONSTRATION:"Demonstration",VERIFY_INSPECTION:"Inspection",VERIFY_TEST:"Test"},Nze={CONTAINS:"contains",COPIES:"copies",DERIVES:"derives",SATISFIES:"satisfies",VERIFIES:"verifies",REFINES:"refines",TRACES:"traces"},Mze=o((t,e)=>(gx.has(t)||gx.set(t,{name:t,type:e,id:Bs.id,text:Bs.text,risk:Bs.risk,verifyMethod:Bs.verifyMethod}),Bs={},gx.get(t)),"addRequirement"),Ize=o(()=>gx,"getRequirements"),Oze=o(t=>{Bs!==void 0&&(Bs.id=t)},"setNewReqId"),Pze=o(t=>{Bs!==void 0&&(Bs.text=t)},"setNewReqText"),Bze=o(t=>{Bs!==void 0&&(Bs.risk=t)},"setNewReqRisk"),Fze=o(t=>{Bs!==void 0&&(Bs.verifyMethod=t)},"setNewReqVerifyMethod"),zze=o(t=>(yx.has(t)||(yx.set(t,{name:t,type:mf.type,docRef:mf.docRef}),V.info("Added new requirement: ",t)),mf={},yx.get(t)),"addElement"),Gze=o(()=>yx,"getElements"),$ze=o(t=>{mf!==void 0&&(mf.type=t)},"setNewElementType"),Vze=o(t=>{mf!==void 0&&(mf.docRef=t)},"setNewElementDocRef"),Uze=o((t,e,r)=>{lO.push({type:t,src:e,dst:r})},"addRelationship"),Hze=o(()=>lO,"getRelationships"),Yze=o(()=>{lO=[],Bs={},gx=new Map,mf={},yx=new Map,vr()},"clear"),Rue={RequirementType:Lze,RiskLevel:Dze,VerifyType:Rze,Relationships:Nze,getConfig:o(()=>de().req,"getConfig"),addRequirement:Mze,getRequirements:Ize,setNewReqId:Oze,setNewReqText:Pze,setNewReqRisk:Bze,setNewReqVerifyMethod:Fze,setAccTitle:kr,getAccTitle:Ar,setAccDescription:_r,getAccDescription:Lr,addElement:zze,getElements:Gze,setNewElementType:$ze,setNewElementDocRef:Vze,addRelationship:Uze,getRelationships:Hze,clear:Yze}});var Wze,Mue,Iue=R(()=>{"use strict";Wze=o(t=>` + + marker { + fill: ${t.relationColor}; + stroke: ${t.relationColor}; + } + + marker.cross { + stroke: ${t.lineColor}; + } + + svg { + font-family: ${t.fontFamily}; + font-size: ${t.fontSize}; + } + + .reqBox { + fill: ${t.requirementBackground}; + fill-opacity: 1.0; + stroke: ${t.requirementBorderColor}; + stroke-width: ${t.requirementBorderSize}; + } + + .reqTitle, .reqLabel{ + fill: ${t.requirementTextColor}; + } + .reqLabelBox { + fill: ${t.relationLabelBackground}; + fill-opacity: 1.0; + } + + .req-title-line { + stroke: ${t.requirementBorderColor}; + stroke-width: ${t.requirementBorderSize}; + } + .relationshipLine { + stroke: ${t.relationColor}; + stroke-width: 1; + } + .relationshipLabel { + fill: ${t.relationLabelColor}; + } + +`,"getStyles"),Mue=Wze});var cO,qze,uO,Oue=R(()=>{"use strict";cO={CONTAINS:"contains",ARROW:"arrow"},qze=o((t,e)=>{let r=t.append("defs").append("marker").attr("id",cO.CONTAINS+"_line_ending").attr("refX",0).attr("refY",e.line_height/2).attr("markerWidth",e.line_height).attr("markerHeight",e.line_height).attr("orient","auto").append("g");r.append("circle").attr("cx",e.line_height/2).attr("cy",e.line_height/2).attr("r",e.line_height/2).attr("fill","none"),r.append("line").attr("x1",0).attr("x2",e.line_height).attr("y1",e.line_height/2).attr("y2",e.line_height/2).attr("stroke-width",1),r.append("line").attr("y1",0).attr("y2",e.line_height).attr("x1",e.line_height/2).attr("x2",e.line_height/2).attr("stroke-width",1),t.append("defs").append("marker").attr("id",cO.ARROW+"_line_ending").attr("refX",e.line_height).attr("refY",.5*e.line_height).attr("markerWidth",e.line_height).attr("markerHeight",e.line_height).attr("orient","auto").append("path").attr("d",`M0,0 + L${e.line_height},${e.line_height/2} + M${e.line_height},${e.line_height/2} + L0,${e.line_height}`).attr("stroke-width",1)},"insertLineEndings"),uO={ReqMarkers:cO,insertLineEndings:qze}});var ai,Pue,Bue,Fue,zue,Xze,jze,Kze,Qze,Zze,Jze,Og,eGe,Gue,$ue=R(()=>{"use strict";Zt();Vd();ya();_t();ut();Yn();rr();Oue();ai={},Pue=0,Bue=o((t,e)=>t.insert("rect","#"+e).attr("class","req reqBox").attr("x",0).attr("y",0).attr("width",ai.rect_min_width+"px").attr("height",ai.rect_min_height+"px"),"newRectNode"),Fue=o((t,e,r)=>{let n=ai.rect_min_width/2,i=t.append("text").attr("class","req reqLabel reqTitle").attr("id",e).attr("x",n).attr("y",ai.rect_padding).attr("dominant-baseline","hanging"),a=0;r.forEach(h=>{a==0?i.append("tspan").attr("text-anchor","middle").attr("x",ai.rect_min_width/2).attr("dy",0).text(h):i.append("tspan").attr("text-anchor","middle").attr("x",ai.rect_min_width/2).attr("dy",ai.line_height*.75).text(h),a++});let s=1.5*ai.rect_padding,l=a*ai.line_height*.75,u=s+l;return t.append("line").attr("class","req-title-line").attr("x1","0").attr("x2",ai.rect_min_width).attr("y1",u).attr("y2",u),{titleNode:i,y:u}},"newTitleNode"),zue=o((t,e,r,n)=>{let i=t.append("text").attr("class","req reqLabel").attr("id",e).attr("x",ai.rect_padding).attr("y",n).attr("dominant-baseline","hanging"),a=0,s=30,l=[];return r.forEach(u=>{let h=u.length;for(;h>s&&a<3;){let f=u.substring(0,s);u=u.substring(s,u.length),h=u.length,l[l.length]=f,a++}if(a==3){let f=l[l.length-1];l[l.length-1]=f.substring(0,f.length-4)+"..."}else l[l.length]=u;a=0}),l.forEach(u=>{i.append("tspan").attr("x",ai.rect_padding).attr("dy",ai.line_height).text(u)}),i},"newBodyNode"),Xze=o((t,e,r,n)=>{let i=e.node().getTotalLength(),a=e.node().getPointAtLength(i*.5),s="rel"+Pue;Pue++;let u=t.append("text").attr("class","req relationshipLabel").attr("id",s).attr("x",a.x).attr("y",a.y).attr("text-anchor","middle").attr("dominant-baseline","middle").text(n).node().getBBox();t.insert("rect","#"+s).attr("class","req reqLabelBox").attr("x",a.x-u.width/2).attr("y",a.y-u.height/2).attr("width",u.width).attr("height",u.height).attr("fill","white").attr("fill-opacity","85%")},"addEdgeLabel"),jze=o(function(t,e,r,n,i){let a=r.edge(Og(e.src),Og(e.dst)),s=ha().x(function(u){return u.x}).y(function(u){return u.y}),l=t.insert("path","#"+n).attr("class","er relationshipLine").attr("d",s(a.points)).attr("fill","none");e.type==i.db.Relationships.CONTAINS?l.attr("marker-start","url("+We.getUrl(ai.arrowMarkerAbsolute)+"#"+e.type+"_line_ending)"):(l.attr("stroke-dasharray","10,7"),l.attr("marker-end","url("+We.getUrl(ai.arrowMarkerAbsolute)+"#"+uO.ReqMarkers.ARROW+"_line_ending)")),Xze(t,l,ai,`<<${e.type}>>`)},"drawRelationshipFromLayout"),Kze=o((t,e,r)=>{t.forEach((n,i)=>{i=Og(i),V.info("Added new requirement: ",i);let a=r.append("g").attr("id",i),s="req-"+i,l=Bue(a,s),u=[],h=Fue(a,i+"_title",[`<<${n.type}>>`,`${n.name}`]);u.push(h.titleNode);let f=zue(a,i+"_body",[`Id: ${n.id}`,`Text: ${n.text}`,`Risk: ${n.risk}`,`Verification: ${n.verifyMethod}`],h.y);u.push(f);let d=l.node().getBBox();e.setNode(i,{width:d.width,height:d.height,shape:"rect",id:i})})},"drawReqs"),Qze=o((t,e,r)=>{t.forEach((n,i)=>{let a=Og(i),s=r.append("g").attr("id",a),l="element-"+a,u=Bue(s,l),h=[],f=Fue(s,l+"_title",["<>",`${i}`]);h.push(f.titleNode);let d=zue(s,l+"_body",[`Type: ${n.type||"Not Specified"}`,`Doc Ref: ${n.docRef||"None"}`],f.y);h.push(d);let p=u.node().getBBox();e.setNode(a,{width:p.width,height:p.height,shape:"rect",id:a})})},"drawElements"),Zze=o((t,e)=>(t.forEach(function(r){let n=Og(r.src),i=Og(r.dst);e.setEdge(n,i,{relationship:r})}),t),"addRelationships"),Jze=o(function(t,e){e.nodes().forEach(function(r){r!==void 0&&e.node(r)!==void 0&&(t.select("#"+r),t.select("#"+r).attr("transform","translate("+(e.node(r).x-e.node(r).width/2)+","+(e.node(r).y-e.node(r).height/2)+" )"))})},"adjustEntities"),Og=o(t=>t.replace(/\s/g,"").replace(/\./g,"_"),"elementString"),eGe=o((t,e,r,n)=>{ai=de().requirement;let i=ai.securityLevel,a;i==="sandbox"&&(a=$e("#i"+e));let l=(i==="sandbox"?$e(a.nodes()[0].contentDocument.body):$e("body")).select(`[id='${e}']`);uO.insertLineEndings(l,ai);let u=new lr({multigraph:!1,compound:!1,directed:!0}).setGraph({rankdir:ai.layoutDirection,marginx:20,marginy:20,nodesep:100,edgesep:100,ranksep:100}).setDefaultEdgeLabel(function(){return{}}),h=n.db.getRequirements(),f=n.db.getElements(),d=n.db.getRelationships();Kze(h,u,l),Qze(f,u,l),Zze(d,u),lo(u),Jze(l,u),d.forEach(function(v){jze(l,v,u,e,n)});let p=ai.rect_padding,m=l.node().getBBox(),g=m.width+p*2,y=m.height+p*2;Sr(l,y,g,ai.useMaxWidth),l.attr("viewBox",`${m.x-p} ${m.y-p} ${g} ${y}`)},"draw"),Gue={draw:eGe}});var Vue={};hr(Vue,{diagram:()=>tGe});var tGe,Uue=R(()=>{"use strict";Due();Nue();Iue();$ue();tGe={parser:Lue,db:Rue,renderer:Gue,styles:Mue}});var hO,Wue,que=R(()=>{"use strict";hO=function(){var t=o(function(H,q,K,se){for(K=K||{},se=H.length;se--;K[H[se]]=q);return K},"o"),e=[1,2],r=[1,3],n=[1,4],i=[2,4],a=[1,9],s=[1,11],l=[1,13],u=[1,14],h=[1,16],f=[1,17],d=[1,18],p=[1,24],m=[1,25],g=[1,26],y=[1,27],v=[1,28],x=[1,29],b=[1,30],w=[1,31],S=[1,32],T=[1,33],E=[1,34],_=[1,35],A=[1,36],L=[1,37],M=[1,38],N=[1,39],k=[1,41],I=[1,42],C=[1,43],O=[1,44],D=[1,45],P=[1,46],F=[1,4,5,13,14,16,18,21,23,29,30,31,33,35,36,37,38,39,41,43,44,46,47,48,49,50,52,53,54,59,60,61,62,70],B=[4,5,16,50,52,53],$=[4,5,13,14,16,18,21,23,29,30,31,33,35,36,37,38,39,41,43,44,46,50,52,53,54,59,60,61,62,70],z=[4,5,13,14,16,18,21,23,29,30,31,33,35,36,37,38,39,41,43,44,46,49,50,52,53,54,59,60,61,62,70],Y=[4,5,13,14,16,18,21,23,29,30,31,33,35,36,37,38,39,41,43,44,46,48,50,52,53,54,59,60,61,62,70],Q=[4,5,13,14,16,18,21,23,29,30,31,33,35,36,37,38,39,41,43,44,46,47,50,52,53,54,59,60,61,62,70],X=[68,69,70],ie=[1,122],j={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,start:3,SPACE:4,NEWLINE:5,SD:6,document:7,line:8,statement:9,box_section:10,box_line:11,participant_statement:12,create:13,box:14,restOfLine:15,end:16,signal:17,autonumber:18,NUM:19,off:20,activate:21,actor:22,deactivate:23,note_statement:24,links_statement:25,link_statement:26,properties_statement:27,details_statement:28,title:29,legacy_title:30,acc_title:31,acc_title_value:32,acc_descr:33,acc_descr_value:34,acc_descr_multiline_value:35,loop:36,rect:37,opt:38,alt:39,else_sections:40,par:41,par_sections:42,par_over:43,critical:44,option_sections:45,break:46,option:47,and:48,else:49,participant:50,AS:51,participant_actor:52,destroy:53,note:54,placement:55,text2:56,over:57,actor_pair:58,links:59,link:60,properties:61,details:62,spaceList:63,",":64,left_of:65,right_of:66,signaltype:67,"+":68,"-":69,ACTOR:70,SOLID_OPEN_ARROW:71,DOTTED_OPEN_ARROW:72,SOLID_ARROW:73,BIDIRECTIONAL_SOLID_ARROW:74,DOTTED_ARROW:75,BIDIRECTIONAL_DOTTED_ARROW:76,SOLID_CROSS:77,DOTTED_CROSS:78,SOLID_POINT:79,DOTTED_POINT:80,TXT:81,$accept:0,$end:1},terminals_:{2:"error",4:"SPACE",5:"NEWLINE",6:"SD",13:"create",14:"box",15:"restOfLine",16:"end",18:"autonumber",19:"NUM",20:"off",21:"activate",23:"deactivate",29:"title",30:"legacy_title",31:"acc_title",32:"acc_title_value",33:"acc_descr",34:"acc_descr_value",35:"acc_descr_multiline_value",36:"loop",37:"rect",38:"opt",39:"alt",41:"par",43:"par_over",44:"critical",46:"break",47:"option",48:"and",49:"else",50:"participant",51:"AS",52:"participant_actor",53:"destroy",54:"note",57:"over",59:"links",60:"link",61:"properties",62:"details",64:",",65:"left_of",66:"right_of",68:"+",69:"-",70:"ACTOR",71:"SOLID_OPEN_ARROW",72:"DOTTED_OPEN_ARROW",73:"SOLID_ARROW",74:"BIDIRECTIONAL_SOLID_ARROW",75:"DOTTED_ARROW",76:"BIDIRECTIONAL_DOTTED_ARROW",77:"SOLID_CROSS",78:"DOTTED_CROSS",79:"SOLID_POINT",80:"DOTTED_POINT",81:"TXT"},productions_:[0,[3,2],[3,2],[3,2],[7,0],[7,2],[8,2],[8,1],[8,1],[10,0],[10,2],[11,2],[11,1],[11,1],[9,1],[9,2],[9,4],[9,2],[9,4],[9,3],[9,3],[9,2],[9,3],[9,3],[9,2],[9,2],[9,2],[9,2],[9,2],[9,1],[9,1],[9,2],[9,2],[9,1],[9,4],[9,4],[9,4],[9,4],[9,4],[9,4],[9,4],[9,4],[45,1],[45,4],[42,1],[42,4],[40,1],[40,4],[12,5],[12,3],[12,5],[12,3],[12,3],[24,4],[24,4],[25,3],[26,3],[27,3],[28,3],[63,2],[63,1],[58,3],[58,1],[55,1],[55,1],[17,5],[17,5],[17,4],[22,1],[67,1],[67,1],[67,1],[67,1],[67,1],[67,1],[67,1],[67,1],[67,1],[67,1],[56,1]],performAction:o(function(q,K,se,ce,ue,te,De){var oe=te.length-1;switch(ue){case 3:return ce.apply(te[oe]),te[oe];break;case 4:case 9:this.$=[];break;case 5:case 10:te[oe-1].push(te[oe]),this.$=te[oe-1];break;case 6:case 7:case 11:case 12:this.$=te[oe];break;case 8:case 13:this.$=[];break;case 15:te[oe].type="createParticipant",this.$=te[oe];break;case 16:te[oe-1].unshift({type:"boxStart",boxData:ce.parseBoxData(te[oe-2])}),te[oe-1].push({type:"boxEnd",boxText:te[oe-2]}),this.$=te[oe-1];break;case 18:this.$={type:"sequenceIndex",sequenceIndex:Number(te[oe-2]),sequenceIndexStep:Number(te[oe-1]),sequenceVisible:!0,signalType:ce.LINETYPE.AUTONUMBER};break;case 19:this.$={type:"sequenceIndex",sequenceIndex:Number(te[oe-1]),sequenceIndexStep:1,sequenceVisible:!0,signalType:ce.LINETYPE.AUTONUMBER};break;case 20:this.$={type:"sequenceIndex",sequenceVisible:!1,signalType:ce.LINETYPE.AUTONUMBER};break;case 21:this.$={type:"sequenceIndex",sequenceVisible:!0,signalType:ce.LINETYPE.AUTONUMBER};break;case 22:this.$={type:"activeStart",signalType:ce.LINETYPE.ACTIVE_START,actor:te[oe-1].actor};break;case 23:this.$={type:"activeEnd",signalType:ce.LINETYPE.ACTIVE_END,actor:te[oe-1].actor};break;case 29:ce.setDiagramTitle(te[oe].substring(6)),this.$=te[oe].substring(6);break;case 30:ce.setDiagramTitle(te[oe].substring(7)),this.$=te[oe].substring(7);break;case 31:this.$=te[oe].trim(),ce.setAccTitle(this.$);break;case 32:case 33:this.$=te[oe].trim(),ce.setAccDescription(this.$);break;case 34:te[oe-1].unshift({type:"loopStart",loopText:ce.parseMessage(te[oe-2]),signalType:ce.LINETYPE.LOOP_START}),te[oe-1].push({type:"loopEnd",loopText:te[oe-2],signalType:ce.LINETYPE.LOOP_END}),this.$=te[oe-1];break;case 35:te[oe-1].unshift({type:"rectStart",color:ce.parseMessage(te[oe-2]),signalType:ce.LINETYPE.RECT_START}),te[oe-1].push({type:"rectEnd",color:ce.parseMessage(te[oe-2]),signalType:ce.LINETYPE.RECT_END}),this.$=te[oe-1];break;case 36:te[oe-1].unshift({type:"optStart",optText:ce.parseMessage(te[oe-2]),signalType:ce.LINETYPE.OPT_START}),te[oe-1].push({type:"optEnd",optText:ce.parseMessage(te[oe-2]),signalType:ce.LINETYPE.OPT_END}),this.$=te[oe-1];break;case 37:te[oe-1].unshift({type:"altStart",altText:ce.parseMessage(te[oe-2]),signalType:ce.LINETYPE.ALT_START}),te[oe-1].push({type:"altEnd",signalType:ce.LINETYPE.ALT_END}),this.$=te[oe-1];break;case 38:te[oe-1].unshift({type:"parStart",parText:ce.parseMessage(te[oe-2]),signalType:ce.LINETYPE.PAR_START}),te[oe-1].push({type:"parEnd",signalType:ce.LINETYPE.PAR_END}),this.$=te[oe-1];break;case 39:te[oe-1].unshift({type:"parStart",parText:ce.parseMessage(te[oe-2]),signalType:ce.LINETYPE.PAR_OVER_START}),te[oe-1].push({type:"parEnd",signalType:ce.LINETYPE.PAR_END}),this.$=te[oe-1];break;case 40:te[oe-1].unshift({type:"criticalStart",criticalText:ce.parseMessage(te[oe-2]),signalType:ce.LINETYPE.CRITICAL_START}),te[oe-1].push({type:"criticalEnd",signalType:ce.LINETYPE.CRITICAL_END}),this.$=te[oe-1];break;case 41:te[oe-1].unshift({type:"breakStart",breakText:ce.parseMessage(te[oe-2]),signalType:ce.LINETYPE.BREAK_START}),te[oe-1].push({type:"breakEnd",optText:ce.parseMessage(te[oe-2]),signalType:ce.LINETYPE.BREAK_END}),this.$=te[oe-1];break;case 43:this.$=te[oe-3].concat([{type:"option",optionText:ce.parseMessage(te[oe-1]),signalType:ce.LINETYPE.CRITICAL_OPTION},te[oe]]);break;case 45:this.$=te[oe-3].concat([{type:"and",parText:ce.parseMessage(te[oe-1]),signalType:ce.LINETYPE.PAR_AND},te[oe]]);break;case 47:this.$=te[oe-3].concat([{type:"else",altText:ce.parseMessage(te[oe-1]),signalType:ce.LINETYPE.ALT_ELSE},te[oe]]);break;case 48:te[oe-3].draw="participant",te[oe-3].type="addParticipant",te[oe-3].description=ce.parseMessage(te[oe-1]),this.$=te[oe-3];break;case 49:te[oe-1].draw="participant",te[oe-1].type="addParticipant",this.$=te[oe-1];break;case 50:te[oe-3].draw="actor",te[oe-3].type="addParticipant",te[oe-3].description=ce.parseMessage(te[oe-1]),this.$=te[oe-3];break;case 51:te[oe-1].draw="actor",te[oe-1].type="addParticipant",this.$=te[oe-1];break;case 52:te[oe-1].type="destroyParticipant",this.$=te[oe-1];break;case 53:this.$=[te[oe-1],{type:"addNote",placement:te[oe-2],actor:te[oe-1].actor,text:te[oe]}];break;case 54:te[oe-2]=[].concat(te[oe-1],te[oe-1]).slice(0,2),te[oe-2][0]=te[oe-2][0].actor,te[oe-2][1]=te[oe-2][1].actor,this.$=[te[oe-1],{type:"addNote",placement:ce.PLACEMENT.OVER,actor:te[oe-2].slice(0,2),text:te[oe]}];break;case 55:this.$=[te[oe-1],{type:"addLinks",actor:te[oe-1].actor,text:te[oe]}];break;case 56:this.$=[te[oe-1],{type:"addALink",actor:te[oe-1].actor,text:te[oe]}];break;case 57:this.$=[te[oe-1],{type:"addProperties",actor:te[oe-1].actor,text:te[oe]}];break;case 58:this.$=[te[oe-1],{type:"addDetails",actor:te[oe-1].actor,text:te[oe]}];break;case 61:this.$=[te[oe-2],te[oe]];break;case 62:this.$=te[oe];break;case 63:this.$=ce.PLACEMENT.LEFTOF;break;case 64:this.$=ce.PLACEMENT.RIGHTOF;break;case 65:this.$=[te[oe-4],te[oe-1],{type:"addMessage",from:te[oe-4].actor,to:te[oe-1].actor,signalType:te[oe-3],msg:te[oe],activate:!0},{type:"activeStart",signalType:ce.LINETYPE.ACTIVE_START,actor:te[oe-1].actor}];break;case 66:this.$=[te[oe-4],te[oe-1],{type:"addMessage",from:te[oe-4].actor,to:te[oe-1].actor,signalType:te[oe-3],msg:te[oe]},{type:"activeEnd",signalType:ce.LINETYPE.ACTIVE_END,actor:te[oe-4].actor}];break;case 67:this.$=[te[oe-3],te[oe-1],{type:"addMessage",from:te[oe-3].actor,to:te[oe-1].actor,signalType:te[oe-2],msg:te[oe]}];break;case 68:this.$={type:"addParticipant",actor:te[oe]};break;case 69:this.$=ce.LINETYPE.SOLID_OPEN;break;case 70:this.$=ce.LINETYPE.DOTTED_OPEN;break;case 71:this.$=ce.LINETYPE.SOLID;break;case 72:this.$=ce.LINETYPE.BIDIRECTIONAL_SOLID;break;case 73:this.$=ce.LINETYPE.DOTTED;break;case 74:this.$=ce.LINETYPE.BIDIRECTIONAL_DOTTED;break;case 75:this.$=ce.LINETYPE.SOLID_CROSS;break;case 76:this.$=ce.LINETYPE.DOTTED_CROSS;break;case 77:this.$=ce.LINETYPE.SOLID_POINT;break;case 78:this.$=ce.LINETYPE.DOTTED_POINT;break;case 79:this.$=ce.parseMessage(te[oe].trim().substring(1));break}},"anonymous"),table:[{3:1,4:e,5:r,6:n},{1:[3]},{3:5,4:e,5:r,6:n},{3:6,4:e,5:r,6:n},t([1,4,5,13,14,18,21,23,29,30,31,33,35,36,37,38,39,41,43,44,46,50,52,53,54,59,60,61,62,70],i,{7:7}),{1:[2,1]},{1:[2,2]},{1:[2,3],4:a,5:s,8:8,9:10,12:12,13:l,14:u,17:15,18:h,21:f,22:40,23:d,24:19,25:20,26:21,27:22,28:23,29:p,30:m,31:g,33:y,35:v,36:x,37:b,38:w,39:S,41:T,43:E,44:_,46:A,50:L,52:M,53:N,54:k,59:I,60:C,61:O,62:D,70:P},t(F,[2,5]),{9:47,12:12,13:l,14:u,17:15,18:h,21:f,22:40,23:d,24:19,25:20,26:21,27:22,28:23,29:p,30:m,31:g,33:y,35:v,36:x,37:b,38:w,39:S,41:T,43:E,44:_,46:A,50:L,52:M,53:N,54:k,59:I,60:C,61:O,62:D,70:P},t(F,[2,7]),t(F,[2,8]),t(F,[2,14]),{12:48,50:L,52:M,53:N},{15:[1,49]},{5:[1,50]},{5:[1,53],19:[1,51],20:[1,52]},{22:54,70:P},{22:55,70:P},{5:[1,56]},{5:[1,57]},{5:[1,58]},{5:[1,59]},{5:[1,60]},t(F,[2,29]),t(F,[2,30]),{32:[1,61]},{34:[1,62]},t(F,[2,33]),{15:[1,63]},{15:[1,64]},{15:[1,65]},{15:[1,66]},{15:[1,67]},{15:[1,68]},{15:[1,69]},{15:[1,70]},{22:71,70:P},{22:72,70:P},{22:73,70:P},{67:74,71:[1,75],72:[1,76],73:[1,77],74:[1,78],75:[1,79],76:[1,80],77:[1,81],78:[1,82],79:[1,83],80:[1,84]},{55:85,57:[1,86],65:[1,87],66:[1,88]},{22:89,70:P},{22:90,70:P},{22:91,70:P},{22:92,70:P},t([5,51,64,71,72,73,74,75,76,77,78,79,80,81],[2,68]),t(F,[2,6]),t(F,[2,15]),t(B,[2,9],{10:93}),t(F,[2,17]),{5:[1,95],19:[1,94]},{5:[1,96]},t(F,[2,21]),{5:[1,97]},{5:[1,98]},t(F,[2,24]),t(F,[2,25]),t(F,[2,26]),t(F,[2,27]),t(F,[2,28]),t(F,[2,31]),t(F,[2,32]),t($,i,{7:99}),t($,i,{7:100}),t($,i,{7:101}),t(z,i,{40:102,7:103}),t(Y,i,{42:104,7:105}),t(Y,i,{7:105,42:106}),t(Q,i,{45:107,7:108}),t($,i,{7:109}),{5:[1,111],51:[1,110]},{5:[1,113],51:[1,112]},{5:[1,114]},{22:117,68:[1,115],69:[1,116],70:P},t(X,[2,69]),t(X,[2,70]),t(X,[2,71]),t(X,[2,72]),t(X,[2,73]),t(X,[2,74]),t(X,[2,75]),t(X,[2,76]),t(X,[2,77]),t(X,[2,78]),{22:118,70:P},{22:120,58:119,70:P},{70:[2,63]},{70:[2,64]},{56:121,81:ie},{56:123,81:ie},{56:124,81:ie},{56:125,81:ie},{4:[1,128],5:[1,130],11:127,12:129,16:[1,126],50:L,52:M,53:N},{5:[1,131]},t(F,[2,19]),t(F,[2,20]),t(F,[2,22]),t(F,[2,23]),{4:a,5:s,8:8,9:10,12:12,13:l,14:u,16:[1,132],17:15,18:h,21:f,22:40,23:d,24:19,25:20,26:21,27:22,28:23,29:p,30:m,31:g,33:y,35:v,36:x,37:b,38:w,39:S,41:T,43:E,44:_,46:A,50:L,52:M,53:N,54:k,59:I,60:C,61:O,62:D,70:P},{4:a,5:s,8:8,9:10,12:12,13:l,14:u,16:[1,133],17:15,18:h,21:f,22:40,23:d,24:19,25:20,26:21,27:22,28:23,29:p,30:m,31:g,33:y,35:v,36:x,37:b,38:w,39:S,41:T,43:E,44:_,46:A,50:L,52:M,53:N,54:k,59:I,60:C,61:O,62:D,70:P},{4:a,5:s,8:8,9:10,12:12,13:l,14:u,16:[1,134],17:15,18:h,21:f,22:40,23:d,24:19,25:20,26:21,27:22,28:23,29:p,30:m,31:g,33:y,35:v,36:x,37:b,38:w,39:S,41:T,43:E,44:_,46:A,50:L,52:M,53:N,54:k,59:I,60:C,61:O,62:D,70:P},{16:[1,135]},{4:a,5:s,8:8,9:10,12:12,13:l,14:u,16:[2,46],17:15,18:h,21:f,22:40,23:d,24:19,25:20,26:21,27:22,28:23,29:p,30:m,31:g,33:y,35:v,36:x,37:b,38:w,39:S,41:T,43:E,44:_,46:A,49:[1,136],50:L,52:M,53:N,54:k,59:I,60:C,61:O,62:D,70:P},{16:[1,137]},{4:a,5:s,8:8,9:10,12:12,13:l,14:u,16:[2,44],17:15,18:h,21:f,22:40,23:d,24:19,25:20,26:21,27:22,28:23,29:p,30:m,31:g,33:y,35:v,36:x,37:b,38:w,39:S,41:T,43:E,44:_,46:A,48:[1,138],50:L,52:M,53:N,54:k,59:I,60:C,61:O,62:D,70:P},{16:[1,139]},{16:[1,140]},{4:a,5:s,8:8,9:10,12:12,13:l,14:u,16:[2,42],17:15,18:h,21:f,22:40,23:d,24:19,25:20,26:21,27:22,28:23,29:p,30:m,31:g,33:y,35:v,36:x,37:b,38:w,39:S,41:T,43:E,44:_,46:A,47:[1,141],50:L,52:M,53:N,54:k,59:I,60:C,61:O,62:D,70:P},{4:a,5:s,8:8,9:10,12:12,13:l,14:u,16:[1,142],17:15,18:h,21:f,22:40,23:d,24:19,25:20,26:21,27:22,28:23,29:p,30:m,31:g,33:y,35:v,36:x,37:b,38:w,39:S,41:T,43:E,44:_,46:A,50:L,52:M,53:N,54:k,59:I,60:C,61:O,62:D,70:P},{15:[1,143]},t(F,[2,49]),{15:[1,144]},t(F,[2,51]),t(F,[2,52]),{22:145,70:P},{22:146,70:P},{56:147,81:ie},{56:148,81:ie},{56:149,81:ie},{64:[1,150],81:[2,62]},{5:[2,55]},{5:[2,79]},{5:[2,56]},{5:[2,57]},{5:[2,58]},t(F,[2,16]),t(B,[2,10]),{12:151,50:L,52:M,53:N},t(B,[2,12]),t(B,[2,13]),t(F,[2,18]),t(F,[2,34]),t(F,[2,35]),t(F,[2,36]),t(F,[2,37]),{15:[1,152]},t(F,[2,38]),{15:[1,153]},t(F,[2,39]),t(F,[2,40]),{15:[1,154]},t(F,[2,41]),{5:[1,155]},{5:[1,156]},{56:157,81:ie},{56:158,81:ie},{5:[2,67]},{5:[2,53]},{5:[2,54]},{22:159,70:P},t(B,[2,11]),t(z,i,{7:103,40:160}),t(Y,i,{7:105,42:161}),t(Q,i,{7:108,45:162}),t(F,[2,48]),t(F,[2,50]),{5:[2,65]},{5:[2,66]},{81:[2,61]},{16:[2,47]},{16:[2,45]},{16:[2,43]}],defaultActions:{5:[2,1],6:[2,2],87:[2,63],88:[2,64],121:[2,55],122:[2,79],123:[2,56],124:[2,57],125:[2,58],147:[2,67],148:[2,53],149:[2,54],157:[2,65],158:[2,66],159:[2,61],160:[2,47],161:[2,45],162:[2,43]},parseError:o(function(q,K){if(K.recoverable)this.trace(q);else{var se=new Error(q);throw se.hash=K,se}},"parseError"),parse:o(function(q){var K=this,se=[0],ce=[],ue=[null],te=[],De=this.table,oe="",ke=0,Ie=0,Se=0,Ue=2,Pe=1,_e=te.slice.call(arguments,1),me=Object.create(this.lexer),W={yy:{}};for(var fe in this.yy)Object.prototype.hasOwnProperty.call(this.yy,fe)&&(W.yy[fe]=this.yy[fe]);me.setInput(q,W.yy),W.yy.lexer=me,W.yy.parser=this,typeof me.yylloc>"u"&&(me.yylloc={});var ge=me.yylloc;te.push(ge);var re=me.options&&me.options.ranges;typeof W.yy.parseError=="function"?this.parseError=W.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function he(yt){se.length=se.length-2*yt,ue.length=ue.length-yt,te.length=te.length-yt}o(he,"popStack");function ne(){var yt;return yt=ce.pop()||me.lex()||Pe,typeof yt!="number"&&(yt instanceof Array&&(ce=yt,yt=ce.pop()),yt=K.symbols_[yt]||yt),yt}o(ne,"lex");for(var ae,we,Te,Ce,Ae,Ge,Me={},ye,He,ze,Ze;;){if(Te=se[se.length-1],this.defaultActions[Te]?Ce=this.defaultActions[Te]:((ae===null||typeof ae>"u")&&(ae=ne()),Ce=De[Te]&&De[Te][ae]),typeof Ce>"u"||!Ce.length||!Ce[0]){var gt="";Ze=[];for(ye in De[Te])this.terminals_[ye]&&ye>Ue&&Ze.push("'"+this.terminals_[ye]+"'");me.showPosition?gt="Parse error on line "+(ke+1)+`: +`+me.showPosition()+` +Expecting `+Ze.join(", ")+", got '"+(this.terminals_[ae]||ae)+"'":gt="Parse error on line "+(ke+1)+": Unexpected "+(ae==Pe?"end of input":"'"+(this.terminals_[ae]||ae)+"'"),this.parseError(gt,{text:me.match,token:this.terminals_[ae]||ae,line:me.yylineno,loc:ge,expected:Ze})}if(Ce[0]instanceof Array&&Ce.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Te+", token: "+ae);switch(Ce[0]){case 1:se.push(ae),ue.push(me.yytext),te.push(me.yylloc),se.push(Ce[1]),ae=null,we?(ae=we,we=null):(Ie=me.yyleng,oe=me.yytext,ke=me.yylineno,ge=me.yylloc,Se>0&&Se--);break;case 2:if(He=this.productions_[Ce[1]][1],Me.$=ue[ue.length-He],Me._$={first_line:te[te.length-(He||1)].first_line,last_line:te[te.length-1].last_line,first_column:te[te.length-(He||1)].first_column,last_column:te[te.length-1].last_column},re&&(Me._$.range=[te[te.length-(He||1)].range[0],te[te.length-1].range[1]]),Ge=this.performAction.apply(Me,[oe,Ie,ke,W.yy,Ce[1],ue,te].concat(_e)),typeof Ge<"u")return Ge;He&&(se=se.slice(0,-1*He*2),ue=ue.slice(0,-1*He),te=te.slice(0,-1*He)),se.push(this.productions_[Ce[1]][0]),ue.push(Me.$),te.push(Me._$),ze=De[se[se.length-2]][se[se.length-1]],se.push(ze);break;case 3:return!0}}return!0},"parse")},J=function(){var H={EOF:1,parseError:o(function(K,se){if(this.yy.parser)this.yy.parser.parseError(K,se);else throw new Error(K)},"parseError"),setInput:o(function(q,K){return this.yy=K||this.yy||{},this._input=q,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var q=this._input[0];this.yytext+=q,this.yyleng++,this.offset++,this.match+=q,this.matched+=q;var K=q.match(/(?:\r\n?|\n).*/g);return K?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),q},"input"),unput:o(function(q){var K=q.length,se=q.split(/(?:\r\n?|\n)/g);this._input=q+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-K),this.offset-=K;var ce=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),se.length-1&&(this.yylineno-=se.length-1);var ue=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:se?(se.length===ce.length?this.yylloc.first_column:0)+ce[ce.length-se.length].length-se[0].length:this.yylloc.first_column-K},this.options.ranges&&(this.yylloc.range=[ue[0],ue[0]+this.yyleng-K]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(q){this.unput(this.match.slice(q))},"less"),pastInput:o(function(){var q=this.matched.substr(0,this.matched.length-this.match.length);return(q.length>20?"...":"")+q.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var q=this.match;return q.length<20&&(q+=this._input.substr(0,20-q.length)),(q.substr(0,20)+(q.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var q=this.pastInput(),K=new Array(q.length+1).join("-");return q+this.upcomingInput()+` +`+K+"^"},"showPosition"),test_match:o(function(q,K){var se,ce,ue;if(this.options.backtrack_lexer&&(ue={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(ue.yylloc.range=this.yylloc.range.slice(0))),ce=q[0].match(/(?:\r\n?|\n).*/g),ce&&(this.yylineno+=ce.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:ce?ce[ce.length-1].length-ce[ce.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+q[0].length},this.yytext+=q[0],this.match+=q[0],this.matches=q,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(q[0].length),this.matched+=q[0],se=this.performAction.call(this,this.yy,this,K,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),se)return se;if(this._backtrack){for(var te in ue)this[te]=ue[te];return!1}return!1},"test_match"),next:o(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var q,K,se,ce;this._more||(this.yytext="",this.match="");for(var ue=this._currentRules(),te=0;teK[0].length)){if(K=se,ce=te,this.options.backtrack_lexer){if(q=this.test_match(se,ue[te]),q!==!1)return q;if(this._backtrack){K=!1;continue}else return!1}else if(!this.options.flex)break}return K?(q=this.test_match(K,ue[ce]),q!==!1?q:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:o(function(){var K=this.next();return K||this.lex()},"lex"),begin:o(function(K){this.conditionStack.push(K)},"begin"),popState:o(function(){var K=this.conditionStack.length-1;return K>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:o(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:o(function(K){return K=this.conditionStack.length-1-Math.abs(K||0),K>=0?this.conditionStack[K]:"INITIAL"},"topState"),pushState:o(function(K){this.begin(K)},"pushState"),stateStackSize:o(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:o(function(K,se,ce,ue){var te=ue;switch(ce){case 0:return 5;case 1:break;case 2:break;case 3:break;case 4:break;case 5:break;case 6:return 19;case 7:return this.begin("LINE"),14;break;case 8:return this.begin("ID"),50;break;case 9:return this.begin("ID"),52;break;case 10:return 13;case 11:return this.begin("ID"),53;break;case 12:return se.yytext=se.yytext.trim(),this.begin("ALIAS"),70;break;case 13:return this.popState(),this.popState(),this.begin("LINE"),51;break;case 14:return this.popState(),this.popState(),5;break;case 15:return this.begin("LINE"),36;break;case 16:return this.begin("LINE"),37;break;case 17:return this.begin("LINE"),38;break;case 18:return this.begin("LINE"),39;break;case 19:return this.begin("LINE"),49;break;case 20:return this.begin("LINE"),41;break;case 21:return this.begin("LINE"),43;break;case 22:return this.begin("LINE"),48;break;case 23:return this.begin("LINE"),44;break;case 24:return this.begin("LINE"),47;break;case 25:return this.begin("LINE"),46;break;case 26:return this.popState(),15;break;case 27:return 16;case 28:return 65;case 29:return 66;case 30:return 59;case 31:return 60;case 32:return 61;case 33:return 62;case 34:return 57;case 35:return 54;case 36:return this.begin("ID"),21;break;case 37:return this.begin("ID"),23;break;case 38:return 29;case 39:return 30;case 40:return this.begin("acc_title"),31;break;case 41:return this.popState(),"acc_title_value";break;case 42:return this.begin("acc_descr"),33;break;case 43:return this.popState(),"acc_descr_value";break;case 44:this.begin("acc_descr_multiline");break;case 45:this.popState();break;case 46:return"acc_descr_multiline_value";case 47:return 6;case 48:return 18;case 49:return 20;case 50:return 64;case 51:return 5;case 52:return se.yytext=se.yytext.trim(),70;break;case 53:return 73;case 54:return 74;case 55:return 75;case 56:return 76;case 57:return 71;case 58:return 72;case 59:return 77;case 60:return 78;case 61:return 79;case 62:return 80;case 63:return 81;case 64:return 68;case 65:return 69;case 66:return 5;case 67:return"INVALID"}},"anonymous"),rules:[/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:((?!\n)\s)+)/i,/^(?:#[^\n]*)/i,/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[0-9]+(?=[ \n]+))/i,/^(?:box\b)/i,/^(?:participant\b)/i,/^(?:actor\b)/i,/^(?:create\b)/i,/^(?:destroy\b)/i,/^(?:[^\<->\->:\n,;]+?([\-]*[^\<->\->:\n,;]+?)*?(?=((?!\n)\s)+as(?!\n)\s|[#\n;]|$))/i,/^(?:as\b)/i,/^(?:(?:))/i,/^(?:loop\b)/i,/^(?:rect\b)/i,/^(?:opt\b)/i,/^(?:alt\b)/i,/^(?:else\b)/i,/^(?:par\b)/i,/^(?:par_over\b)/i,/^(?:and\b)/i,/^(?:critical\b)/i,/^(?:option\b)/i,/^(?:break\b)/i,/^(?:(?:[:]?(?:no)?wrap)?[^#\n;]*)/i,/^(?:end\b)/i,/^(?:left of\b)/i,/^(?:right of\b)/i,/^(?:links\b)/i,/^(?:link\b)/i,/^(?:properties\b)/i,/^(?:details\b)/i,/^(?:over\b)/i,/^(?:note\b)/i,/^(?:activate\b)/i,/^(?:deactivate\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:title:\s[^#\n;]+)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?:sequenceDiagram\b)/i,/^(?:autonumber\b)/i,/^(?:off\b)/i,/^(?:,)/i,/^(?:;)/i,/^(?:[^\+\<->\->:\n,;]+((?!(-x|--x|-\)|--\)))[\-]*[^\+\<->\->:\n,;]+)*)/i,/^(?:->>)/i,/^(?:<<->>)/i,/^(?:-->>)/i,/^(?:<<-->>)/i,/^(?:->)/i,/^(?:-->)/i,/^(?:-[x])/i,/^(?:--[x])/i,/^(?:-[\)])/i,/^(?:--[\)])/i,/^(?::(?:(?:no)?wrap)?[^#\n;]+)/i,/^(?:\+)/i,/^(?:-)/i,/^(?:$)/i,/^(?:.)/i],conditions:{acc_descr_multiline:{rules:[45,46],inclusive:!1},acc_descr:{rules:[43],inclusive:!1},acc_title:{rules:[41],inclusive:!1},ID:{rules:[2,3,12],inclusive:!1},ALIAS:{rules:[2,3,13,14],inclusive:!1},LINE:{rules:[2,3,26],inclusive:!1},INITIAL:{rules:[0,1,3,4,5,6,7,8,9,10,11,15,16,17,18,19,20,21,22,23,24,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,42,44,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67],inclusive:!0}}};return H}();j.lexer=J;function Z(){this.yy={}}return o(Z,"Parser"),Z.prototype=j,j.Parser=Z,new Z}();hO.parser=hO;Wue=hO});function dO(t,e){if(t.links==null)t.links=e;else for(let r in e)t.links[r]=e[r]}function Zue(t,e){if(t.properties==null)t.properties=e;else for(let r in e)t.properties[r]=e[r]}function SGe(){Mt.records.currentBox=void 0}var Mt,aGe,fO,sGe,oGe,pi,lGe,cGe,uGe,hGe,fGe,dGe,pGe,xx,mGe,gGe,yGe,vGe,xGe,Xue,A0,bGe,wGe,TGe,vx,kGe,EGe,jue,Kue,CGe,Que,Jue,AGe,ehe,pO,the=R(()=>{"use strict";_t();ut();Jk();rr();bi();Mt=new uf(()=>({prevActor:void 0,actors:new Map,createdActors:new Map,destroyedActors:new Map,boxes:[],messages:[],notes:[],sequenceNumbersEnabled:!1,wrapEnabled:void 0,currentBox:void 0,lastCreated:void 0,lastDestroyed:void 0})),aGe=o(function(t){Mt.records.boxes.push({name:t.text,wrap:t.wrap??A0(),fill:t.color,actorKeys:[]}),Mt.records.currentBox=Mt.records.boxes.slice(-1)[0]},"addBox"),fO=o(function(t,e,r,n){let i=Mt.records.currentBox,a=Mt.records.actors.get(t);if(a){if(Mt.records.currentBox&&a.box&&Mt.records.currentBox!==a.box)throw new Error(`A same participant should only be defined in one Box: ${a.name} can't be in '${a.box.name}' and in '${Mt.records.currentBox.name}' at the same time.`);if(i=a.box?a.box:Mt.records.currentBox,a.box=i,a&&e===a.name&&r==null)return}if(r?.text==null&&(r={text:e,type:n}),(n==null||r.text==null)&&(r={text:e,type:n}),Mt.records.actors.set(t,{box:i,name:e,description:r.text,wrap:r.wrap??A0(),prevActor:Mt.records.prevActor,links:{},properties:{},actorCnt:null,rectData:null,type:n??"participant"}),Mt.records.prevActor){let s=Mt.records.actors.get(Mt.records.prevActor);s&&(s.nextActor=t)}Mt.records.currentBox&&Mt.records.currentBox.actorKeys.push(t),Mt.records.prevActor=t},"addActor"),sGe=o(t=>{let e,r=0;if(!t)return 0;for(e=0;e>-",token:"->>-",line:"1",loc:{first_line:1,last_line:1,first_column:1,last_column:1},expected:["'ACTIVE_PARTICIPANT'"]},s}return Mt.records.messages.push({from:t,to:e,message:r?.text??"",wrap:r?.wrap??A0(),type:n,activate:i}),!0},"addSignal"),lGe=o(function(){return Mt.records.boxes.length>0},"hasAtLeastOneBox"),cGe=o(function(){return Mt.records.boxes.some(t=>t.name)},"hasAtLeastOneBoxWithTitle"),uGe=o(function(){return Mt.records.messages},"getMessages"),hGe=o(function(){return Mt.records.boxes},"getBoxes"),fGe=o(function(){return Mt.records.actors},"getActors"),dGe=o(function(){return Mt.records.createdActors},"getCreatedActors"),pGe=o(function(){return Mt.records.destroyedActors},"getDestroyedActors"),xx=o(function(t){return Mt.records.actors.get(t)},"getActor"),mGe=o(function(){return[...Mt.records.actors.keys()]},"getActorKeys"),gGe=o(function(){Mt.records.sequenceNumbersEnabled=!0},"enableSequenceNumbers"),yGe=o(function(){Mt.records.sequenceNumbersEnabled=!1},"disableSequenceNumbers"),vGe=o(()=>Mt.records.sequenceNumbersEnabled,"showSequenceNumbers"),xGe=o(function(t){Mt.records.wrapEnabled=t},"setWrap"),Xue=o(t=>{if(t===void 0)return{};t=t.trim();let e=/^:?wrap:/.exec(t)!==null?!0:/^:?nowrap:/.exec(t)!==null?!1:void 0;return{cleanedText:(e===void 0?t:t.replace(/^:?(?:no)?wrap:/,"")).trim(),wrap:e}},"extractWrap"),A0=o(()=>Mt.records.wrapEnabled!==void 0?Mt.records.wrapEnabled:de().sequence?.wrap??!1,"autoWrap"),bGe=o(function(){Mt.reset(),vr()},"clear"),wGe=o(function(t){let e=t.trim(),{wrap:r,cleanedText:n}=Xue(e),i={text:n,wrap:r};return V.debug(`parseMessage: ${JSON.stringify(i)}`),i},"parseMessage"),TGe=o(function(t){let e=/^((?:rgba?|hsla?)\s*\(.*\)|\w*)(.*)$/.exec(t),r=e?.[1]?e[1].trim():"transparent",n=e?.[2]?e[2].trim():void 0;if(window?.CSS)window.CSS.supports("color",r)||(r="transparent",n=t.trim());else{let s=new Option().style;s.color=r,s.color!==r&&(r="transparent",n=t.trim())}let{wrap:i,cleanedText:a}=Xue(n);return{text:a?qr(a,de()):void 0,color:r,wrap:i}},"parseBoxData"),vx={SOLID:0,DOTTED:1,NOTE:2,SOLID_CROSS:3,DOTTED_CROSS:4,SOLID_OPEN:5,DOTTED_OPEN:6,LOOP_START:10,LOOP_END:11,ALT_START:12,ALT_ELSE:13,ALT_END:14,OPT_START:15,OPT_END:16,ACTIVE_START:17,ACTIVE_END:18,PAR_START:19,PAR_AND:20,PAR_END:21,RECT_START:22,RECT_END:23,SOLID_POINT:24,DOTTED_POINT:25,AUTONUMBER:26,CRITICAL_START:27,CRITICAL_OPTION:28,CRITICAL_END:29,BREAK_START:30,BREAK_END:31,PAR_OVER_START:32,BIDIRECTIONAL_SOLID:33,BIDIRECTIONAL_DOTTED:34},kGe={FILLED:0,OPEN:1},EGe={LEFTOF:0,RIGHTOF:1,OVER:2},jue=o(function(t,e,r){let n={actor:t,placement:e,message:r.text,wrap:r.wrap??A0()},i=[].concat(t,t);Mt.records.notes.push(n),Mt.records.messages.push({from:i[0],to:i[1],message:r.text,wrap:r.wrap??A0(),type:vx.NOTE,placement:e})},"addNote"),Kue=o(function(t,e){let r=xx(t);try{let n=qr(e.text,de());n=n.replace(/&/g,"&"),n=n.replace(/=/g,"=");let i=JSON.parse(n);dO(r,i)}catch(n){V.error("error while parsing actor link text",n)}},"addLinks"),CGe=o(function(t,e){let r=xx(t);try{let n={},i=qr(e.text,de()),a=i.indexOf("@");i=i.replace(/&/g,"&"),i=i.replace(/=/g,"=");let s=i.slice(0,a-1).trim(),l=i.slice(a+1).trim();n[s]=l,dO(r,n)}catch(n){V.error("error while parsing actor link text",n)}},"addALink");o(dO,"insertLinks");Que=o(function(t,e){let r=xx(t);try{let n=qr(e.text,de()),i=JSON.parse(n);Zue(r,i)}catch(n){V.error("error while parsing actor properties text",n)}},"addProperties");o(Zue,"insertProperties");o(SGe,"boxEnd");Jue=o(function(t,e){let r=xx(t),n=document.getElementById(e.text);try{let i=n.innerHTML,a=JSON.parse(i);a.properties&&Zue(r,a.properties),a.links&&dO(r,a.links)}catch(i){V.error("error while parsing actor details text",i)}},"addDetails"),AGe=o(function(t,e){if(t?.properties!==void 0)return t.properties[e]},"getActorProperty"),ehe=o(function(t){if(Array.isArray(t))t.forEach(function(e){ehe(e)});else switch(t.type){case"sequenceIndex":Mt.records.messages.push({from:void 0,to:void 0,message:{start:t.sequenceIndex,step:t.sequenceIndexStep,visible:t.sequenceVisible},wrap:!1,type:t.signalType});break;case"addParticipant":fO(t.actor,t.actor,t.description,t.draw);break;case"createParticipant":if(Mt.records.actors.has(t.actor))throw new Error("It is not possible to have actors with the same id, even if one is destroyed before the next is created. Use 'AS' aliases to simulate the behavior");Mt.records.lastCreated=t.actor,fO(t.actor,t.actor,t.description,t.draw),Mt.records.createdActors.set(t.actor,Mt.records.messages.length);break;case"destroyParticipant":Mt.records.lastDestroyed=t.actor,Mt.records.destroyedActors.set(t.actor,Mt.records.messages.length);break;case"activeStart":pi(t.actor,void 0,void 0,t.signalType);break;case"activeEnd":pi(t.actor,void 0,void 0,t.signalType);break;case"addNote":jue(t.actor,t.placement,t.text);break;case"addLinks":Kue(t.actor,t.text);break;case"addALink":CGe(t.actor,t.text);break;case"addProperties":Que(t.actor,t.text);break;case"addDetails":Jue(t.actor,t.text);break;case"addMessage":if(Mt.records.lastCreated){if(t.to!==Mt.records.lastCreated)throw new Error("The created participant "+Mt.records.lastCreated.name+" does not have an associated creating message after its declaration. Please check the sequence diagram.");Mt.records.lastCreated=void 0}else if(Mt.records.lastDestroyed){if(t.to!==Mt.records.lastDestroyed&&t.from!==Mt.records.lastDestroyed)throw new Error("The destroyed participant "+Mt.records.lastDestroyed.name+" does not have an associated destroying message after its declaration. Please check the sequence diagram.");Mt.records.lastDestroyed=void 0}pi(t.from,t.to,t.msg,t.signalType,t.activate);break;case"boxStart":aGe(t.boxData);break;case"boxEnd":SGe();break;case"loopStart":pi(void 0,void 0,t.loopText,t.signalType);break;case"loopEnd":pi(void 0,void 0,void 0,t.signalType);break;case"rectStart":pi(void 0,void 0,t.color,t.signalType);break;case"rectEnd":pi(void 0,void 0,void 0,t.signalType);break;case"optStart":pi(void 0,void 0,t.optText,t.signalType);break;case"optEnd":pi(void 0,void 0,void 0,t.signalType);break;case"altStart":pi(void 0,void 0,t.altText,t.signalType);break;case"else":pi(void 0,void 0,t.altText,t.signalType);break;case"altEnd":pi(void 0,void 0,void 0,t.signalType);break;case"setAccTitle":kr(t.text);break;case"parStart":pi(void 0,void 0,t.parText,t.signalType);break;case"and":pi(void 0,void 0,t.parText,t.signalType);break;case"parEnd":pi(void 0,void 0,void 0,t.signalType);break;case"criticalStart":pi(void 0,void 0,t.criticalText,t.signalType);break;case"option":pi(void 0,void 0,t.optionText,t.signalType);break;case"criticalEnd":pi(void 0,void 0,void 0,t.signalType);break;case"breakStart":pi(void 0,void 0,t.breakText,t.signalType);break;case"breakEnd":pi(void 0,void 0,void 0,t.signalType);break}},"apply"),pO={addActor:fO,addMessage:oGe,addSignal:pi,addLinks:Kue,addDetails:Jue,addProperties:Que,autoWrap:A0,setWrap:xGe,enableSequenceNumbers:gGe,disableSequenceNumbers:yGe,showSequenceNumbers:vGe,getMessages:uGe,getActors:fGe,getCreatedActors:dGe,getDestroyedActors:pGe,getActor:xx,getActorKeys:mGe,getActorProperty:AGe,getAccTitle:Ar,getBoxes:hGe,getDiagramTitle:Xr,setDiagramTitle:nn,getConfig:o(()=>de().sequence,"getConfig"),clear:bGe,parseMessage:wGe,parseBoxData:TGe,LINETYPE:vx,ARROWTYPE:kGe,PLACEMENT:EGe,addNote:jue,setAccTitle:kr,apply:ehe,setAccDescription:_r,getAccDescription:Lr,hasAtLeastOneBox:lGe,hasAtLeastOneBoxWithTitle:cGe}});var _Ge,rhe,nhe=R(()=>{"use strict";_Ge=o(t=>`.actor { + stroke: ${t.actorBorder}; + fill: ${t.actorBkg}; + } + + text.actor > tspan { + fill: ${t.actorTextColor}; + stroke: none; + } + + .actor-line { + stroke: ${t.actorLineColor}; + } + + .messageLine0 { + stroke-width: 1.5; + stroke-dasharray: none; + stroke: ${t.signalColor}; + } + + .messageLine1 { + stroke-width: 1.5; + stroke-dasharray: 2, 2; + stroke: ${t.signalColor}; + } + + #arrowhead path { + fill: ${t.signalColor}; + stroke: ${t.signalColor}; + } + + .sequenceNumber { + fill: ${t.sequenceNumberColor}; + } + + #sequencenumber { + fill: ${t.signalColor}; + } + + #crosshead path { + fill: ${t.signalColor}; + stroke: ${t.signalColor}; + } + + .messageText { + fill: ${t.signalTextColor}; + stroke: none; + } + + .labelBox { + stroke: ${t.labelBoxBorderColor}; + fill: ${t.labelBoxBkgColor}; + } + + .labelText, .labelText > tspan { + fill: ${t.labelTextColor}; + stroke: none; + } + + .loopText, .loopText > tspan { + fill: ${t.loopTextColor}; + stroke: none; + } + + .loopLine { + stroke-width: 2px; + stroke-dasharray: 2, 2; + stroke: ${t.labelBoxBorderColor}; + fill: ${t.labelBoxBorderColor}; + } + + .note { + //stroke: #decc93; + stroke: ${t.noteBorderColor}; + fill: ${t.noteBkgColor}; + } + + .noteText, .noteText > tspan { + fill: ${t.noteTextColor}; + stroke: none; + } + + .activation0 { + fill: ${t.activationBkgColor}; + stroke: ${t.activationBorderColor}; + } + + .activation1 { + fill: ${t.activationBkgColor}; + stroke: ${t.activationBorderColor}; + } + + .activation2 { + fill: ${t.activationBkgColor}; + stroke: ${t.activationBorderColor}; + } + + .actorPopupMenu { + position: absolute; + } + + .actorPopupMenuPanel { + position: absolute; + fill: ${t.actorBkg}; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + filter: drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4)); +} + .actor-man line { + stroke: ${t.actorBorder}; + fill: ${t.actorBkg}; + } + .actor-man circle, line { + stroke: ${t.actorBorder}; + fill: ${t.actorBkg}; + stroke-width: 2px; + } +`,"getStyles"),rhe=_Ge});var mO,gf,ahe,she,LGe,ihe,gO,DGe,RGe,bx,_0,ohe,Bc,yO,NGe,MGe,IGe,OGe,PGe,BGe,FGe,lhe,zGe,GGe,$Ge,VGe,UGe,HGe,YGe,che,WGe,vO,qGe,si,uhe=R(()=>{"use strict";rr();Qy();xr();mO=Xi(Up(),1);qs();gf=18*2,ahe="actor-top",she="actor-bottom",LGe="actor-box",ihe="actor-man",gO=o(function(t,e){return yd(t,e)},"drawRect"),DGe=o(function(t,e,r,n,i){if(e.links===void 0||e.links===null||Object.keys(e.links).length===0)return{height:0,width:0};let a=e.links,s=e.actorCnt,l=e.rectData;var u="none";i&&(u="block !important");let h=t.append("g");h.attr("id","actor"+s+"_popup"),h.attr("class","actorPopupMenu"),h.attr("display",u);var f="";l.class!==void 0&&(f=" "+l.class);let d=l.width>r?l.width:r,p=h.append("rect");if(p.attr("class","actorPopupMenuPanel"+f),p.attr("x",l.x),p.attr("y",l.height),p.attr("fill",l.fill),p.attr("stroke",l.stroke),p.attr("width",d),p.attr("height",l.height),p.attr("rx",l.rx),p.attr("ry",l.ry),a!=null){var m=20;for(let v in a){var g=h.append("a"),y=(0,mO.sanitizeUrl)(a[v]);g.attr("xlink:href",y),g.attr("target","_blank"),qGe(n)(v,g,l.x+10,l.height+m,d,20,{class:"actor"},n),m+=30}}return p.attr("height",m),{height:l.height+m,width:d}},"drawPopup"),RGe=o(function(t){return"var pu = document.getElementById('"+t+"'); if (pu != null) { pu.style.display = pu.style.display == 'block' ? 'none' : 'block'; }"},"popupMenuToggle"),bx=o(async function(t,e,r=null){let n=t.append("foreignObject"),i=await yh(e.text,Or()),s=n.append("xhtml:div").attr("style","width: fit-content;").attr("xmlns","http://www.w3.org/1999/xhtml").html(i).node().getBoundingClientRect();if(n.attr("height",Math.round(s.height)).attr("width",Math.round(s.width)),e.class==="noteText"){let l=t.node().firstChild;l.setAttribute("height",s.height+2*e.textMargin);let u=l.getBBox();n.attr("x",Math.round(u.x+u.width/2-s.width/2)).attr("y",Math.round(u.y+u.height/2-s.height/2))}else if(r){let{startx:l,stopx:u,starty:h}=r;if(l>u){let f=l;l=u,u=f}n.attr("x",Math.round(l+Math.abs(l-u)/2-s.width/2)),e.class==="loopText"?n.attr("y",Math.round(h)):n.attr("y",Math.round(h-s.height))}return[n]},"drawKatex"),_0=o(function(t,e){let r=0,n=0,i=e.text.split(We.lineBreakRegex),[a,s]=mc(e.fontSize),l=[],u=0,h=o(()=>e.y,"yfunc");if(e.valign!==void 0&&e.textMargin!==void 0&&e.textMargin>0)switch(e.valign){case"top":case"start":h=o(()=>Math.round(e.y+e.textMargin),"yfunc");break;case"middle":case"center":h=o(()=>Math.round(e.y+(r+n+e.textMargin)/2),"yfunc");break;case"bottom":case"end":h=o(()=>Math.round(e.y+(r+n+2*e.textMargin)-e.textMargin),"yfunc");break}if(e.anchor!==void 0&&e.textMargin!==void 0&&e.width!==void 0)switch(e.anchor){case"left":case"start":e.x=Math.round(e.x+e.textMargin),e.anchor="start",e.dominantBaseline="middle",e.alignmentBaseline="middle";break;case"middle":case"center":e.x=Math.round(e.x+e.width/2),e.anchor="middle",e.dominantBaseline="middle",e.alignmentBaseline="middle";break;case"right":case"end":e.x=Math.round(e.x+e.width-e.textMargin),e.anchor="end",e.dominantBaseline="middle",e.alignmentBaseline="middle";break}for(let[f,d]of i.entries()){e.textMargin!==void 0&&e.textMargin===0&&a!==void 0&&(u=f*a);let p=t.append("text");p.attr("x",e.x),p.attr("y",h()),e.anchor!==void 0&&p.attr("text-anchor",e.anchor).attr("dominant-baseline",e.dominantBaseline).attr("alignment-baseline",e.alignmentBaseline),e.fontFamily!==void 0&&p.style("font-family",e.fontFamily),s!==void 0&&p.style("font-size",s),e.fontWeight!==void 0&&p.style("font-weight",e.fontWeight),e.fill!==void 0&&p.attr("fill",e.fill),e.class!==void 0&&p.attr("class",e.class),e.dy!==void 0?p.attr("dy",e.dy):u!==0&&p.attr("dy",u);let m=d||K_;if(e.tspan){let g=p.append("tspan");g.attr("x",e.x),e.fill!==void 0&&g.attr("fill",e.fill),g.text(m)}else p.text(m);e.valign!==void 0&&e.textMargin!==void 0&&e.textMargin>0&&(n+=(p._groups||p)[0][0].getBBox().height,r=n),l.push(p)}return l},"drawText"),ohe=o(function(t,e){function r(i,a,s,l,u){return i+","+a+" "+(i+s)+","+a+" "+(i+s)+","+(a+l-u)+" "+(i+s-u*1.2)+","+(a+l)+" "+i+","+(a+l)}o(r,"genPoints");let n=t.append("polygon");return n.attr("points",r(e.x,e.y,e.width,e.height,7)),n.attr("class","labelBox"),e.y=e.y+e.height/2,_0(t,e),n},"drawLabel"),Bc=-1,yO=o((t,e,r,n)=>{t.select&&r.forEach(i=>{let a=e.get(i),s=t.select("#actor"+a.actorCnt);!n.mirrorActors&&a.stopy?s.attr("y2",a.stopy+a.height/2):n.mirrorActors&&s.attr("y2",a.stopy)})},"fixLifeLineHeights"),NGe=o(function(t,e,r,n){let i=n?e.stopy:e.starty,a=e.x+e.width/2,s=i+e.height,l=t.append("g").lower();var u=l;n||(Bc++,Object.keys(e.links||{}).length&&!r.forceMenus&&u.attr("onclick",RGe(`actor${Bc}_popup`)).attr("cursor","pointer"),u.append("line").attr("id","actor"+Bc).attr("x1",a).attr("y1",s).attr("x2",a).attr("y2",2e3).attr("class","actor-line 200").attr("stroke-width","0.5px").attr("stroke","#999").attr("name",e.name),u=l.append("g"),e.actorCnt=Bc,e.links!=null&&u.attr("id","root-"+Bc));let h=wl();var f="actor";e.properties?.class?f=e.properties.class:h.fill="#eaeaea",n?f+=` ${she}`:f+=` ${ahe}`,h.x=e.x,h.y=i,h.width=e.width,h.height=e.height,h.class=f,h.rx=3,h.ry=3,h.name=e.name;let d=gO(u,h);if(e.rectData=h,e.properties?.icon){let m=e.properties.icon.trim();m.charAt(0)==="@"?EW(u,h.x+h.width-20,h.y+10,m.substr(1)):kW(u,h.x+h.width-20,h.y+10,m)}vO(r,Ni(e.description))(e.description,u,h.x,h.y,h.width,h.height,{class:`actor ${LGe}`},r);let p=e.height;if(d.node){let m=d.node().getBBox();e.height=m.height,p=m.height}return p},"drawActorTypeParticipant"),MGe=o(function(t,e,r,n){let i=n?e.stopy:e.starty,a=e.x+e.width/2,s=i+80,l=t.append("g").lower();n||(Bc++,l.append("line").attr("id","actor"+Bc).attr("x1",a).attr("y1",s).attr("x2",a).attr("y2",2e3).attr("class","actor-line 200").attr("stroke-width","0.5px").attr("stroke","#999").attr("name",e.name),e.actorCnt=Bc);let u=t.append("g"),h=ihe;n?h+=` ${she}`:h+=` ${ahe}`,u.attr("class",h),u.attr("name",e.name);let f=wl();f.x=e.x,f.y=i,f.fill="#eaeaea",f.width=e.width,f.height=e.height,f.class="actor",f.rx=3,f.ry=3,u.append("line").attr("id","actor-man-torso"+Bc).attr("x1",a).attr("y1",i+25).attr("x2",a).attr("y2",i+45),u.append("line").attr("id","actor-man-arms"+Bc).attr("x1",a-gf/2).attr("y1",i+33).attr("x2",a+gf/2).attr("y2",i+33),u.append("line").attr("x1",a-gf/2).attr("y1",i+60).attr("x2",a).attr("y2",i+45),u.append("line").attr("x1",a).attr("y1",i+45).attr("x2",a+gf/2-2).attr("y2",i+60);let d=u.append("circle");d.attr("cx",e.x+e.width/2),d.attr("cy",i+10),d.attr("r",15),d.attr("width",e.width),d.attr("height",e.height);let p=u.node().getBBox();return e.height=p.height,vO(r,Ni(e.description))(e.description,u,f.x,f.y+35,f.width,f.height,{class:`actor ${ihe}`},r),e.height},"drawActorTypeActor"),IGe=o(async function(t,e,r,n){switch(e.type){case"actor":return await MGe(t,e,r,n);case"participant":return await NGe(t,e,r,n)}},"drawActor"),OGe=o(function(t,e,r){let i=t.append("g");lhe(i,e),e.name&&vO(r)(e.name,i,e.x,e.y+(e.textMaxHeight||0)/2,e.width,0,{class:"text"},r),i.lower()},"drawBox"),PGe=o(function(t){return t.append("g")},"anchorElement"),BGe=o(function(t,e,r,n,i){let a=wl(),s=e.anchored;a.x=e.startx,a.y=e.starty,a.class="activation"+i%3,a.width=e.stopx-e.startx,a.height=r-e.starty,gO(s,a)},"drawActivation"),FGe=o(async function(t,e,r,n){let{boxMargin:i,boxTextMargin:a,labelBoxHeight:s,labelBoxWidth:l,messageFontFamily:u,messageFontSize:h,messageFontWeight:f}=n,d=t.append("g"),p=o(function(y,v,x,b){return d.append("line").attr("x1",y).attr("y1",v).attr("x2",x).attr("y2",b).attr("class","loopLine")},"drawLoopLine");p(e.startx,e.starty,e.stopx,e.starty),p(e.stopx,e.starty,e.stopx,e.stopy),p(e.startx,e.stopy,e.stopx,e.stopy),p(e.startx,e.starty,e.startx,e.stopy),e.sections!==void 0&&e.sections.forEach(function(y){p(e.startx,y.y,e.stopx,y.y).style("stroke-dasharray","3, 3")});let m=Ky();m.text=r,m.x=e.startx,m.y=e.starty,m.fontFamily=u,m.fontSize=h,m.fontWeight=f,m.anchor="middle",m.valign="middle",m.tspan=!1,m.width=l||50,m.height=s||20,m.textMargin=a,m.class="labelText",ohe(d,m),m=che(),m.text=e.title,m.x=e.startx+l/2+(e.stopx-e.startx)/2,m.y=e.starty+i+a,m.anchor="middle",m.valign="middle",m.textMargin=a,m.class="loopText",m.fontFamily=u,m.fontSize=h,m.fontWeight=f,m.wrap=!0;let g=Ni(m.text)?await bx(d,m,e):_0(d,m);if(e.sectionTitles!==void 0){for(let[y,v]of Object.entries(e.sectionTitles))if(v.message){m.text=v.message,m.x=e.startx+(e.stopx-e.startx)/2,m.y=e.sections[y].y+i+a,m.class="loopText",m.anchor="middle",m.valign="middle",m.tspan=!1,m.fontFamily=u,m.fontSize=h,m.fontWeight=f,m.wrap=e.wrap,Ni(m.text)?(e.starty=e.sections[y].y,await bx(d,m,e)):_0(d,m);let x=Math.round(g.map(b=>(b._groups||b)[0][0].getBBox().height).reduce((b,w)=>b+w));e.sections[y].height+=x-(i+a)}}return e.height=Math.round(e.stopy-e.starty),d},"drawLoop"),lhe=o(function(t,e){j3(t,e)},"drawBackgroundRect"),zGe=o(function(t){t.append("defs").append("symbol").attr("id","database").attr("fill-rule","evenodd").attr("clip-rule","evenodd").append("path").attr("transform","scale(.5)").attr("d","M12.258.001l.256.004.255.005.253.008.251.01.249.012.247.015.246.016.242.019.241.02.239.023.236.024.233.027.231.028.229.031.225.032.223.034.22.036.217.038.214.04.211.041.208.043.205.045.201.046.198.048.194.05.191.051.187.053.183.054.18.056.175.057.172.059.168.06.163.061.16.063.155.064.15.066.074.033.073.033.071.034.07.034.069.035.068.035.067.035.066.035.064.036.064.036.062.036.06.036.06.037.058.037.058.037.055.038.055.038.053.038.052.038.051.039.05.039.048.039.047.039.045.04.044.04.043.04.041.04.04.041.039.041.037.041.036.041.034.041.033.042.032.042.03.042.029.042.027.042.026.043.024.043.023.043.021.043.02.043.018.044.017.043.015.044.013.044.012.044.011.045.009.044.007.045.006.045.004.045.002.045.001.045v17l-.001.045-.002.045-.004.045-.006.045-.007.045-.009.044-.011.045-.012.044-.013.044-.015.044-.017.043-.018.044-.02.043-.021.043-.023.043-.024.043-.026.043-.027.042-.029.042-.03.042-.032.042-.033.042-.034.041-.036.041-.037.041-.039.041-.04.041-.041.04-.043.04-.044.04-.045.04-.047.039-.048.039-.05.039-.051.039-.052.038-.053.038-.055.038-.055.038-.058.037-.058.037-.06.037-.06.036-.062.036-.064.036-.064.036-.066.035-.067.035-.068.035-.069.035-.07.034-.071.034-.073.033-.074.033-.15.066-.155.064-.16.063-.163.061-.168.06-.172.059-.175.057-.18.056-.183.054-.187.053-.191.051-.194.05-.198.048-.201.046-.205.045-.208.043-.211.041-.214.04-.217.038-.22.036-.223.034-.225.032-.229.031-.231.028-.233.027-.236.024-.239.023-.241.02-.242.019-.246.016-.247.015-.249.012-.251.01-.253.008-.255.005-.256.004-.258.001-.258-.001-.256-.004-.255-.005-.253-.008-.251-.01-.249-.012-.247-.015-.245-.016-.243-.019-.241-.02-.238-.023-.236-.024-.234-.027-.231-.028-.228-.031-.226-.032-.223-.034-.22-.036-.217-.038-.214-.04-.211-.041-.208-.043-.204-.045-.201-.046-.198-.048-.195-.05-.19-.051-.187-.053-.184-.054-.179-.056-.176-.057-.172-.059-.167-.06-.164-.061-.159-.063-.155-.064-.151-.066-.074-.033-.072-.033-.072-.034-.07-.034-.069-.035-.068-.035-.067-.035-.066-.035-.064-.036-.063-.036-.062-.036-.061-.036-.06-.037-.058-.037-.057-.037-.056-.038-.055-.038-.053-.038-.052-.038-.051-.039-.049-.039-.049-.039-.046-.039-.046-.04-.044-.04-.043-.04-.041-.04-.04-.041-.039-.041-.037-.041-.036-.041-.034-.041-.033-.042-.032-.042-.03-.042-.029-.042-.027-.042-.026-.043-.024-.043-.023-.043-.021-.043-.02-.043-.018-.044-.017-.043-.015-.044-.013-.044-.012-.044-.011-.045-.009-.044-.007-.045-.006-.045-.004-.045-.002-.045-.001-.045v-17l.001-.045.002-.045.004-.045.006-.045.007-.045.009-.044.011-.045.012-.044.013-.044.015-.044.017-.043.018-.044.02-.043.021-.043.023-.043.024-.043.026-.043.027-.042.029-.042.03-.042.032-.042.033-.042.034-.041.036-.041.037-.041.039-.041.04-.041.041-.04.043-.04.044-.04.046-.04.046-.039.049-.039.049-.039.051-.039.052-.038.053-.038.055-.038.056-.038.057-.037.058-.037.06-.037.061-.036.062-.036.063-.036.064-.036.066-.035.067-.035.068-.035.069-.035.07-.034.072-.034.072-.033.074-.033.151-.066.155-.064.159-.063.164-.061.167-.06.172-.059.176-.057.179-.056.184-.054.187-.053.19-.051.195-.05.198-.048.201-.046.204-.045.208-.043.211-.041.214-.04.217-.038.22-.036.223-.034.226-.032.228-.031.231-.028.234-.027.236-.024.238-.023.241-.02.243-.019.245-.016.247-.015.249-.012.251-.01.253-.008.255-.005.256-.004.258-.001.258.001zm-9.258 20.499v.01l.001.021.003.021.004.022.005.021.006.022.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.023.018.024.019.024.021.024.022.025.023.024.024.025.052.049.056.05.061.051.066.051.07.051.075.051.079.052.084.052.088.052.092.052.097.052.102.051.105.052.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.048.144.049.147.047.152.047.155.047.16.045.163.045.167.043.171.043.176.041.178.041.183.039.187.039.19.037.194.035.197.035.202.033.204.031.209.03.212.029.216.027.219.025.222.024.226.021.23.02.233.018.236.016.24.015.243.012.246.01.249.008.253.005.256.004.259.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.021.224-.024.22-.026.216-.027.212-.028.21-.031.205-.031.202-.034.198-.034.194-.036.191-.037.187-.039.183-.04.179-.04.175-.042.172-.043.168-.044.163-.045.16-.046.155-.046.152-.047.148-.048.143-.049.139-.049.136-.05.131-.05.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.053.083-.051.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.05.023-.024.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.023.01-.022.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.127l-.077.055-.08.053-.083.054-.085.053-.087.052-.09.052-.093.051-.095.05-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.045-.118.044-.12.043-.122.042-.124.042-.126.041-.128.04-.13.04-.132.038-.134.038-.135.037-.138.037-.139.035-.142.035-.143.034-.144.033-.147.032-.148.031-.15.03-.151.03-.153.029-.154.027-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.01-.179.008-.179.008-.181.006-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.006-.179-.008-.179-.008-.178-.01-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.027-.153-.029-.151-.03-.15-.03-.148-.031-.146-.032-.145-.033-.143-.034-.141-.035-.14-.035-.137-.037-.136-.037-.134-.038-.132-.038-.13-.04-.128-.04-.126-.041-.124-.042-.122-.042-.12-.044-.117-.043-.116-.045-.113-.045-.112-.046-.109-.047-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.05-.093-.052-.09-.051-.087-.052-.085-.053-.083-.054-.08-.054-.077-.054v4.127zm0-5.654v.011l.001.021.003.021.004.021.005.022.006.022.007.022.009.022.01.022.011.023.012.023.013.023.015.024.016.023.017.024.018.024.019.024.021.024.022.024.023.025.024.024.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.052.11.051.114.051.119.052.123.05.127.051.131.05.135.049.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.044.171.042.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.022.23.02.233.018.236.016.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.012.241-.015.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.048.139-.05.136-.049.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.051.051-.049.023-.025.023-.024.021-.025.02-.024.019-.024.018-.024.017-.024.015-.023.014-.023.013-.024.012-.022.01-.023.01-.023.008-.022.006-.022.006-.022.004-.021.004-.022.001-.021.001-.021v-4.139l-.077.054-.08.054-.083.054-.085.052-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.044-.118.044-.12.044-.122.042-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.035-.143.033-.144.033-.147.033-.148.031-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.009-.179.009-.179.007-.181.007-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.007-.179-.007-.179-.009-.178-.009-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.031-.146-.033-.145-.033-.143-.033-.141-.035-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.04-.126-.041-.124-.042-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.051-.093-.051-.09-.051-.087-.053-.085-.052-.083-.054-.08-.054-.077-.054v4.139zm0-5.666v.011l.001.02.003.022.004.021.005.022.006.021.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.024.018.023.019.024.021.025.022.024.023.024.024.025.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.051.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.043.171.043.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.021.23.02.233.018.236.017.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.013.241-.014.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.049.139-.049.136-.049.131-.051.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.049.023-.025.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.022.01-.023.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.153l-.077.054-.08.054-.083.053-.085.053-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.048-.105.048-.106.048-.109.046-.111.046-.114.046-.115.044-.118.044-.12.043-.122.043-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.034-.143.034-.144.033-.147.032-.148.032-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.024-.161.024-.162.023-.163.023-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.01-.178.01-.179.009-.179.007-.181.006-.182.006-.182.004-.184.003-.184.001-.185.001-.185-.001-.184-.001-.184-.003-.182-.004-.182-.006-.181-.006-.179-.007-.179-.009-.178-.01-.176-.01-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.023-.162-.023-.161-.024-.159-.024-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.032-.146-.032-.145-.033-.143-.034-.141-.034-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.041-.126-.041-.124-.041-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.048-.105-.048-.102-.048-.1-.05-.097-.049-.095-.051-.093-.051-.09-.052-.087-.052-.085-.053-.083-.053-.08-.054-.077-.054v4.153zm8.74-8.179l-.257.004-.254.005-.25.008-.247.011-.244.012-.241.014-.237.016-.233.018-.231.021-.226.022-.224.023-.22.026-.216.027-.212.028-.21.031-.205.032-.202.033-.198.034-.194.036-.191.038-.187.038-.183.04-.179.041-.175.042-.172.043-.168.043-.163.045-.16.046-.155.046-.152.048-.148.048-.143.048-.139.049-.136.05-.131.05-.126.051-.123.051-.118.051-.114.052-.11.052-.106.052-.101.052-.096.052-.092.052-.088.052-.083.052-.079.052-.074.051-.07.052-.065.051-.06.05-.056.05-.051.05-.023.025-.023.024-.021.024-.02.025-.019.024-.018.024-.017.023-.015.024-.014.023-.013.023-.012.023-.01.023-.01.022-.008.022-.006.023-.006.021-.004.022-.004.021-.001.021-.001.021.001.021.001.021.004.021.004.022.006.021.006.023.008.022.01.022.01.023.012.023.013.023.014.023.015.024.017.023.018.024.019.024.02.025.021.024.023.024.023.025.051.05.056.05.06.05.065.051.07.052.074.051.079.052.083.052.088.052.092.052.096.052.101.052.106.052.11.052.114.052.118.051.123.051.126.051.131.05.136.05.139.049.143.048.148.048.152.048.155.046.16.046.163.045.168.043.172.043.175.042.179.041.183.04.187.038.191.038.194.036.198.034.202.033.205.032.21.031.212.028.216.027.22.026.224.023.226.022.231.021.233.018.237.016.241.014.244.012.247.011.25.008.254.005.257.004.26.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.022.224-.023.22-.026.216-.027.212-.028.21-.031.205-.032.202-.033.198-.034.194-.036.191-.038.187-.038.183-.04.179-.041.175-.042.172-.043.168-.043.163-.045.16-.046.155-.046.152-.048.148-.048.143-.048.139-.049.136-.05.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.05.051-.05.023-.025.023-.024.021-.024.02-.025.019-.024.018-.024.017-.023.015-.024.014-.023.013-.023.012-.023.01-.023.01-.022.008-.022.006-.023.006-.021.004-.022.004-.021.001-.021.001-.021-.001-.021-.001-.021-.004-.021-.004-.022-.006-.021-.006-.023-.008-.022-.01-.022-.01-.023-.012-.023-.013-.023-.014-.023-.015-.024-.017-.023-.018-.024-.019-.024-.02-.025-.021-.024-.023-.024-.023-.025-.051-.05-.056-.05-.06-.05-.065-.051-.07-.052-.074-.051-.079-.052-.083-.052-.088-.052-.092-.052-.096-.052-.101-.052-.106-.052-.11-.052-.114-.052-.118-.051-.123-.051-.126-.051-.131-.05-.136-.05-.139-.049-.143-.048-.148-.048-.152-.048-.155-.046-.16-.046-.163-.045-.168-.043-.172-.043-.175-.042-.179-.041-.183-.04-.187-.038-.191-.038-.194-.036-.198-.034-.202-.033-.205-.032-.21-.031-.212-.028-.216-.027-.22-.026-.224-.023-.226-.022-.231-.021-.233-.018-.237-.016-.241-.014-.244-.012-.247-.011-.25-.008-.254-.005-.257-.004-.26-.001-.26.001z")},"insertDatabaseIcon"),GGe=o(function(t){t.append("defs").append("symbol").attr("id","computer").attr("width","24").attr("height","24").append("path").attr("transform","scale(.5)").attr("d","M2 2v13h20v-13h-20zm18 11h-16v-9h16v9zm-10.228 6l.466-1h3.524l.467 1h-4.457zm14.228 3h-24l2-6h2.104l-1.33 4h18.45l-1.297-4h2.073l2 6zm-5-10h-14v-7h14v7z")},"insertComputerIcon"),$Ge=o(function(t){t.append("defs").append("symbol").attr("id","clock").attr("width","24").attr("height","24").append("path").attr("transform","scale(.5)").attr("d","M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5.848 12.459c.202.038.202.333.001.372-1.907.361-6.045 1.111-6.547 1.111-.719 0-1.301-.582-1.301-1.301 0-.512.77-5.447 1.125-7.445.034-.192.312-.181.343.014l.985 6.238 5.394 1.011z")},"insertClockIcon"),VGe=o(function(t){t.append("defs").append("marker").attr("id","arrowhead").attr("refX",7.9).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto-start-reverse").append("path").attr("d","M -1 0 L 10 5 L 0 10 z")},"insertArrowHead"),UGe=o(function(t){t.append("defs").append("marker").attr("id","filled-head").attr("refX",15.5).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},"insertArrowFilledHead"),HGe=o(function(t){t.append("defs").append("marker").attr("id","sequencenumber").attr("refX",15).attr("refY",15).attr("markerWidth",60).attr("markerHeight",40).attr("orient","auto").append("circle").attr("cx",15).attr("cy",15).attr("r",6)},"insertSequenceNumber"),YGe=o(function(t){t.append("defs").append("marker").attr("id","crosshead").attr("markerWidth",15).attr("markerHeight",8).attr("orient","auto").attr("refX",4).attr("refY",4.5).append("path").attr("fill","none").attr("stroke","#000000").style("stroke-dasharray","0, 0").attr("stroke-width","1pt").attr("d","M 1,2 L 6,7 M 6,2 L 1,7")},"insertArrowCrossHead"),che=o(function(){return{x:0,y:0,fill:void 0,anchor:void 0,style:"#666",width:void 0,height:void 0,textMargin:0,rx:0,ry:0,tspan:!0,valign:void 0}},"getTextObj"),WGe=o(function(){return{x:0,y:0,fill:"#EDF2AE",stroke:"#666",width:100,anchor:"start",height:100,rx:0,ry:0}},"getNoteRect"),vO=function(){function t(a,s,l,u,h,f,d){let p=s.append("text").attr("x",l+h/2).attr("y",u+f/2+5).style("text-anchor","middle").text(a);i(p,d)}o(t,"byText");function e(a,s,l,u,h,f,d,p){let{actorFontSize:m,actorFontFamily:g,actorFontWeight:y}=p,[v,x]=mc(m),b=a.split(We.lineBreakRegex);for(let w=0;w{let s=L0(Ne),l=a.actorKeys.reduce((f,d)=>f+=t.get(d).width+(t.get(d).margin||0),0);l-=2*Ne.boxTextMargin,a.wrap&&(a.name=Lt.wrapLabel(a.name,l-2*Ne.wrapPadding,s));let u=Lt.calculateTextDimensions(a.name,s);i=We.getMax(u.height,i);let h=We.getMax(l,u.width+2*Ne.wrapPadding);if(a.margin=Ne.boxTextMargin,la.textMaxHeight=i),We.getMax(n,Ne.height)}var Ne,Ke,XGe,L0,Pg,xO,KGe,QGe,bO,fhe,dhe,bE,hhe,JGe,t$e,n$e,i$e,a$e,phe,mhe=R(()=>{"use strict";Zt();uhe();ut();rr();Qy();_t();cp();xr();Yn();Ne={},Ke={data:{startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},verticalPos:0,sequenceItems:[],activations:[],models:{getHeight:o(function(){return Math.max.apply(null,this.actors.length===0?[0]:this.actors.map(t=>t.height||0))+(this.loops.length===0?0:this.loops.map(t=>t.height||0).reduce((t,e)=>t+e))+(this.messages.length===0?0:this.messages.map(t=>t.height||0).reduce((t,e)=>t+e))+(this.notes.length===0?0:this.notes.map(t=>t.height||0).reduce((t,e)=>t+e))},"getHeight"),clear:o(function(){this.actors=[],this.boxes=[],this.loops=[],this.messages=[],this.notes=[]},"clear"),addBox:o(function(t){this.boxes.push(t)},"addBox"),addActor:o(function(t){this.actors.push(t)},"addActor"),addLoop:o(function(t){this.loops.push(t)},"addLoop"),addMessage:o(function(t){this.messages.push(t)},"addMessage"),addNote:o(function(t){this.notes.push(t)},"addNote"),lastActor:o(function(){return this.actors[this.actors.length-1]},"lastActor"),lastLoop:o(function(){return this.loops[this.loops.length-1]},"lastLoop"),lastMessage:o(function(){return this.messages[this.messages.length-1]},"lastMessage"),lastNote:o(function(){return this.notes[this.notes.length-1]},"lastNote"),actors:[],boxes:[],loops:[],messages:[],notes:[]},init:o(function(){this.sequenceItems=[],this.activations=[],this.models.clear(),this.data={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},this.verticalPos=0,dhe(de())},"init"),updateVal:o(function(t,e,r,n){t[e]===void 0?t[e]=r:t[e]=n(r,t[e])},"updateVal"),updateBounds:o(function(t,e,r,n){let i=this,a=0;function s(l){return o(function(h){a++;let f=i.sequenceItems.length-a+1;i.updateVal(h,"starty",e-f*Ne.boxMargin,Math.min),i.updateVal(h,"stopy",n+f*Ne.boxMargin,Math.max),i.updateVal(Ke.data,"startx",t-f*Ne.boxMargin,Math.min),i.updateVal(Ke.data,"stopx",r+f*Ne.boxMargin,Math.max),l!=="activation"&&(i.updateVal(h,"startx",t-f*Ne.boxMargin,Math.min),i.updateVal(h,"stopx",r+f*Ne.boxMargin,Math.max),i.updateVal(Ke.data,"starty",e-f*Ne.boxMargin,Math.min),i.updateVal(Ke.data,"stopy",n+f*Ne.boxMargin,Math.max))},"updateItemBounds")}o(s,"updateFn"),this.sequenceItems.forEach(s()),this.activations.forEach(s("activation"))},"updateBounds"),insert:o(function(t,e,r,n){let i=We.getMin(t,r),a=We.getMax(t,r),s=We.getMin(e,n),l=We.getMax(e,n);this.updateVal(Ke.data,"startx",i,Math.min),this.updateVal(Ke.data,"starty",s,Math.min),this.updateVal(Ke.data,"stopx",a,Math.max),this.updateVal(Ke.data,"stopy",l,Math.max),this.updateBounds(i,s,a,l)},"insert"),newActivation:o(function(t,e,r){let n=r.get(t.from),i=bE(t.from).length||0,a=n.x+n.width/2+(i-1)*Ne.activationWidth/2;this.activations.push({startx:a,starty:this.verticalPos+2,stopx:a+Ne.activationWidth,stopy:void 0,actor:t.from,anchored:si.anchorElement(e)})},"newActivation"),endActivation:o(function(t){let e=this.activations.map(function(r){return r.actor}).lastIndexOf(t.from);return this.activations.splice(e,1)[0]},"endActivation"),createLoop:o(function(t={message:void 0,wrap:!1,width:void 0},e){return{startx:void 0,starty:this.verticalPos,stopx:void 0,stopy:void 0,title:t.message,wrap:t.wrap,width:t.width,height:0,fill:e}},"createLoop"),newLoop:o(function(t={message:void 0,wrap:!1,width:void 0},e){this.sequenceItems.push(this.createLoop(t,e))},"newLoop"),endLoop:o(function(){return this.sequenceItems.pop()},"endLoop"),isLoopOverlap:o(function(){return this.sequenceItems.length?this.sequenceItems[this.sequenceItems.length-1].overlap:!1},"isLoopOverlap"),addSectionToLoop:o(function(t){let e=this.sequenceItems.pop();e.sections=e.sections||[],e.sectionTitles=e.sectionTitles||[],e.sections.push({y:Ke.getVerticalPos(),height:0}),e.sectionTitles.push(t),this.sequenceItems.push(e)},"addSectionToLoop"),saveVerticalPos:o(function(){this.isLoopOverlap()&&(this.savedVerticalPos=this.verticalPos)},"saveVerticalPos"),resetVerticalPos:o(function(){this.isLoopOverlap()&&(this.verticalPos=this.savedVerticalPos)},"resetVerticalPos"),bumpVerticalPos:o(function(t){this.verticalPos=this.verticalPos+t,this.data.stopy=We.getMax(this.data.stopy,this.verticalPos)},"bumpVerticalPos"),getVerticalPos:o(function(){return this.verticalPos},"getVerticalPos"),getBounds:o(function(){return{bounds:this.data,models:this.models}},"getBounds")},XGe=o(async function(t,e){Ke.bumpVerticalPos(Ne.boxMargin),e.height=Ne.boxMargin,e.starty=Ke.getVerticalPos();let r=wl();r.x=e.startx,r.y=e.starty,r.width=e.width||Ne.width,r.class="note";let n=t.append("g"),i=si.drawRect(n,r),a=Ky();a.x=e.startx,a.y=e.starty,a.width=r.width,a.dy="1em",a.text=e.message,a.class="noteText",a.fontFamily=Ne.noteFontFamily,a.fontSize=Ne.noteFontSize,a.fontWeight=Ne.noteFontWeight,a.anchor=Ne.noteAlign,a.textMargin=Ne.noteMargin,a.valign="center";let s=Ni(a.text)?await bx(n,a):_0(n,a),l=Math.round(s.map(u=>(u._groups||u)[0][0].getBBox().height).reduce((u,h)=>u+h));i.attr("height",l+2*Ne.noteMargin),e.height+=l+2*Ne.noteMargin,Ke.bumpVerticalPos(l+2*Ne.noteMargin),e.stopy=e.starty+l+2*Ne.noteMargin,e.stopx=e.startx+r.width,Ke.insert(e.startx,e.starty,e.stopx,e.stopy),Ke.models.addNote(e)},"drawNote"),L0=o(t=>({fontFamily:t.messageFontFamily,fontSize:t.messageFontSize,fontWeight:t.messageFontWeight}),"messageFont"),Pg=o(t=>({fontFamily:t.noteFontFamily,fontSize:t.noteFontSize,fontWeight:t.noteFontWeight}),"noteFont"),xO=o(t=>({fontFamily:t.actorFontFamily,fontSize:t.actorFontSize,fontWeight:t.actorFontWeight}),"actorFont");o(jGe,"boundMessage");KGe=o(async function(t,e,r,n){let{startx:i,stopx:a,starty:s,message:l,type:u,sequenceIndex:h,sequenceVisible:f}=e,d=Lt.calculateTextDimensions(l,L0(Ne)),p=Ky();p.x=i,p.y=s+10,p.width=a-i,p.class="messageText",p.dy="1em",p.text=l,p.fontFamily=Ne.messageFontFamily,p.fontSize=Ne.messageFontSize,p.fontWeight=Ne.messageFontWeight,p.anchor=Ne.messageAlign,p.valign="center",p.textMargin=Ne.wrapPadding,p.tspan=!1,Ni(p.text)?await bx(t,p,{startx:i,stopx:a,starty:r}):_0(t,p);let m=d.width,g;i===a?Ne.rightAngles?g=t.append("path").attr("d",`M ${i},${r} H ${i+We.getMax(Ne.width/2,m/2)} V ${r+25} H ${i}`):g=t.append("path").attr("d","M "+i+","+r+" C "+(i+60)+","+(r-10)+" "+(i+60)+","+(r+30)+" "+i+","+(r+20)):(g=t.append("line"),g.attr("x1",i),g.attr("y1",r),g.attr("x2",a),g.attr("y2",r)),u===n.db.LINETYPE.DOTTED||u===n.db.LINETYPE.DOTTED_CROSS||u===n.db.LINETYPE.DOTTED_POINT||u===n.db.LINETYPE.DOTTED_OPEN||u===n.db.LINETYPE.BIDIRECTIONAL_DOTTED?(g.style("stroke-dasharray","3, 3"),g.attr("class","messageLine1")):g.attr("class","messageLine0");let y="";Ne.arrowMarkerAbsolute&&(y=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search,y=y.replace(/\(/g,"\\("),y=y.replace(/\)/g,"\\)")),g.attr("stroke-width",2),g.attr("stroke","none"),g.style("fill","none"),(u===n.db.LINETYPE.SOLID||u===n.db.LINETYPE.DOTTED)&&g.attr("marker-end","url("+y+"#arrowhead)"),(u===n.db.LINETYPE.BIDIRECTIONAL_SOLID||u===n.db.LINETYPE.BIDIRECTIONAL_DOTTED)&&(g.attr("marker-start","url("+y+"#arrowhead)"),g.attr("marker-end","url("+y+"#arrowhead)")),(u===n.db.LINETYPE.SOLID_POINT||u===n.db.LINETYPE.DOTTED_POINT)&&g.attr("marker-end","url("+y+"#filled-head)"),(u===n.db.LINETYPE.SOLID_CROSS||u===n.db.LINETYPE.DOTTED_CROSS)&&g.attr("marker-end","url("+y+"#crosshead)"),(f||Ne.showSequenceNumbers)&&(g.attr("marker-start","url("+y+"#sequencenumber)"),t.append("text").attr("x",i).attr("y",r+4).attr("font-family","sans-serif").attr("font-size","12px").attr("text-anchor","middle").attr("class","sequenceNumber").text(h))},"drawMessage"),QGe=o(function(t,e,r,n,i,a,s){let l=0,u=0,h,f=0;for(let d of n){let p=e.get(d),m=p.box;h&&h!=m&&(s||Ke.models.addBox(h),u+=Ne.boxMargin+h.margin),m&&m!=h&&(s||(m.x=l+u,m.y=i),u+=m.margin),p.width=p.width||Ne.width,p.height=We.getMax(p.height||Ne.height,Ne.height),p.margin=p.margin||Ne.actorMargin,f=We.getMax(f,p.height),r.get(p.name)&&(u+=p.width/2),p.x=l+u,p.starty=Ke.getVerticalPos(),Ke.insert(p.x,i,p.x+p.width,p.height),l+=p.width+u,p.box&&(p.box.width=l+m.margin-p.box.x),u=p.margin,h=p.box,Ke.models.addActor(p)}h&&!s&&Ke.models.addBox(h),Ke.bumpVerticalPos(f)},"addActorRenderingData"),bO=o(async function(t,e,r,n){if(n){let i=0;Ke.bumpVerticalPos(Ne.boxMargin*2);for(let a of r){let s=e.get(a);s.stopy||(s.stopy=Ke.getVerticalPos());let l=await si.drawActor(t,s,Ne,!0);i=We.getMax(i,l)}Ke.bumpVerticalPos(i+Ne.boxMargin)}else for(let i of r){let a=e.get(i);await si.drawActor(t,a,Ne,!1)}},"drawActors"),fhe=o(function(t,e,r,n){let i=0,a=0;for(let s of r){let l=e.get(s),u=t$e(l),h=si.drawPopup(t,l,u,Ne,Ne.forceMenus,n);h.height>i&&(i=h.height),h.width+l.x>a&&(a=h.width+l.x)}return{maxHeight:i,maxWidth:a}},"drawActorsPopup"),dhe=o(function(t){On(Ne,t),t.fontFamily&&(Ne.actorFontFamily=Ne.noteFontFamily=Ne.messageFontFamily=t.fontFamily),t.fontSize&&(Ne.actorFontSize=Ne.noteFontSize=Ne.messageFontSize=t.fontSize),t.fontWeight&&(Ne.actorFontWeight=Ne.noteFontWeight=Ne.messageFontWeight=t.fontWeight)},"setConf"),bE=o(function(t){return Ke.activations.filter(function(e){return e.actor===t})},"actorActivations"),hhe=o(function(t,e){let r=e.get(t),n=bE(t),i=n.reduce(function(s,l){return We.getMin(s,l.startx)},r.x+r.width/2-1),a=n.reduce(function(s,l){return We.getMax(s,l.stopx)},r.x+r.width/2+1);return[i,a]},"activationBounds");o(Fc,"adjustLoopHeightForWrap");o(ZGe,"adjustCreatedDestroyedData");JGe=o(async function(t,e,r,n){let{securityLevel:i,sequence:a}=de();Ne=a;let s;i==="sandbox"&&(s=$e("#i"+e));let l=i==="sandbox"?$e(s.nodes()[0].contentDocument.body):$e("body"),u=i==="sandbox"?s.nodes()[0].contentDocument:document;Ke.init(),V.debug(n.db);let h=i==="sandbox"?l.select(`[id="${e}"]`):$e(`[id="${e}"]`),f=n.db.getActors(),d=n.db.getCreatedActors(),p=n.db.getDestroyedActors(),m=n.db.getBoxes(),g=n.db.getActorKeys(),y=n.db.getMessages(),v=n.db.getDiagramTitle(),x=n.db.hasAtLeastOneBox(),b=n.db.hasAtLeastOneBoxWithTitle(),w=await e$e(f,y,n);if(Ne.height=await r$e(f,w,m),si.insertComputerIcon(h),si.insertDatabaseIcon(h),si.insertClockIcon(h),x&&(Ke.bumpVerticalPos(Ne.boxMargin),b&&Ke.bumpVerticalPos(m[0].textMaxHeight)),Ne.hideUnusedParticipants===!0){let F=new Set;y.forEach(B=>{F.add(B.from),F.add(B.to)}),g=g.filter(B=>F.has(B))}QGe(h,f,d,g,0,y,!1);let S=await a$e(y,f,w,n);si.insertArrowHead(h),si.insertArrowCrossHead(h),si.insertArrowFilledHead(h),si.insertSequenceNumber(h);function T(F,B){let $=Ke.endActivation(F);$.starty+18>B&&($.starty=B-6,B+=12),si.drawActivation(h,$,B,Ne,bE(F.from).length),Ke.insert($.startx,B-10,$.stopx,B)}o(T,"activeEnd");let E=1,_=1,A=[],L=[],M=0;for(let F of y){let B,$,z;switch(F.type){case n.db.LINETYPE.NOTE:Ke.resetVerticalPos(),$=F.noteModel,await XGe(h,$);break;case n.db.LINETYPE.ACTIVE_START:Ke.newActivation(F,h,f);break;case n.db.LINETYPE.ACTIVE_END:T(F,Ke.getVerticalPos());break;case n.db.LINETYPE.LOOP_START:Fc(S,F,Ne.boxMargin,Ne.boxMargin+Ne.boxTextMargin,Y=>Ke.newLoop(Y));break;case n.db.LINETYPE.LOOP_END:B=Ke.endLoop(),await si.drawLoop(h,B,"loop",Ne),Ke.bumpVerticalPos(B.stopy-Ke.getVerticalPos()),Ke.models.addLoop(B);break;case n.db.LINETYPE.RECT_START:Fc(S,F,Ne.boxMargin,Ne.boxMargin,Y=>Ke.newLoop(void 0,Y.message));break;case n.db.LINETYPE.RECT_END:B=Ke.endLoop(),L.push(B),Ke.models.addLoop(B),Ke.bumpVerticalPos(B.stopy-Ke.getVerticalPos());break;case n.db.LINETYPE.OPT_START:Fc(S,F,Ne.boxMargin,Ne.boxMargin+Ne.boxTextMargin,Y=>Ke.newLoop(Y));break;case n.db.LINETYPE.OPT_END:B=Ke.endLoop(),await si.drawLoop(h,B,"opt",Ne),Ke.bumpVerticalPos(B.stopy-Ke.getVerticalPos()),Ke.models.addLoop(B);break;case n.db.LINETYPE.ALT_START:Fc(S,F,Ne.boxMargin,Ne.boxMargin+Ne.boxTextMargin,Y=>Ke.newLoop(Y));break;case n.db.LINETYPE.ALT_ELSE:Fc(S,F,Ne.boxMargin+Ne.boxTextMargin,Ne.boxMargin,Y=>Ke.addSectionToLoop(Y));break;case n.db.LINETYPE.ALT_END:B=Ke.endLoop(),await si.drawLoop(h,B,"alt",Ne),Ke.bumpVerticalPos(B.stopy-Ke.getVerticalPos()),Ke.models.addLoop(B);break;case n.db.LINETYPE.PAR_START:case n.db.LINETYPE.PAR_OVER_START:Fc(S,F,Ne.boxMargin,Ne.boxMargin+Ne.boxTextMargin,Y=>Ke.newLoop(Y)),Ke.saveVerticalPos();break;case n.db.LINETYPE.PAR_AND:Fc(S,F,Ne.boxMargin+Ne.boxTextMargin,Ne.boxMargin,Y=>Ke.addSectionToLoop(Y));break;case n.db.LINETYPE.PAR_END:B=Ke.endLoop(),await si.drawLoop(h,B,"par",Ne),Ke.bumpVerticalPos(B.stopy-Ke.getVerticalPos()),Ke.models.addLoop(B);break;case n.db.LINETYPE.AUTONUMBER:E=F.message.start||E,_=F.message.step||_,F.message.visible?n.db.enableSequenceNumbers():n.db.disableSequenceNumbers();break;case n.db.LINETYPE.CRITICAL_START:Fc(S,F,Ne.boxMargin,Ne.boxMargin+Ne.boxTextMargin,Y=>Ke.newLoop(Y));break;case n.db.LINETYPE.CRITICAL_OPTION:Fc(S,F,Ne.boxMargin+Ne.boxTextMargin,Ne.boxMargin,Y=>Ke.addSectionToLoop(Y));break;case n.db.LINETYPE.CRITICAL_END:B=Ke.endLoop(),await si.drawLoop(h,B,"critical",Ne),Ke.bumpVerticalPos(B.stopy-Ke.getVerticalPos()),Ke.models.addLoop(B);break;case n.db.LINETYPE.BREAK_START:Fc(S,F,Ne.boxMargin,Ne.boxMargin+Ne.boxTextMargin,Y=>Ke.newLoop(Y));break;case n.db.LINETYPE.BREAK_END:B=Ke.endLoop(),await si.drawLoop(h,B,"break",Ne),Ke.bumpVerticalPos(B.stopy-Ke.getVerticalPos()),Ke.models.addLoop(B);break;default:try{z=F.msgModel,z.starty=Ke.getVerticalPos(),z.sequenceIndex=E,z.sequenceVisible=n.db.showSequenceNumbers();let Y=await jGe(h,z);ZGe(F,z,Y,M,f,d,p),A.push({messageModel:z,lineStartY:Y}),Ke.models.addMessage(z)}catch(Y){V.error("error while drawing message",Y)}}[n.db.LINETYPE.SOLID_OPEN,n.db.LINETYPE.DOTTED_OPEN,n.db.LINETYPE.SOLID,n.db.LINETYPE.DOTTED,n.db.LINETYPE.SOLID_CROSS,n.db.LINETYPE.DOTTED_CROSS,n.db.LINETYPE.SOLID_POINT,n.db.LINETYPE.DOTTED_POINT,n.db.LINETYPE.BIDIRECTIONAL_SOLID,n.db.LINETYPE.BIDIRECTIONAL_DOTTED].includes(F.type)&&(E=E+_),M++}V.debug("createdActors",d),V.debug("destroyedActors",p),await bO(h,f,g,!1);for(let F of A)await KGe(h,F.messageModel,F.lineStartY,n);Ne.mirrorActors&&await bO(h,f,g,!0),L.forEach(F=>si.drawBackgroundRect(h,F)),yO(h,f,g,Ne);for(let F of Ke.models.boxes)F.height=Ke.getVerticalPos()-F.y,Ke.insert(F.x,F.y,F.x+F.width,F.height),F.startx=F.x,F.starty=F.y,F.stopx=F.startx+F.width,F.stopy=F.starty+F.height,F.stroke="rgb(0,0,0, 0.5)",si.drawBox(h,F,Ne);x&&Ke.bumpVerticalPos(Ne.boxMargin);let N=fhe(h,f,g,u),{bounds:k}=Ke.getBounds();k.startx===void 0&&(k.startx=0),k.starty===void 0&&(k.starty=0),k.stopx===void 0&&(k.stopx=0),k.stopy===void 0&&(k.stopy=0);let I=k.stopy-k.starty;I2,d=o(y=>l?-y:y,"adjustValue");t.from===t.to?h=u:(t.activate&&!f&&(h+=d(Ne.activationWidth/2-1)),[r.db.LINETYPE.SOLID_OPEN,r.db.LINETYPE.DOTTED_OPEN].includes(t.type)||(h+=d(3)),[r.db.LINETYPE.BIDIRECTIONAL_SOLID,r.db.LINETYPE.BIDIRECTIONAL_DOTTED].includes(t.type)&&(u-=d(3)));let p=[n,i,a,s],m=Math.abs(u-h);t.wrap&&t.message&&(t.message=Lt.wrapLabel(t.message,We.getMax(m+2*Ne.wrapPadding,Ne.width),L0(Ne)));let g=Lt.calculateTextDimensions(t.message,L0(Ne));return{width:We.getMax(t.wrap?0:g.width+2*Ne.wrapPadding,m+2*Ne.wrapPadding,Ne.width),height:0,startx:u,stopx:h,starty:0,stopy:0,message:t.message,type:t.type,wrap:t.wrap,fromBounds:Math.min.apply(null,p),toBounds:Math.max.apply(null,p)}},"buildMessageModel"),a$e=o(async function(t,e,r,n){let i={},a=[],s,l,u;for(let h of t){switch(h.id=Lt.random({length:10}),h.type){case n.db.LINETYPE.LOOP_START:case n.db.LINETYPE.ALT_START:case n.db.LINETYPE.OPT_START:case n.db.LINETYPE.PAR_START:case n.db.LINETYPE.PAR_OVER_START:case n.db.LINETYPE.CRITICAL_START:case n.db.LINETYPE.BREAK_START:a.push({id:h.id,msg:h.message,from:Number.MAX_SAFE_INTEGER,to:Number.MIN_SAFE_INTEGER,width:0});break;case n.db.LINETYPE.ALT_ELSE:case n.db.LINETYPE.PAR_AND:case n.db.LINETYPE.CRITICAL_OPTION:h.message&&(s=a.pop(),i[s.id]=s,i[h.id]=s,a.push(s));break;case n.db.LINETYPE.LOOP_END:case n.db.LINETYPE.ALT_END:case n.db.LINETYPE.OPT_END:case n.db.LINETYPE.PAR_END:case n.db.LINETYPE.CRITICAL_END:case n.db.LINETYPE.BREAK_END:s=a.pop(),i[s.id]=s;break;case n.db.LINETYPE.ACTIVE_START:{let d=e.get(h.from?h.from:h.to.actor),p=bE(h.from?h.from:h.to.actor).length,m=d.x+d.width/2+(p-1)*Ne.activationWidth/2,g={startx:m,stopx:m+Ne.activationWidth,actor:h.from,enabled:!0};Ke.activations.push(g)}break;case n.db.LINETYPE.ACTIVE_END:{let d=Ke.activations.map(p=>p.actor).lastIndexOf(h.from);Ke.activations.splice(d,1).splice(0,1)}break}h.placement!==void 0?(l=await n$e(h,e,n),h.noteModel=l,a.forEach(d=>{s=d,s.from=We.getMin(s.from,l.startx),s.to=We.getMax(s.to,l.startx+l.width),s.width=We.getMax(s.width,Math.abs(s.from-s.to))-Ne.labelBoxWidth})):(u=i$e(h,e,n),h.msgModel=u,u.startx&&u.stopx&&a.length>0&&a.forEach(d=>{if(s=d,u.startx===u.stopx){let p=e.get(h.from),m=e.get(h.to);s.from=We.getMin(p.x-u.width/2,p.x-p.width/2,s.from),s.to=We.getMax(m.x+u.width/2,m.x+p.width/2,s.to),s.width=We.getMax(s.width,Math.abs(s.to-s.from))-Ne.labelBoxWidth}else s.from=We.getMin(u.startx,s.from),s.to=We.getMax(u.stopx,s.to),s.width=We.getMax(s.width,u.width)-Ne.labelBoxWidth}))}return Ke.activations=[],V.debug("Loop type widths:",i),i},"calculateLoopBounds"),phe={bounds:Ke,drawActors:bO,drawActorsPopup:fhe,setConf:dhe,draw:JGe}});var ghe={};hr(ghe,{diagram:()=>s$e});var s$e,yhe=R(()=>{"use strict";que();the();nhe();mhe();s$e={parser:Wue,db:pO,renderer:phe,styles:rhe,init:o(({wrap:t})=>{pO.setWrap(t)},"init")}});var wO,wE,TO=R(()=>{"use strict";wO=function(){var t=o(function(Pe,_e,me,W){for(me=me||{},W=Pe.length;W--;me[Pe[W]]=_e);return me},"o"),e=[1,17],r=[1,18],n=[1,19],i=[1,39],a=[1,40],s=[1,25],l=[1,23],u=[1,24],h=[1,31],f=[1,32],d=[1,33],p=[1,34],m=[1,35],g=[1,36],y=[1,26],v=[1,27],x=[1,28],b=[1,29],w=[1,43],S=[1,30],T=[1,42],E=[1,44],_=[1,41],A=[1,45],L=[1,9],M=[1,8,9],N=[1,56],k=[1,57],I=[1,58],C=[1,59],O=[1,60],D=[1,61],P=[1,62],F=[1,8,9,39],B=[1,74],$=[1,8,9,12,13,21,37,39,42,59,60,61,62,63,64,65,70,72],z=[1,8,9,12,13,19,21,37,39,42,46,59,60,61,62,63,64,65,70,72,74,80,95,97,98],Y=[13,74,80,95,97,98],Q=[13,64,65,74,80,95,97,98],X=[13,59,60,61,62,63,74,80,95,97,98],ie=[1,93],j=[1,110],J=[1,108],Z=[1,102],H=[1,103],q=[1,104],K=[1,105],se=[1,106],ce=[1,107],ue=[1,109],te=[1,8,9,37,39,42],De=[1,8,9,21],oe=[1,8,9,78],ke=[1,8,9,21,73,74,78,80,81,82,83,84,85],Ie={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,start:3,mermaidDoc:4,statements:5,graphConfig:6,CLASS_DIAGRAM:7,NEWLINE:8,EOF:9,statement:10,classLabel:11,SQS:12,STR:13,SQE:14,namespaceName:15,alphaNumToken:16,className:17,classLiteralName:18,GENERICTYPE:19,relationStatement:20,LABEL:21,namespaceStatement:22,classStatement:23,memberStatement:24,annotationStatement:25,clickStatement:26,styleStatement:27,cssClassStatement:28,noteStatement:29,direction:30,acc_title:31,acc_title_value:32,acc_descr:33,acc_descr_value:34,acc_descr_multiline_value:35,namespaceIdentifier:36,STRUCT_START:37,classStatements:38,STRUCT_STOP:39,NAMESPACE:40,classIdentifier:41,STYLE_SEPARATOR:42,members:43,CLASS:44,ANNOTATION_START:45,ANNOTATION_END:46,MEMBER:47,SEPARATOR:48,relation:49,NOTE_FOR:50,noteText:51,NOTE:52,direction_tb:53,direction_bt:54,direction_rl:55,direction_lr:56,relationType:57,lineType:58,AGGREGATION:59,EXTENSION:60,COMPOSITION:61,DEPENDENCY:62,LOLLIPOP:63,LINE:64,DOTTED_LINE:65,CALLBACK:66,LINK:67,LINK_TARGET:68,CLICK:69,CALLBACK_NAME:70,CALLBACK_ARGS:71,HREF:72,STYLE:73,ALPHA:74,stylesOpt:75,CSSCLASS:76,style:77,COMMA:78,styleComponent:79,NUM:80,COLON:81,UNIT:82,SPACE:83,BRKT:84,PCT:85,commentToken:86,textToken:87,graphCodeTokens:88,textNoTagsToken:89,TAGSTART:90,TAGEND:91,"==":92,"--":93,DEFAULT:94,MINUS:95,keywords:96,UNICODE_TEXT:97,BQUOTE_STR:98,$accept:0,$end:1},terminals_:{2:"error",7:"CLASS_DIAGRAM",8:"NEWLINE",9:"EOF",12:"SQS",13:"STR",14:"SQE",19:"GENERICTYPE",21:"LABEL",31:"acc_title",32:"acc_title_value",33:"acc_descr",34:"acc_descr_value",35:"acc_descr_multiline_value",37:"STRUCT_START",39:"STRUCT_STOP",40:"NAMESPACE",42:"STYLE_SEPARATOR",44:"CLASS",45:"ANNOTATION_START",46:"ANNOTATION_END",47:"MEMBER",48:"SEPARATOR",50:"NOTE_FOR",52:"NOTE",53:"direction_tb",54:"direction_bt",55:"direction_rl",56:"direction_lr",59:"AGGREGATION",60:"EXTENSION",61:"COMPOSITION",62:"DEPENDENCY",63:"LOLLIPOP",64:"LINE",65:"DOTTED_LINE",66:"CALLBACK",67:"LINK",68:"LINK_TARGET",69:"CLICK",70:"CALLBACK_NAME",71:"CALLBACK_ARGS",72:"HREF",73:"STYLE",74:"ALPHA",76:"CSSCLASS",78:"COMMA",80:"NUM",81:"COLON",82:"UNIT",83:"SPACE",84:"BRKT",85:"PCT",88:"graphCodeTokens",90:"TAGSTART",91:"TAGEND",92:"==",93:"--",94:"DEFAULT",95:"MINUS",96:"keywords",97:"UNICODE_TEXT",98:"BQUOTE_STR"},productions_:[0,[3,1],[3,1],[4,1],[6,4],[5,1],[5,2],[5,3],[11,3],[15,1],[15,2],[17,1],[17,1],[17,2],[17,2],[17,2],[10,1],[10,2],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,2],[10,2],[10,1],[22,4],[22,5],[36,2],[38,1],[38,2],[38,3],[23,1],[23,3],[23,4],[23,6],[41,2],[41,3],[25,4],[43,1],[43,2],[24,1],[24,2],[24,1],[24,1],[20,3],[20,4],[20,4],[20,5],[29,3],[29,2],[30,1],[30,1],[30,1],[30,1],[49,3],[49,2],[49,2],[49,1],[57,1],[57,1],[57,1],[57,1],[57,1],[58,1],[58,1],[26,3],[26,4],[26,3],[26,4],[26,4],[26,5],[26,3],[26,4],[26,4],[26,5],[26,4],[26,5],[26,5],[26,6],[27,3],[28,3],[75,1],[75,3],[77,1],[77,2],[79,1],[79,1],[79,1],[79,1],[79,1],[79,1],[79,1],[79,1],[79,1],[86,1],[86,1],[87,1],[87,1],[87,1],[87,1],[87,1],[87,1],[87,1],[89,1],[89,1],[89,1],[89,1],[16,1],[16,1],[16,1],[16,1],[18,1],[51,1]],performAction:o(function(_e,me,W,fe,ge,re,he){var ne=re.length-1;switch(ge){case 8:this.$=re[ne-1];break;case 9:case 11:case 12:this.$=re[ne];break;case 10:case 13:this.$=re[ne-1]+re[ne];break;case 14:case 15:this.$=re[ne-1]+"~"+re[ne]+"~";break;case 16:fe.addRelation(re[ne]);break;case 17:re[ne-1].title=fe.cleanupLabel(re[ne]),fe.addRelation(re[ne-1]);break;case 27:this.$=re[ne].trim(),fe.setAccTitle(this.$);break;case 28:case 29:this.$=re[ne].trim(),fe.setAccDescription(this.$);break;case 30:fe.addClassesToNamespace(re[ne-3],re[ne-1]);break;case 31:fe.addClassesToNamespace(re[ne-4],re[ne-1]);break;case 32:this.$=re[ne],fe.addNamespace(re[ne]);break;case 33:this.$=[re[ne]];break;case 34:this.$=[re[ne-1]];break;case 35:re[ne].unshift(re[ne-2]),this.$=re[ne];break;case 37:fe.setCssClass(re[ne-2],re[ne]);break;case 38:fe.addMembers(re[ne-3],re[ne-1]);break;case 39:fe.setCssClass(re[ne-5],re[ne-3]),fe.addMembers(re[ne-5],re[ne-1]);break;case 40:this.$=re[ne],fe.addClass(re[ne]);break;case 41:this.$=re[ne-1],fe.addClass(re[ne-1]),fe.setClassLabel(re[ne-1],re[ne]);break;case 42:fe.addAnnotation(re[ne],re[ne-2]);break;case 43:this.$=[re[ne]];break;case 44:re[ne].push(re[ne-1]),this.$=re[ne];break;case 45:break;case 46:fe.addMember(re[ne-1],fe.cleanupLabel(re[ne]));break;case 47:break;case 48:break;case 49:this.$={id1:re[ne-2],id2:re[ne],relation:re[ne-1],relationTitle1:"none",relationTitle2:"none"};break;case 50:this.$={id1:re[ne-3],id2:re[ne],relation:re[ne-1],relationTitle1:re[ne-2],relationTitle2:"none"};break;case 51:this.$={id1:re[ne-3],id2:re[ne],relation:re[ne-2],relationTitle1:"none",relationTitle2:re[ne-1]};break;case 52:this.$={id1:re[ne-4],id2:re[ne],relation:re[ne-2],relationTitle1:re[ne-3],relationTitle2:re[ne-1]};break;case 53:fe.addNote(re[ne],re[ne-1]);break;case 54:fe.addNote(re[ne]);break;case 55:fe.setDirection("TB");break;case 56:fe.setDirection("BT");break;case 57:fe.setDirection("RL");break;case 58:fe.setDirection("LR");break;case 59:this.$={type1:re[ne-2],type2:re[ne],lineType:re[ne-1]};break;case 60:this.$={type1:"none",type2:re[ne],lineType:re[ne-1]};break;case 61:this.$={type1:re[ne-1],type2:"none",lineType:re[ne]};break;case 62:this.$={type1:"none",type2:"none",lineType:re[ne]};break;case 63:this.$=fe.relationType.AGGREGATION;break;case 64:this.$=fe.relationType.EXTENSION;break;case 65:this.$=fe.relationType.COMPOSITION;break;case 66:this.$=fe.relationType.DEPENDENCY;break;case 67:this.$=fe.relationType.LOLLIPOP;break;case 68:this.$=fe.lineType.LINE;break;case 69:this.$=fe.lineType.DOTTED_LINE;break;case 70:case 76:this.$=re[ne-2],fe.setClickEvent(re[ne-1],re[ne]);break;case 71:case 77:this.$=re[ne-3],fe.setClickEvent(re[ne-2],re[ne-1]),fe.setTooltip(re[ne-2],re[ne]);break;case 72:this.$=re[ne-2],fe.setLink(re[ne-1],re[ne]);break;case 73:this.$=re[ne-3],fe.setLink(re[ne-2],re[ne-1],re[ne]);break;case 74:this.$=re[ne-3],fe.setLink(re[ne-2],re[ne-1]),fe.setTooltip(re[ne-2],re[ne]);break;case 75:this.$=re[ne-4],fe.setLink(re[ne-3],re[ne-2],re[ne]),fe.setTooltip(re[ne-3],re[ne-1]);break;case 78:this.$=re[ne-3],fe.setClickEvent(re[ne-2],re[ne-1],re[ne]);break;case 79:this.$=re[ne-4],fe.setClickEvent(re[ne-3],re[ne-2],re[ne-1]),fe.setTooltip(re[ne-3],re[ne]);break;case 80:this.$=re[ne-3],fe.setLink(re[ne-2],re[ne]);break;case 81:this.$=re[ne-4],fe.setLink(re[ne-3],re[ne-1],re[ne]);break;case 82:this.$=re[ne-4],fe.setLink(re[ne-3],re[ne-1]),fe.setTooltip(re[ne-3],re[ne]);break;case 83:this.$=re[ne-5],fe.setLink(re[ne-4],re[ne-2],re[ne]),fe.setTooltip(re[ne-4],re[ne-1]);break;case 84:this.$=re[ne-2],fe.setCssStyle(re[ne-1],re[ne]);break;case 85:fe.setCssClass(re[ne-1],re[ne]);break;case 86:this.$=[re[ne]];break;case 87:re[ne-2].push(re[ne]),this.$=re[ne-2];break;case 89:this.$=re[ne-1]+re[ne];break}},"anonymous"),table:[{3:1,4:2,5:3,6:4,7:[1,6],10:5,16:37,17:20,18:38,20:7,22:8,23:9,24:10,25:11,26:12,27:13,28:14,29:15,30:16,31:e,33:r,35:n,36:21,40:i,41:22,44:a,45:s,47:l,48:u,50:h,52:f,53:d,54:p,55:m,56:g,66:y,67:v,69:x,73:b,74:w,76:S,80:T,95:E,97:_,98:A},{1:[3]},{1:[2,1]},{1:[2,2]},{1:[2,3]},t(L,[2,5],{8:[1,46]}),{8:[1,47]},t(M,[2,16],{21:[1,48]}),t(M,[2,18]),t(M,[2,19]),t(M,[2,20]),t(M,[2,21]),t(M,[2,22]),t(M,[2,23]),t(M,[2,24]),t(M,[2,25]),t(M,[2,26]),{32:[1,49]},{34:[1,50]},t(M,[2,29]),t(M,[2,45],{49:51,57:54,58:55,13:[1,52],21:[1,53],59:N,60:k,61:I,62:C,63:O,64:D,65:P}),{37:[1,63]},t(F,[2,36],{37:[1,65],42:[1,64]}),t(M,[2,47]),t(M,[2,48]),{16:66,74:w,80:T,95:E,97:_},{16:37,17:67,18:38,74:w,80:T,95:E,97:_,98:A},{16:37,17:68,18:38,74:w,80:T,95:E,97:_,98:A},{16:37,17:69,18:38,74:w,80:T,95:E,97:_,98:A},{74:[1,70]},{13:[1,71]},{16:37,17:72,18:38,74:w,80:T,95:E,97:_,98:A},{13:B,51:73},t(M,[2,55]),t(M,[2,56]),t(M,[2,57]),t(M,[2,58]),t($,[2,11],{16:37,18:38,17:75,19:[1,76],74:w,80:T,95:E,97:_,98:A}),t($,[2,12],{19:[1,77]}),{15:78,16:79,74:w,80:T,95:E,97:_},{16:37,17:80,18:38,74:w,80:T,95:E,97:_,98:A},t(z,[2,112]),t(z,[2,113]),t(z,[2,114]),t(z,[2,115]),t([1,8,9,12,13,19,21,37,39,42,59,60,61,62,63,64,65,70,72],[2,116]),t(L,[2,6],{10:5,20:7,22:8,23:9,24:10,25:11,26:12,27:13,28:14,29:15,30:16,17:20,36:21,41:22,16:37,18:38,5:81,31:e,33:r,35:n,40:i,44:a,45:s,47:l,48:u,50:h,52:f,53:d,54:p,55:m,56:g,66:y,67:v,69:x,73:b,74:w,76:S,80:T,95:E,97:_,98:A}),{5:82,10:5,16:37,17:20,18:38,20:7,22:8,23:9,24:10,25:11,26:12,27:13,28:14,29:15,30:16,31:e,33:r,35:n,36:21,40:i,41:22,44:a,45:s,47:l,48:u,50:h,52:f,53:d,54:p,55:m,56:g,66:y,67:v,69:x,73:b,74:w,76:S,80:T,95:E,97:_,98:A},t(M,[2,17]),t(M,[2,27]),t(M,[2,28]),{13:[1,84],16:37,17:83,18:38,74:w,80:T,95:E,97:_,98:A},{49:85,57:54,58:55,59:N,60:k,61:I,62:C,63:O,64:D,65:P},t(M,[2,46]),{58:86,64:D,65:P},t(Y,[2,62],{57:87,59:N,60:k,61:I,62:C,63:O}),t(Q,[2,63]),t(Q,[2,64]),t(Q,[2,65]),t(Q,[2,66]),t(Q,[2,67]),t(X,[2,68]),t(X,[2,69]),{8:[1,89],23:90,38:88,41:22,44:a},{16:91,74:w,80:T,95:E,97:_},{43:92,47:ie},{46:[1,94]},{13:[1,95]},{13:[1,96]},{70:[1,97],72:[1,98]},{21:j,73:J,74:Z,75:99,77:100,79:101,80:H,81:q,82:K,83:se,84:ce,85:ue},{74:[1,111]},{13:B,51:112},t(M,[2,54]),t(M,[2,117]),t($,[2,13]),t($,[2,14]),t($,[2,15]),{37:[2,32]},{15:113,16:79,37:[2,9],74:w,80:T,95:E,97:_},t(te,[2,40],{11:114,12:[1,115]}),t(L,[2,7]),{9:[1,116]},t(De,[2,49]),{16:37,17:117,18:38,74:w,80:T,95:E,97:_,98:A},{13:[1,119],16:37,17:118,18:38,74:w,80:T,95:E,97:_,98:A},t(Y,[2,61],{57:120,59:N,60:k,61:I,62:C,63:O}),t(Y,[2,60]),{39:[1,121]},{23:90,38:122,41:22,44:a},{8:[1,123],39:[2,33]},t(F,[2,37],{37:[1,124]}),{39:[1,125]},{39:[2,43],43:126,47:ie},{16:37,17:127,18:38,74:w,80:T,95:E,97:_,98:A},t(M,[2,70],{13:[1,128]}),t(M,[2,72],{13:[1,130],68:[1,129]}),t(M,[2,76],{13:[1,131],71:[1,132]}),{13:[1,133]},t(M,[2,84],{78:[1,134]}),t(oe,[2,86],{79:135,21:j,73:J,74:Z,80:H,81:q,82:K,83:se,84:ce,85:ue}),t(ke,[2,88]),t(ke,[2,90]),t(ke,[2,91]),t(ke,[2,92]),t(ke,[2,93]),t(ke,[2,94]),t(ke,[2,95]),t(ke,[2,96]),t(ke,[2,97]),t(ke,[2,98]),t(M,[2,85]),t(M,[2,53]),{37:[2,10]},t(te,[2,41]),{13:[1,136]},{1:[2,4]},t(De,[2,51]),t(De,[2,50]),{16:37,17:137,18:38,74:w,80:T,95:E,97:_,98:A},t(Y,[2,59]),t(M,[2,30]),{39:[1,138]},{23:90,38:139,39:[2,34],41:22,44:a},{43:140,47:ie},t(F,[2,38]),{39:[2,44]},t(M,[2,42]),t(M,[2,71]),t(M,[2,73]),t(M,[2,74],{68:[1,141]}),t(M,[2,77]),t(M,[2,78],{13:[1,142]}),t(M,[2,80],{13:[1,144],68:[1,143]}),{21:j,73:J,74:Z,77:145,79:101,80:H,81:q,82:K,83:se,84:ce,85:ue},t(ke,[2,89]),{14:[1,146]},t(De,[2,52]),t(M,[2,31]),{39:[2,35]},{39:[1,147]},t(M,[2,75]),t(M,[2,79]),t(M,[2,81]),t(M,[2,82],{68:[1,148]}),t(oe,[2,87],{79:135,21:j,73:J,74:Z,80:H,81:q,82:K,83:se,84:ce,85:ue}),t(te,[2,8]),t(F,[2,39]),t(M,[2,83])],defaultActions:{2:[2,1],3:[2,2],4:[2,3],78:[2,32],113:[2,10],116:[2,4],126:[2,44],139:[2,35]},parseError:o(function(_e,me){if(me.recoverable)this.trace(_e);else{var W=new Error(_e);throw W.hash=me,W}},"parseError"),parse:o(function(_e){var me=this,W=[0],fe=[],ge=[null],re=[],he=this.table,ne="",ae=0,we=0,Te=0,Ce=2,Ae=1,Ge=re.slice.call(arguments,1),Me=Object.create(this.lexer),ye={yy:{}};for(var He in this.yy)Object.prototype.hasOwnProperty.call(this.yy,He)&&(ye.yy[He]=this.yy[He]);Me.setInput(_e,ye.yy),ye.yy.lexer=Me,ye.yy.parser=this,typeof Me.yylloc>"u"&&(Me.yylloc={});var ze=Me.yylloc;re.push(ze);var Ze=Me.options&&Me.options.ranges;typeof ye.yy.parseError=="function"?this.parseError=ye.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function gt(mt){W.length=W.length-2*mt,ge.length=ge.length-mt,re.length=re.length-mt}o(gt,"popStack");function yt(){var mt;return mt=fe.pop()||Me.lex()||Ae,typeof mt!="number"&&(mt instanceof Array&&(fe=mt,mt=fe.pop()),mt=me.symbols_[mt]||mt),mt}o(yt,"lex");for(var tt,Ye,Je,Ve,je,kt,at={},xt,it,dt,lt;;){if(Je=W[W.length-1],this.defaultActions[Je]?Ve=this.defaultActions[Je]:((tt===null||typeof tt>"u")&&(tt=yt()),Ve=he[Je]&&he[Je][tt]),typeof Ve>"u"||!Ve.length||!Ve[0]){var It="";lt=[];for(xt in he[Je])this.terminals_[xt]&&xt>Ce&<.push("'"+this.terminals_[xt]+"'");Me.showPosition?It="Parse error on line "+(ae+1)+`: +`+Me.showPosition()+` +Expecting `+lt.join(", ")+", got '"+(this.terminals_[tt]||tt)+"'":It="Parse error on line "+(ae+1)+": Unexpected "+(tt==Ae?"end of input":"'"+(this.terminals_[tt]||tt)+"'"),this.parseError(It,{text:Me.match,token:this.terminals_[tt]||tt,line:Me.yylineno,loc:ze,expected:lt})}if(Ve[0]instanceof Array&&Ve.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Je+", token: "+tt);switch(Ve[0]){case 1:W.push(tt),ge.push(Me.yytext),re.push(Me.yylloc),W.push(Ve[1]),tt=null,Ye?(tt=Ye,Ye=null):(we=Me.yyleng,ne=Me.yytext,ae=Me.yylineno,ze=Me.yylloc,Te>0&&Te--);break;case 2:if(it=this.productions_[Ve[1]][1],at.$=ge[ge.length-it],at._$={first_line:re[re.length-(it||1)].first_line,last_line:re[re.length-1].last_line,first_column:re[re.length-(it||1)].first_column,last_column:re[re.length-1].last_column},Ze&&(at._$.range=[re[re.length-(it||1)].range[0],re[re.length-1].range[1]]),kt=this.performAction.apply(at,[ne,we,ae,ye.yy,Ve[1],ge,re].concat(Ge)),typeof kt<"u")return kt;it&&(W=W.slice(0,-1*it*2),ge=ge.slice(0,-1*it),re=re.slice(0,-1*it)),W.push(this.productions_[Ve[1]][0]),ge.push(at.$),re.push(at._$),dt=he[W[W.length-2]][W[W.length-1]],W.push(dt);break;case 3:return!0}}return!0},"parse")},Se=function(){var Pe={EOF:1,parseError:o(function(me,W){if(this.yy.parser)this.yy.parser.parseError(me,W);else throw new Error(me)},"parseError"),setInput:o(function(_e,me){return this.yy=me||this.yy||{},this._input=_e,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var _e=this._input[0];this.yytext+=_e,this.yyleng++,this.offset++,this.match+=_e,this.matched+=_e;var me=_e.match(/(?:\r\n?|\n).*/g);return me?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),_e},"input"),unput:o(function(_e){var me=_e.length,W=_e.split(/(?:\r\n?|\n)/g);this._input=_e+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-me),this.offset-=me;var fe=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),W.length-1&&(this.yylineno-=W.length-1);var ge=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:W?(W.length===fe.length?this.yylloc.first_column:0)+fe[fe.length-W.length].length-W[0].length:this.yylloc.first_column-me},this.options.ranges&&(this.yylloc.range=[ge[0],ge[0]+this.yyleng-me]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(_e){this.unput(this.match.slice(_e))},"less"),pastInput:o(function(){var _e=this.matched.substr(0,this.matched.length-this.match.length);return(_e.length>20?"...":"")+_e.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var _e=this.match;return _e.length<20&&(_e+=this._input.substr(0,20-_e.length)),(_e.substr(0,20)+(_e.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var _e=this.pastInput(),me=new Array(_e.length+1).join("-");return _e+this.upcomingInput()+` +`+me+"^"},"showPosition"),test_match:o(function(_e,me){var W,fe,ge;if(this.options.backtrack_lexer&&(ge={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(ge.yylloc.range=this.yylloc.range.slice(0))),fe=_e[0].match(/(?:\r\n?|\n).*/g),fe&&(this.yylineno+=fe.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:fe?fe[fe.length-1].length-fe[fe.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+_e[0].length},this.yytext+=_e[0],this.match+=_e[0],this.matches=_e,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(_e[0].length),this.matched+=_e[0],W=this.performAction.call(this,this.yy,this,me,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),W)return W;if(this._backtrack){for(var re in ge)this[re]=ge[re];return!1}return!1},"test_match"),next:o(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var _e,me,W,fe;this._more||(this.yytext="",this.match="");for(var ge=this._currentRules(),re=0;reme[0].length)){if(me=W,fe=re,this.options.backtrack_lexer){if(_e=this.test_match(W,ge[re]),_e!==!1)return _e;if(this._backtrack){me=!1;continue}else return!1}else if(!this.options.flex)break}return me?(_e=this.test_match(me,ge[fe]),_e!==!1?_e:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:o(function(){var me=this.next();return me||this.lex()},"lex"),begin:o(function(me){this.conditionStack.push(me)},"begin"),popState:o(function(){var me=this.conditionStack.length-1;return me>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:o(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:o(function(me){return me=this.conditionStack.length-1-Math.abs(me||0),me>=0?this.conditionStack[me]:"INITIAL"},"topState"),pushState:o(function(me){this.begin(me)},"pushState"),stateStackSize:o(function(){return this.conditionStack.length},"stateStackSize"),options:{},performAction:o(function(me,W,fe,ge){var re=ge;switch(fe){case 0:return 53;case 1:return 54;case 2:return 55;case 3:return 56;case 4:break;case 5:break;case 6:return this.begin("acc_title"),31;break;case 7:return this.popState(),"acc_title_value";break;case 8:return this.begin("acc_descr"),33;break;case 9:return this.popState(),"acc_descr_value";break;case 10:this.begin("acc_descr_multiline");break;case 11:this.popState();break;case 12:return"acc_descr_multiline_value";case 13:return 8;case 14:break;case 15:return 7;case 16:return 7;case 17:return"EDGE_STATE";case 18:this.begin("callback_name");break;case 19:this.popState();break;case 20:this.popState(),this.begin("callback_args");break;case 21:return 70;case 22:this.popState();break;case 23:return 71;case 24:this.popState();break;case 25:return"STR";case 26:this.begin("string");break;case 27:return 73;case 28:return this.begin("namespace"),40;break;case 29:return this.popState(),8;break;case 30:break;case 31:return this.begin("namespace-body"),37;break;case 32:return this.popState(),39;break;case 33:return"EOF_IN_STRUCT";case 34:return 8;case 35:break;case 36:return"EDGE_STATE";case 37:return this.begin("class"),44;break;case 38:return this.popState(),8;break;case 39:break;case 40:return this.popState(),this.popState(),39;break;case 41:return this.begin("class-body"),37;break;case 42:return this.popState(),39;break;case 43:return"EOF_IN_STRUCT";case 44:return"EDGE_STATE";case 45:return"OPEN_IN_STRUCT";case 46:break;case 47:return"MEMBER";case 48:return 76;case 49:return 66;case 50:return 67;case 51:return 69;case 52:return 50;case 53:return 52;case 54:return 45;case 55:return 46;case 56:return 72;case 57:this.popState();break;case 58:return"GENERICTYPE";case 59:this.begin("generic");break;case 60:this.popState();break;case 61:return"BQUOTE_STR";case 62:this.begin("bqstring");break;case 63:return 68;case 64:return 68;case 65:return 68;case 66:return 68;case 67:return 60;case 68:return 60;case 69:return 62;case 70:return 62;case 71:return 61;case 72:return 59;case 73:return 63;case 74:return 64;case 75:return 65;case 76:return 21;case 77:return 42;case 78:return 95;case 79:return"DOT";case 80:return"PLUS";case 81:return 81;case 82:return 78;case 83:return 84;case 84:return 84;case 85:return 85;case 86:return"EQUALS";case 87:return"EQUALS";case 88:return 74;case 89:return 12;case 90:return 14;case 91:return"PUNCTUATION";case 92:return 80;case 93:return 97;case 94:return 83;case 95:return 83;case 96:return 9}},"anonymous"),rules:[/^(?:.*direction\s+TB[^\n]*)/,/^(?:.*direction\s+BT[^\n]*)/,/^(?:.*direction\s+RL[^\n]*)/,/^(?:.*direction\s+LR[^\n]*)/,/^(?:%%(?!\{)*[^\n]*(\r?\n?)+)/,/^(?:%%[^\n]*(\r?\n)*)/,/^(?:accTitle\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*\{\s*)/,/^(?:[\}])/,/^(?:[^\}]*)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:classDiagram-v2\b)/,/^(?:classDiagram\b)/,/^(?:\[\*\])/,/^(?:call[\s]+)/,/^(?:\([\s]*\))/,/^(?:\()/,/^(?:[^(]*)/,/^(?:\))/,/^(?:[^)]*)/,/^(?:["])/,/^(?:[^"]*)/,/^(?:["])/,/^(?:style\b)/,/^(?:namespace\b)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:[{])/,/^(?:[}])/,/^(?:$)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:\[\*\])/,/^(?:class\b)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:[}])/,/^(?:[{])/,/^(?:[}])/,/^(?:$)/,/^(?:\[\*\])/,/^(?:[{])/,/^(?:[\n])/,/^(?:[^{}\n]*)/,/^(?:cssClass\b)/,/^(?:callback\b)/,/^(?:link\b)/,/^(?:click\b)/,/^(?:note for\b)/,/^(?:note\b)/,/^(?:<<)/,/^(?:>>)/,/^(?:href\b)/,/^(?:[~])/,/^(?:[^~]*)/,/^(?:~)/,/^(?:[`])/,/^(?:[^`]+)/,/^(?:[`])/,/^(?:_self\b)/,/^(?:_blank\b)/,/^(?:_parent\b)/,/^(?:_top\b)/,/^(?:\s*<\|)/,/^(?:\s*\|>)/,/^(?:\s*>)/,/^(?:\s*<)/,/^(?:\s*\*)/,/^(?:\s*o\b)/,/^(?:\s*\(\))/,/^(?:--)/,/^(?:\.\.)/,/^(?::{1}[^:\n;]+)/,/^(?::{3})/,/^(?:-)/,/^(?:\.)/,/^(?:\+)/,/^(?::)/,/^(?:,)/,/^(?:#)/,/^(?:#)/,/^(?:%)/,/^(?:=)/,/^(?:=)/,/^(?:\w+)/,/^(?:\[)/,/^(?:\])/,/^(?:[!"#$%&'*+,-.`?\\/])/,/^(?:[0-9]+)/,/^(?:[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|[\uFFD2-\uFFD7\uFFDA-\uFFDC])/,/^(?:\s)/,/^(?:\s)/,/^(?:$)/],conditions:{"namespace-body":{rules:[26,32,33,34,35,36,37,48,49,50,51,52,53,54,55,56,59,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,85,86,87,88,89,90,91,92,93,94,96],inclusive:!1},namespace:{rules:[26,28,29,30,31,48,49,50,51,52,53,54,55,56,59,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,85,86,87,88,89,90,91,92,93,94,96],inclusive:!1},"class-body":{rules:[26,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,59,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,85,86,87,88,89,90,91,92,93,94,96],inclusive:!1},class:{rules:[26,38,39,40,41,48,49,50,51,52,53,54,55,56,59,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,85,86,87,88,89,90,91,92,93,94,96],inclusive:!1},acc_descr_multiline:{rules:[11,12,26,48,49,50,51,52,53,54,55,56,59,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,85,86,87,88,89,90,91,92,93,94,96],inclusive:!1},acc_descr:{rules:[9,26,48,49,50,51,52,53,54,55,56,59,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,85,86,87,88,89,90,91,92,93,94,96],inclusive:!1},acc_title:{rules:[7,26,48,49,50,51,52,53,54,55,56,59,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,85,86,87,88,89,90,91,92,93,94,96],inclusive:!1},callback_args:{rules:[22,23,26,48,49,50,51,52,53,54,55,56,59,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,85,86,87,88,89,90,91,92,93,94,96],inclusive:!1},callback_name:{rules:[19,20,21,26,48,49,50,51,52,53,54,55,56,59,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,85,86,87,88,89,90,91,92,93,94,96],inclusive:!1},href:{rules:[26,48,49,50,51,52,53,54,55,56,59,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,85,86,87,88,89,90,91,92,93,94,96],inclusive:!1},struct:{rules:[26,48,49,50,51,52,53,54,55,56,59,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,85,86,87,88,89,90,91,92,93,94,96],inclusive:!1},generic:{rules:[26,48,49,50,51,52,53,54,55,56,57,58,59,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,85,86,87,88,89,90,91,92,93,94,96],inclusive:!1},bqstring:{rules:[26,48,49,50,51,52,53,54,55,56,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,85,86,87,88,89,90,91,92,93,94,96],inclusive:!1},string:{rules:[24,25,26,48,49,50,51,52,53,54,55,56,59,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,85,86,87,88,89,90,91,92,93,94,96],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,8,10,13,14,15,16,17,18,26,27,28,37,48,49,50,51,52,53,54,55,56,59,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96],inclusive:!0}}};return Pe}();Ie.lexer=Se;function Ue(){this.yy={}}return o(Ue,"Parser"),Ue.prototype=Ie,Ie.Parser=Ue,new Ue}();wO.parser=wO;wE=wO});var bhe,wx,whe=R(()=>{"use strict";_t();rr();bhe=["#","+","~","-",""],wx=class{static{o(this,"ClassMember")}constructor(e,r){this.memberType=r,this.visibility="",this.classifier="";let n=qr(e,de());this.parseMember(n)}getDisplayDetails(){let e=this.visibility+gh(this.id);this.memberType==="method"&&(e+=`(${gh(this.parameters.trim())})`,this.returnType&&(e+=" : "+gh(this.returnType))),e=e.trim();let r=this.parseClassifier();return{displayText:e,cssStyle:r}}parseMember(e){let r="";if(this.memberType==="method"){let i=/([#+~-])?(.+)\((.*)\)([\s$*])?(.*)([$*])?/.exec(e);if(i){let a=i[1]?i[1].trim():"";if(bhe.includes(a)&&(this.visibility=a),this.id=i[2].trim(),this.parameters=i[3]?i[3].trim():"",r=i[4]?i[4].trim():"",this.returnType=i[5]?i[5].trim():"",r===""){let s=this.returnType.substring(this.returnType.length-1);/[$*]/.exec(s)&&(r=s,this.returnType=this.returnType.substring(0,this.returnType.length-1))}}}else{let n=e.length,i=e.substring(0,1),a=e.substring(n-1);bhe.includes(i)&&(this.visibility=i),/[$*]/.exec(a)&&(r=a),this.id=e.substring(this.visibility===""?0:1,r===""?n:n-1)}this.classifier=r}parseClassifier(){switch(this.classifier){case"*":return"font-style:italic;";case"$":return"text-decoration:underline;";default:return""}}}});var EE,EO,Gi,TE,The,qu,kO,Tx,D0,R0,u$e,kE,khe,h$e,f$e,d$e,p$e,m$e,g$e,y$e,Ehe,v$e,x$e,b$e,CO,w$e,T$e,k$e,E$e,C$e,S$e,A$e,_$e,Che,SO,L$e,D$e,R$e,N$e,M$e,I$e,O$e,Bg,AO=R(()=>{"use strict";Zt();ut();_t();rr();xr();bi();whe();EE="classId-",EO=[],Gi=new Map,TE=[],The=0,qu=new Map,kO=0,Tx=[],D0=o(t=>We.sanitizeText(t,de()),"sanitizeText"),R0=o(function(t){let e=We.sanitizeText(t,de()),r="",n=e;if(e.indexOf("~")>0){let i=e.split("~");n=D0(i[0]),r=D0(i[1])}return{className:n,type:r}},"splitClassNameAndType"),u$e=o(function(t,e){let r=We.sanitizeText(t,de());e&&(e=D0(e));let{className:n}=R0(r);Gi.get(n).label=e},"setClassLabel"),kE=o(function(t){let e=We.sanitizeText(t,de()),{className:r,type:n}=R0(e);if(Gi.has(r))return;let i=We.sanitizeText(r,de());Gi.set(i,{id:i,type:n,label:i,cssClasses:[],methods:[],members:[],annotations:[],styles:[],domId:EE+i+"-"+The}),The++},"addClass"),khe=o(function(t){let e=We.sanitizeText(t,de());if(Gi.has(e))return Gi.get(e).domId;throw new Error("Class not found: "+e)},"lookUpDomId"),h$e=o(function(){EO=[],Gi=new Map,TE=[],Tx=[],Tx.push(Che),qu=new Map,kO=0,SO="TB",vr()},"clear"),f$e=o(function(t){return Gi.get(t)},"getClass"),d$e=o(function(){return Gi},"getClasses"),p$e=o(function(){return EO},"getRelations"),m$e=o(function(){return TE},"getNotes"),g$e=o(function(t){V.debug("Adding relation: "+JSON.stringify(t)),kE(t.id1),kE(t.id2),t.id1=R0(t.id1).className,t.id2=R0(t.id2).className,t.relationTitle1=We.sanitizeText(t.relationTitle1.trim(),de()),t.relationTitle2=We.sanitizeText(t.relationTitle2.trim(),de()),EO.push(t)},"addRelation"),y$e=o(function(t,e){let r=R0(t).className;Gi.get(r).annotations.push(e)},"addAnnotation"),Ehe=o(function(t,e){kE(t);let r=R0(t).className,n=Gi.get(r);if(typeof e=="string"){let i=e.trim();i.startsWith("<<")&&i.endsWith(">>")?n.annotations.push(D0(i.substring(2,i.length-2))):i.indexOf(")")>0?n.methods.push(new wx(i,"method")):i&&n.members.push(new wx(i,"attribute"))}},"addMember"),v$e=o(function(t,e){Array.isArray(e)&&(e.reverse(),e.forEach(r=>Ehe(t,r)))},"addMembers"),x$e=o(function(t,e){let r={id:`note${TE.length}`,class:e,text:t};TE.push(r)},"addNote"),b$e=o(function(t){return t.startsWith(":")&&(t=t.substring(1)),D0(t.trim())},"cleanupLabel"),CO=o(function(t,e){t.split(",").forEach(function(r){let n=r;/\d/.exec(r[0])&&(n=EE+n);let i=Gi.get(n);i&&i.cssClasses.push(e)})},"setCssClass"),w$e=o(function(t,e){t.split(",").forEach(function(r){e!==void 0&&(Gi.get(r).tooltip=D0(e))})},"setTooltip"),T$e=o(function(t,e){return e&&qu.has(e)?qu.get(e).classes.get(t).tooltip:Gi.get(t).tooltip},"getTooltip"),k$e=o(function(t,e,r){let n=de();t.split(",").forEach(function(i){let a=i;/\d/.exec(i[0])&&(a=EE+a);let s=Gi.get(a);s&&(s.link=Lt.formatUrl(e,n),n.securityLevel==="sandbox"?s.linkTarget="_top":typeof r=="string"?s.linkTarget=D0(r):s.linkTarget="_blank")}),CO(t,"clickable")},"setLink"),E$e=o(function(t,e,r){t.split(",").forEach(function(n){C$e(n,e,r),Gi.get(n).haveCallback=!0}),CO(t,"clickable")},"setClickEvent"),C$e=o(function(t,e,r){let n=We.sanitizeText(t,de());if(de().securityLevel!=="loose"||e===void 0)return;let a=n;if(Gi.has(a)){let s=khe(a),l=[];if(typeof r=="string"){l=r.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(let u=0;u")),i.classed("hover",!0)}).on("mouseout",function(){e.transition().duration(500).style("opacity",0),$e(this).classed("hover",!1)})},"setupToolTips");Tx.push(Che);SO="TB",L$e=o(()=>SO,"getDirection"),D$e=o(t=>{SO=t},"setDirection"),R$e=o(function(t){qu.has(t)||(qu.set(t,{id:t,classes:new Map,children:{},domId:EE+t+"-"+kO}),kO++)},"addNamespace"),N$e=o(function(t){return qu.get(t)},"getNamespace"),M$e=o(function(){return qu},"getNamespaces"),I$e=o(function(t,e){if(qu.has(t))for(let r of e){let{className:n}=R0(r);Gi.get(n).parent=t,qu.get(t).classes.set(n,Gi.get(n))}},"addClassesToNamespace"),O$e=o(function(t,e){let r=Gi.get(t);if(!(!e||!r))for(let n of e)n.includes(",")?r.styles.push(...n.split(",")):r.styles.push(n)},"setCssStyle"),Bg={setAccTitle:kr,getAccTitle:Ar,getAccDescription:Lr,setAccDescription:_r,getConfig:o(()=>de().class,"getConfig"),addClass:kE,bindFunctions:S$e,clear:h$e,getClass:f$e,getClasses:d$e,getNotes:m$e,addAnnotation:y$e,addNote:x$e,getRelations:p$e,addRelation:g$e,getDirection:L$e,setDirection:D$e,addMember:Ehe,addMembers:v$e,cleanupLabel:b$e,lineType:A$e,relationType:_$e,setClickEvent:E$e,setCssClass:CO,setLink:k$e,getTooltip:T$e,setTooltip:w$e,lookUpDomId:khe,setDiagramTitle:nn,getDiagramTitle:Xr,setClassLabel:u$e,addNamespace:R$e,addClassesToNamespace:I$e,getNamespace:N$e,getNamespaces:M$e,setCssStyle:O$e}});var P$e,CE,_O=R(()=>{"use strict";P$e=o(t=>`g.classGroup text { + fill: ${t.nodeBorder||t.classText}; + stroke: none; + font-family: ${t.fontFamily}; + font-size: 10px; + + .title { + font-weight: bolder; + } + +} + +.nodeLabel, .edgeLabel { + color: ${t.classText}; +} +.edgeLabel .label rect { + fill: ${t.mainBkg}; +} +.label text { + fill: ${t.classText}; +} +.edgeLabel .label span { + background: ${t.mainBkg}; +} + +.classTitle { + font-weight: bolder; +} +.node rect, + .node circle, + .node ellipse, + .node polygon, + .node path { + fill: ${t.mainBkg}; + stroke: ${t.nodeBorder}; + stroke-width: 1px; + } + + +.divider { + stroke: ${t.nodeBorder}; + stroke-width: 1; +} + +g.clickable { + cursor: pointer; +} + +g.classGroup rect { + fill: ${t.mainBkg}; + stroke: ${t.nodeBorder}; +} + +g.classGroup line { + stroke: ${t.nodeBorder}; + stroke-width: 1; +} + +.classLabel .box { + stroke: none; + stroke-width: 0; + fill: ${t.mainBkg}; + opacity: 0.5; +} + +.classLabel .label { + fill: ${t.nodeBorder}; + font-size: 10px; +} + +.relation { + stroke: ${t.lineColor}; + stroke-width: 1; + fill: none; +} + +.dashed-line{ + stroke-dasharray: 3; +} + +.dotted-line{ + stroke-dasharray: 1 2; +} + +#compositionStart, .composition { + fill: ${t.lineColor} !important; + stroke: ${t.lineColor} !important; + stroke-width: 1; +} + +#compositionEnd, .composition { + fill: ${t.lineColor} !important; + stroke: ${t.lineColor} !important; + stroke-width: 1; +} + +#dependencyStart, .dependency { + fill: ${t.lineColor} !important; + stroke: ${t.lineColor} !important; + stroke-width: 1; +} + +#dependencyStart, .dependency { + fill: ${t.lineColor} !important; + stroke: ${t.lineColor} !important; + stroke-width: 1; +} + +#extensionStart, .extension { + fill: transparent !important; + stroke: ${t.lineColor} !important; + stroke-width: 1; +} + +#extensionEnd, .extension { + fill: transparent !important; + stroke: ${t.lineColor} !important; + stroke-width: 1; +} + +#aggregationStart, .aggregation { + fill: transparent !important; + stroke: ${t.lineColor} !important; + stroke-width: 1; +} + +#aggregationEnd, .aggregation { + fill: transparent !important; + stroke: ${t.lineColor} !important; + stroke-width: 1; +} + +#lollipopStart, .lollipop { + fill: ${t.mainBkg} !important; + stroke: ${t.lineColor} !important; + stroke-width: 1; +} + +#lollipopEnd, .lollipop { + fill: ${t.mainBkg} !important; + stroke: ${t.lineColor} !important; + stroke-width: 1; +} + +.edgeTerminals { + font-size: 11px; + line-height: initial; +} + +.classTitleText { + text-anchor: middle; + font-size: 18px; + fill: ${t.textColor}; +} +`,"getStyles"),CE=P$e});var She,B$e,F$e,_he,z$e,Ahe,SE,Lhe=R(()=>{"use strict";Zt();xr();ut();rr();She=0,B$e=o(function(t,e,r,n,i){let a=o(function(b){switch(b){case i.db.relationType.AGGREGATION:return"aggregation";case i.db.relationType.EXTENSION:return"extension";case i.db.relationType.COMPOSITION:return"composition";case i.db.relationType.DEPENDENCY:return"dependency";case i.db.relationType.LOLLIPOP:return"lollipop"}},"getRelationType");e.points=e.points.filter(b=>!Number.isNaN(b.y));let s=e.points,l=ha().x(function(b){return b.x}).y(function(b){return b.y}).curve(vs),u=t.append("path").attr("d",l(s)).attr("id","edge"+She).attr("class","relation"),h="";n.arrowMarkerAbsolute&&(h=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search,h=h.replace(/\(/g,"\\("),h=h.replace(/\)/g,"\\)")),r.relation.lineType==1&&u.attr("class","relation dashed-line"),r.relation.lineType==10&&u.attr("class","relation dotted-line"),r.relation.type1!=="none"&&u.attr("marker-start","url("+h+"#"+a(r.relation.type1)+"Start)"),r.relation.type2!=="none"&&u.attr("marker-end","url("+h+"#"+a(r.relation.type2)+"End)");let f,d,p=e.points.length,m=Lt.calcLabelPosition(e.points);f=m.x,d=m.y;let g,y,v,x;if(p%2!==0&&p>1){let b=Lt.calcCardinalityPosition(r.relation.type1!=="none",e.points,e.points[0]),w=Lt.calcCardinalityPosition(r.relation.type2!=="none",e.points,e.points[p-1]);V.debug("cardinality_1_point "+JSON.stringify(b)),V.debug("cardinality_2_point "+JSON.stringify(w)),g=b.x,y=b.y,v=w.x,x=w.y}if(r.title!==void 0){let b=t.append("g").attr("class","classLabel"),w=b.append("text").attr("class","label").attr("x",f).attr("y",d).attr("fill","red").attr("text-anchor","middle").text(r.title);window.label=w;let S=w.node().getBBox();b.insert("rect",":first-child").attr("class","box").attr("x",S.x-n.padding/2).attr("y",S.y-n.padding/2).attr("width",S.width+n.padding).attr("height",S.height+n.padding)}V.info("Rendering relation "+JSON.stringify(r)),r.relationTitle1!==void 0&&r.relationTitle1!=="none"&&t.append("g").attr("class","cardinality").append("text").attr("class","type1").attr("x",g).attr("y",y).attr("fill","black").attr("font-size","6").text(r.relationTitle1),r.relationTitle2!==void 0&&r.relationTitle2!=="none"&&t.append("g").attr("class","cardinality").append("text").attr("class","type2").attr("x",v).attr("y",x).attr("fill","black").attr("font-size","6").text(r.relationTitle2),She++},"drawEdge"),F$e=o(function(t,e,r,n){V.debug("Rendering class ",e,r);let i=e.id,a={id:i,label:e.id,width:0,height:0},s=t.append("g").attr("id",n.db.lookUpDomId(i)).attr("class","classGroup"),l;e.link?l=s.append("svg:a").attr("xlink:href",e.link).attr("target",e.linkTarget).append("text").attr("y",r.textHeight+r.padding).attr("x",0):l=s.append("text").attr("y",r.textHeight+r.padding).attr("x",0);let u=!0;e.annotations.forEach(function(w){let S=l.append("tspan").text("\xAB"+w+"\xBB");u||S.attr("dy",r.textHeight),u=!1});let h=_he(e),f=l.append("tspan").text(h).attr("class","title");u||f.attr("dy",r.textHeight);let d=l.node().getBBox().height,p,m,g;if(e.members.length>0){p=s.append("line").attr("x1",0).attr("y1",r.padding+d+r.dividerMargin/2).attr("y2",r.padding+d+r.dividerMargin/2);let w=s.append("text").attr("x",r.padding).attr("y",d+r.dividerMargin+r.textHeight).attr("fill","white").attr("class","classText");u=!0,e.members.forEach(function(S){Ahe(w,S,u,r),u=!1}),m=w.node().getBBox()}if(e.methods.length>0){g=s.append("line").attr("x1",0).attr("y1",r.padding+d+r.dividerMargin+m.height).attr("y2",r.padding+d+r.dividerMargin+m.height);let w=s.append("text").attr("x",r.padding).attr("y",d+2*r.dividerMargin+m.height+r.textHeight).attr("fill","white").attr("class","classText");u=!0,e.methods.forEach(function(S){Ahe(w,S,u,r),u=!1})}let y=s.node().getBBox();var v=" ";e.cssClasses.length>0&&(v=v+e.cssClasses.join(" "));let b=s.insert("rect",":first-child").attr("x",0).attr("y",0).attr("width",y.width+2*r.padding).attr("height",y.height+r.padding+.5*r.dividerMargin).attr("class",v).node().getBBox().width;return l.node().childNodes.forEach(function(w){w.setAttribute("x",(b-w.getBBox().width)/2)}),e.tooltip&&l.insert("title").text(e.tooltip),p&&p.attr("x2",b),g&&g.attr("x2",b),a.width=b,a.height=y.height+r.padding+.5*r.dividerMargin,a},"drawClass"),_he=o(function(t){let e=t.id;return t.type&&(e+="<"+gh(t.type)+">"),e},"getClassTitleString"),z$e=o(function(t,e,r,n){V.debug("Rendering note ",e,r);let i=e.id,a={id:i,text:e.text,width:0,height:0},s=t.append("g").attr("id",i).attr("class","classGroup"),l=s.append("text").attr("y",r.textHeight+r.padding).attr("x",0),u=JSON.parse(`"${e.text}"`).split(` +`);u.forEach(function(p){V.debug(`Adding line: ${p}`),l.append("tspan").text(p).attr("class","title").attr("dy",r.textHeight)});let h=s.node().getBBox(),d=s.insert("rect",":first-child").attr("x",0).attr("y",0).attr("width",h.width+2*r.padding).attr("height",h.height+u.length*r.textHeight+r.padding+.5*r.dividerMargin).node().getBBox().width;return l.node().childNodes.forEach(function(p){p.setAttribute("x",(d-p.getBBox().width)/2)}),a.width=d,a.height=h.height+u.length*r.textHeight+r.padding+.5*r.dividerMargin,a},"drawNote"),Ahe=o(function(t,e,r,n){let{displayText:i,cssStyle:a}=e.getDisplayDetails(),s=t.append("tspan").attr("x",n.padding).text(i);a!==""&&s.attr("style",e.cssStyle),r||s.attr("dy",n.textHeight)},"addTspan"),SE={getClassTitleString:_he,drawClass:F$e,drawEdge:B$e,drawNote:z$e}});var _E,AE,kx,G$e,$$e,Dhe,Rhe=R(()=>{"use strict";Zt();Vd();ya();ut();Lhe();Yn();_t();_E={},AE=20,kx=o(function(t){let e=Object.entries(_E).find(r=>r[1].label===t);if(e)return e[0]},"getGraphId"),G$e=o(function(t){t.append("defs").append("marker").attr("id","extensionStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 1,7 L18,13 V 1 Z"),t.append("defs").append("marker").attr("id","extensionEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 1,1 V 13 L18,7 Z"),t.append("defs").append("marker").attr("id","compositionStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id","compositionEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id","aggregationStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id","aggregationEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id","dependencyStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 5,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id","dependencyEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},"insertMarkers"),$$e=o(function(t,e,r,n){let i=de().class;_E={},V.info("Rendering diagram "+t);let a=de().securityLevel,s;a==="sandbox"&&(s=$e("#i"+e));let l=a==="sandbox"?$e(s.nodes()[0].contentDocument.body):$e("body"),u=l.select(`[id='${e}']`);G$e(u);let h=new lr({multigraph:!0});h.setGraph({isMultiGraph:!0}),h.setDefaultEdgeLabel(function(){return{}});let f=n.db.getClasses(),d=[...f.keys()];for(let b of d){let w=f.get(b),S=SE.drawClass(u,w,i,n);_E[S.id]=S,h.setNode(S.id,S),V.info("Org height: "+S.height)}n.db.getRelations().forEach(function(b){V.info("tjoho"+kx(b.id1)+kx(b.id2)+JSON.stringify(b)),h.setEdge(kx(b.id1),kx(b.id2),{relation:b},b.title||"DEFAULT")}),n.db.getNotes().forEach(function(b){V.debug(`Adding note: ${JSON.stringify(b)}`);let w=SE.drawNote(u,b,i,n);_E[w.id]=w,h.setNode(w.id,w),b.class&&f.has(b.class)&&h.setEdge(b.id,kx(b.class),{relation:{id1:b.id,id2:b.class,relation:{type1:"none",type2:"none",lineType:10}}},"DEFAULT")}),lo(h),h.nodes().forEach(function(b){b!==void 0&&h.node(b)!==void 0&&(V.debug("Node "+b+": "+JSON.stringify(h.node(b))),l.select("#"+(n.db.lookUpDomId(b)||b)).attr("transform","translate("+(h.node(b).x-h.node(b).width/2)+","+(h.node(b).y-h.node(b).height/2)+" )"))}),h.edges().forEach(function(b){b!==void 0&&h.edge(b)!==void 0&&(V.debug("Edge "+b.v+" -> "+b.w+": "+JSON.stringify(h.edge(b))),SE.drawEdge(u,h.edge(b),h.edge(b).relation,i,n))});let g=u.node().getBBox(),y=g.width+AE*2,v=g.height+AE*2;Sr(u,v,y,i.useMaxWidth);let x=`${g.x-AE} ${g.y-AE} ${y} ${v}`;V.debug(`viewBox ${x}`),u.attr("viewBox",x)},"draw"),Dhe={draw:$$e}});var Nhe={};hr(Nhe,{diagram:()=>V$e});var V$e,Mhe=R(()=>{"use strict";TO();AO();_O();Rhe();V$e={parser:wE,db:Bg,renderer:Dhe,styles:CE,init:o(t=>{t.class||(t.class={}),t.class.arrowMarkerAbsolute=t.arrowMarkerAbsolute,Bg.clear()},"init")}});var W$e,q$e,X$e,j$e,K$e,Q$e,Z$e,J$e,eVe,tVe,rVe,LE,LO=R(()=>{"use strict";ut();W$e=o((t,e,r,n)=>{e.forEach(i=>{rVe[i](t,r,n)})},"insertMarkers"),q$e=o((t,e,r)=>{V.trace("Making markers for ",r),t.append("defs").append("marker").attr("id",r+"_"+e+"-extensionStart").attr("class","marker extension "+e).attr("refX",18).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 1,7 L18,13 V 1 Z"),t.append("defs").append("marker").attr("id",r+"_"+e+"-extensionEnd").attr("class","marker extension "+e).attr("refX",1).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 1,1 V 13 L18,7 Z")},"extension"),X$e=o((t,e,r)=>{t.append("defs").append("marker").attr("id",r+"_"+e+"-compositionStart").attr("class","marker composition "+e).attr("refX",18).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",r+"_"+e+"-compositionEnd").attr("class","marker composition "+e).attr("refX",1).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z")},"composition"),j$e=o((t,e,r)=>{t.append("defs").append("marker").attr("id",r+"_"+e+"-aggregationStart").attr("class","marker aggregation "+e).attr("refX",18).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",r+"_"+e+"-aggregationEnd").attr("class","marker aggregation "+e).attr("refX",1).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z")},"aggregation"),K$e=o((t,e,r)=>{t.append("defs").append("marker").attr("id",r+"_"+e+"-dependencyStart").attr("class","marker dependency "+e).attr("refX",6).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 5,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",r+"_"+e+"-dependencyEnd").attr("class","marker dependency "+e).attr("refX",13).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},"dependency"),Q$e=o((t,e,r)=>{t.append("defs").append("marker").attr("id",r+"_"+e+"-lollipopStart").attr("class","marker lollipop "+e).attr("refX",13).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("circle").attr("stroke","black").attr("fill","transparent").attr("cx",7).attr("cy",7).attr("r",6),t.append("defs").append("marker").attr("id",r+"_"+e+"-lollipopEnd").attr("class","marker lollipop "+e).attr("refX",1).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("circle").attr("stroke","black").attr("fill","transparent").attr("cx",7).attr("cy",7).attr("r",6)},"lollipop"),Z$e=o((t,e,r)=>{t.append("marker").attr("id",r+"_"+e+"-pointEnd").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",6).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),t.append("marker").attr("id",r+"_"+e+"-pointStart").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",4.5).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 5 L 10 10 L 10 0 z").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0")},"point"),J$e=o((t,e,r)=>{t.append("marker").attr("id",r+"_"+e+"-circleEnd").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",11).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),t.append("marker").attr("id",r+"_"+e+"-circleStart").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",-1).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0")},"circle"),eVe=o((t,e,r)=>{t.append("marker").attr("id",r+"_"+e+"-crossEnd").attr("class","marker cross "+e).attr("viewBox","0 0 11 11").attr("refX",12).attr("refY",5.2).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("path").attr("d","M 1,1 l 9,9 M 10,1 l -9,9").attr("class","arrowMarkerPath").style("stroke-width",2).style("stroke-dasharray","1,0"),t.append("marker").attr("id",r+"_"+e+"-crossStart").attr("class","marker cross "+e).attr("viewBox","0 0 11 11").attr("refX",-1).attr("refY",5.2).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("path").attr("d","M 1,1 l 9,9 M 10,1 l -9,9").attr("class","arrowMarkerPath").style("stroke-width",2).style("stroke-dasharray","1,0")},"cross"),tVe=o((t,e,r)=>{t.append("defs").append("marker").attr("id",r+"_"+e+"-barbEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",14).attr("markerUnits","strokeWidth").attr("orient","auto").append("path").attr("d","M 19,7 L9,13 L14,7 L9,1 Z")},"barb"),rVe={extension:q$e,composition:X$e,aggregation:j$e,dependency:K$e,lollipop:Q$e,point:Z$e,circle:J$e,cross:eVe,barb:tVe},LE=W$e});var tr,zl,Phe,Bhe,RE,nVe,Fhe,zhe,Fg,DE,Ghe,$he,Vhe,Uhe,Hhe=R(()=>{"use strict";ut();Pv();ya();tr={},zl={},Phe={},Bhe=o(()=>{zl={},Phe={},tr={}},"clear"),RE=o((t,e)=>(V.trace("In isDescendant",e," ",t," = ",zl[e].includes(t)),!!zl[e].includes(t)),"isDescendant"),nVe=o((t,e)=>(V.info("Descendants of ",e," is ",zl[e]),V.info("Edge is ",t),t.v===e||t.w===e?!1:zl[e]?zl[e].includes(t.v)||RE(t.v,e)||RE(t.w,e)||zl[e].includes(t.w):(V.debug("Tilt, ",e,",not in descendants"),!1)),"edgeInCluster"),Fhe=o((t,e,r,n)=>{V.warn("Copying children of ",t,"root",n,"data",e.node(t),n);let i=e.children(t)||[];t!==n&&i.push(t),V.warn("Copying (nodes) clusterId",t,"nodes",i),i.forEach(a=>{if(e.children(a).length>0)Fhe(a,e,r,n);else{let s=e.node(a);V.info("cp ",a," to ",n," with parent ",t),r.setNode(a,s),n!==e.parent(a)&&(V.warn("Setting parent",a,e.parent(a)),r.setParent(a,e.parent(a))),t!==n&&a!==t?(V.debug("Setting parent",a,t),r.setParent(a,t)):(V.info("In copy ",t,"root",n,"data",e.node(t),n),V.debug("Not Setting parent for node=",a,"cluster!==rootId",t!==n,"node!==clusterId",a!==t));let l=e.edges(a);V.debug("Copying Edges",l),l.forEach(u=>{V.info("Edge",u);let h=e.edge(u.v,u.w,u.name);V.info("Edge data",h,n);try{nVe(u,n)?(V.info("Copying as ",u.v,u.w,h,u.name),r.setEdge(u.v,u.w,h,u.name),V.info("newGraph edges ",r.edges(),r.edge(r.edges()[0]))):V.info("Skipping copy of edge ",u.v,"-->",u.w," rootId: ",n," clusterId:",t)}catch(f){V.error(f)}})}V.debug("Removing node",a),e.removeNode(a)})},"copy"),zhe=o((t,e)=>{let r=e.children(t),n=[...r];for(let i of r)Phe[i]=t,n=[...n,...zhe(i,e)];return n},"extractDescendants"),Fg=o((t,e)=>{V.trace("Searching",t);let r=e.children(t);if(V.trace("Searching children of id ",t,r),r.length<1)return V.trace("This is a valid node",t),t;for(let n of r){let i=Fg(n,e);if(i)return V.trace("Found replacement for",t," => ",i),i}},"findNonClusterChild"),DE=o(t=>!tr[t]||!tr[t].externalConnections?t:tr[t]?tr[t].id:t,"getAnchorId"),Ghe=o((t,e)=>{if(!t||e>10){V.debug("Opting out, no graph ");return}else V.debug("Opting in, graph ");t.nodes().forEach(function(r){t.children(r).length>0&&(V.warn("Cluster identified",r," Replacement id in edges: ",Fg(r,t)),zl[r]=zhe(r,t),tr[r]={id:Fg(r,t),clusterData:t.node(r)})}),t.nodes().forEach(function(r){let n=t.children(r),i=t.edges();n.length>0?(V.debug("Cluster identified",r,zl),i.forEach(a=>{if(a.v!==r&&a.w!==r){let s=RE(a.v,r),l=RE(a.w,r);s^l&&(V.warn("Edge: ",a," leaves cluster ",r),V.warn("Descendants of XXX ",r,": ",zl[r]),tr[r].externalConnections=!0)}})):V.debug("Not a cluster ",r,zl)});for(let r of Object.keys(tr)){let n=tr[r].id,i=t.parent(n);i!==r&&tr[i]&&!tr[i].externalConnections&&(tr[r].id=i)}t.edges().forEach(function(r){let n=t.edge(r);V.warn("Edge "+r.v+" -> "+r.w+": "+JSON.stringify(r)),V.warn("Edge "+r.v+" -> "+r.w+": "+JSON.stringify(t.edge(r)));let i=r.v,a=r.w;if(V.warn("Fix XXX",tr,"ids:",r.v,r.w,"Translating: ",tr[r.v]," --- ",tr[r.w]),tr[r.v]&&tr[r.w]&&tr[r.v]===tr[r.w]){V.warn("Fixing and trixing link to self - removing XXX",r.v,r.w,r.name),V.warn("Fixing and trixing - removing XXX",r.v,r.w,r.name),i=DE(r.v),a=DE(r.w),t.removeEdge(r.v,r.w,r.name);let s=r.w+"---"+r.v;t.setNode(s,{domId:s,id:s,labelStyle:"",labelText:n.label,padding:0,shape:"labelRect",style:""});let l=structuredClone(n),u=structuredClone(n);l.label="",l.arrowTypeEnd="none",u.label="",l.fromCluster=r.v,u.toCluster=r.v,t.setEdge(i,s,l,r.name+"-cyclic-special"),t.setEdge(s,a,u,r.name+"-cyclic-special")}else if(tr[r.v]||tr[r.w]){if(V.warn("Fixing and trixing - removing XXX",r.v,r.w,r.name),i=DE(r.v),a=DE(r.w),t.removeEdge(r.v,r.w,r.name),i!==r.v){let s=t.parent(i);tr[s].externalConnections=!0,n.fromCluster=r.v}if(a!==r.w){let s=t.parent(a);tr[s].externalConnections=!0,n.toCluster=r.w}V.warn("Fix Replacing with XXX",i,a,r.name),t.setEdge(i,a,n,r.name)}}),V.warn("Adjusted Graph",zn(t)),$he(t,0),V.trace(tr)},"adjustClustersAndEdges"),$he=o((t,e)=>{if(V.warn("extractor - ",e,zn(t),t.children("D")),e>10){V.error("Bailing out");return}let r=t.nodes(),n=!1;for(let i of r){let a=t.children(i);n=n||a.length>0}if(!n){V.debug("Done, no node has children",t.nodes());return}V.debug("Nodes = ",r,e);for(let i of r)if(V.debug("Extracting node",i,tr,tr[i]&&!tr[i].externalConnections,!t.parent(i),t.node(i),t.children("D")," Depth ",e),!tr[i])V.debug("Not a cluster",i,e);else if(!tr[i].externalConnections&&t.children(i)&&t.children(i).length>0){V.warn("Cluster without external connections, without a parent and with children",i,e);let s=t.graph().rankdir==="TB"?"LR":"TB";tr[i]?.clusterData?.dir&&(s=tr[i].clusterData.dir,V.warn("Fixing dir",tr[i].clusterData.dir,s));let l=new lr({multigraph:!0,compound:!0}).setGraph({rankdir:s,nodesep:50,ranksep:50,marginx:8,marginy:8}).setDefaultEdgeLabel(function(){return{}});V.warn("Old graph before copy",zn(t)),Fhe(i,t,l,i),t.setNode(i,{clusterNode:!0,id:i,clusterData:tr[i].clusterData,labelText:tr[i].labelText,graph:l}),V.warn("New graph after copy node: (",i,")",zn(l)),V.debug("Old graph after copy",zn(t))}else V.warn("Cluster ** ",i," **not meeting the criteria !externalConnections:",!tr[i].externalConnections," no parent: ",!t.parent(i)," children ",t.children(i)&&t.children(i).length>0,t.children("D"),e),V.debug(tr);r=t.nodes(),V.warn("New list of nodes",r);for(let i of r){let a=t.node(i);V.warn(" Now next level",i,a),a.clusterNode&&$he(a.graph,e+1)}},"extractor"),Vhe=o((t,e)=>{if(e.length===0)return[];let r=Object.assign(e);return e.forEach(n=>{let i=t.children(n),a=Vhe(t,i);r=[...r,...a]}),r},"sorter"),Uhe=o(t=>Vhe(t,t.children()),"sortNodesByHierarchy")});var iVe,aVe,sVe,oVe,lVe,Yhe,Whe,qhe,Xhe=R(()=>{"use strict";S9();ut();bv();Al();Zt();_t();rr();_d();iVe=o((t,e)=>{V.info("Creating subgraph rect for ",e.id,e);let r=de(),n=t.insert("g").attr("class","cluster"+(e.class?" "+e.class:"")).attr("id",e.id),i=n.insert("rect",":first-child"),a=yr(r.flowchart.htmlLabels),s=n.insert("g").attr("class","cluster-label"),l=e.labelType==="markdown"?ta(s,e.labelText,{style:e.labelStyle,useHtmlLabels:a},r):s.node().appendChild(ra(e.labelText,e.labelStyle,void 0,!0)),u=l.getBBox();if(yr(r.flowchart.htmlLabels)){let g=l.children[0],y=$e(l);u=g.getBoundingClientRect(),y.attr("width",u.width),y.attr("height",u.height)}let h=0*e.padding,f=h/2,d=e.width<=u.width+h?u.width+h:e.width;e.width<=u.width+h?e.diff=(u.width-e.width)/2-e.padding/2:e.diff=-e.padding/2,V.trace("Data ",e,JSON.stringify(e)),i.attr("style",e.style).attr("rx",e.rx).attr("ry",e.ry).attr("x",e.x-d/2).attr("y",e.y-e.height/2-f).attr("width",d).attr("height",e.height+h);let{subGraphTitleTopMargin:p}=io(r);a?s.attr("transform",`translate(${e.x-u.width/2}, ${e.y-e.height/2+p})`):s.attr("transform",`translate(${e.x}, ${e.y-e.height/2+p})`);let m=i.node().getBBox();return e.width=m.width,e.height=m.height,e.intersect=function(g){return Ad(e,g)},n},"rect"),aVe=o((t,e)=>{let r=t.insert("g").attr("class","note-cluster").attr("id",e.id),n=r.insert("rect",":first-child"),i=0*e.padding,a=i/2;n.attr("rx",e.rx).attr("ry",e.ry).attr("x",e.x-e.width/2-a).attr("y",e.y-e.height/2-a).attr("width",e.width+i).attr("height",e.height+i).attr("fill","none");let s=n.node().getBBox();return e.width=s.width,e.height=s.height,e.intersect=function(l){return Ad(e,l)},r},"noteGroup"),sVe=o((t,e)=>{let r=de(),n=t.insert("g").attr("class",e.classes).attr("id",e.id),i=n.insert("rect",":first-child"),a=n.insert("g").attr("class","cluster-label"),s=n.append("rect"),l=a.node().appendChild(ra(e.labelText,e.labelStyle,void 0,!0)),u=l.getBBox();if(yr(r.flowchart.htmlLabels)){let g=l.children[0],y=$e(l);u=g.getBoundingClientRect(),y.attr("width",u.width),y.attr("height",u.height)}u=l.getBBox();let h=0*e.padding,f=h/2,d=e.width<=u.width+e.padding?u.width+e.padding:e.width;e.width<=u.width+e.padding?e.diff=(u.width+e.padding*0-e.width)/2:e.diff=-e.padding/2,i.attr("class","outer").attr("x",e.x-d/2-f).attr("y",e.y-e.height/2-f).attr("width",d+h).attr("height",e.height+h),s.attr("class","inner").attr("x",e.x-d/2-f).attr("y",e.y-e.height/2-f+u.height-1).attr("width",d+h).attr("height",e.height+h-u.height-3);let{subGraphTitleTopMargin:p}=io(r);a.attr("transform",`translate(${e.x-u.width/2}, ${e.y-e.height/2-e.padding/3+(yr(r.flowchart.htmlLabels)?5:3)+p})`);let m=i.node().getBBox();return e.height=m.height,e.intersect=function(g){return Ad(e,g)},n},"roundedWithTitle"),oVe=o((t,e)=>{let r=t.insert("g").attr("class",e.classes).attr("id",e.id),n=r.insert("rect",":first-child"),i=0*e.padding,a=i/2;n.attr("class","divider").attr("x",e.x-e.width/2-a).attr("y",e.y-e.height/2).attr("width",e.width+i).attr("height",e.height+i);let s=n.node().getBBox();return e.width=s.width,e.height=s.height,e.diff=-e.padding/2,e.intersect=function(l){return Ad(e,l)},r},"divider"),lVe={rect:iVe,roundedWithTitle:sVe,noteGroup:aVe,divider:oVe},Yhe={},Whe=o((t,e)=>{V.trace("Inserting cluster");let r=e.shape||"rect";Yhe[e.id]=lVe[r](t,e)},"insertCluster"),qhe=o(()=>{Yhe={}},"clear")});var Khe,cVe,jhe,Qhe=R(()=>{"use strict";ut();Khe=o((t,e,r,n,i)=>{e.arrowTypeStart&&jhe(t,"start",e.arrowTypeStart,r,n,i),e.arrowTypeEnd&&jhe(t,"end",e.arrowTypeEnd,r,n,i)},"addEdgeMarkers"),cVe={arrow_cross:"cross",arrow_point:"point",arrow_barb:"barb",arrow_circle:"circle",aggregation:"aggregation",extension:"extension",composition:"composition",dependency:"dependency",lollipop:"lollipop"},jhe=o((t,e,r,n,i,a)=>{let s=cVe[r];if(!s){V.warn(`Unknown arrow type: ${r}`);return}let l=e==="start"?"Start":"End";t.attr(`marker-${e}`,`url(${n}#${i}_${a}-${s}${l})`)},"addEdgeMarker")});function NE(t,e){de().flowchart.htmlLabels&&t&&(t.style.width=e.length*9+"px",t.style.height="12px")}var ME,Ta,Jhe,IE,OE,uVe,hVe,Zhe,PE,DO=R(()=>{"use strict";ut();bv();Al();Zt();_t();xr();rr();j9();_d();Qhe();ME={},Ta={},Jhe=o(()=>{ME={},Ta={}},"clear"),IE=o((t,e)=>{let r=de(),n=yr(r.flowchart.htmlLabels),i=e.labelType==="markdown"?ta(t,e.label,{style:e.labelStyle,useHtmlLabels:n,addSvgBackground:!0},r):ra(e.label,e.labelStyle),a=t.insert("g").attr("class","edgeLabel"),s=a.insert("g").attr("class","label");s.node().appendChild(i);let l=i.getBBox();if(n){let h=i.children[0],f=$e(i);l=h.getBoundingClientRect(),f.attr("width",l.width),f.attr("height",l.height)}s.attr("transform","translate("+-l.width/2+", "+-l.height/2+")"),ME[e.id]=a,e.width=l.width,e.height=l.height;let u;if(e.startLabelLeft){let h=ra(e.startLabelLeft,e.labelStyle),f=t.insert("g").attr("class","edgeTerminals"),d=f.insert("g").attr("class","inner");u=d.node().appendChild(h);let p=h.getBBox();d.attr("transform","translate("+-p.width/2+", "+-p.height/2+")"),Ta[e.id]||(Ta[e.id]={}),Ta[e.id].startLeft=f,NE(u,e.startLabelLeft)}if(e.startLabelRight){let h=ra(e.startLabelRight,e.labelStyle),f=t.insert("g").attr("class","edgeTerminals"),d=f.insert("g").attr("class","inner");u=f.node().appendChild(h),d.node().appendChild(h);let p=h.getBBox();d.attr("transform","translate("+-p.width/2+", "+-p.height/2+")"),Ta[e.id]||(Ta[e.id]={}),Ta[e.id].startRight=f,NE(u,e.startLabelRight)}if(e.endLabelLeft){let h=ra(e.endLabelLeft,e.labelStyle),f=t.insert("g").attr("class","edgeTerminals"),d=f.insert("g").attr("class","inner");u=d.node().appendChild(h);let p=h.getBBox();d.attr("transform","translate("+-p.width/2+", "+-p.height/2+")"),f.node().appendChild(h),Ta[e.id]||(Ta[e.id]={}),Ta[e.id].endLeft=f,NE(u,e.endLabelLeft)}if(e.endLabelRight){let h=ra(e.endLabelRight,e.labelStyle),f=t.insert("g").attr("class","edgeTerminals"),d=f.insert("g").attr("class","inner");u=d.node().appendChild(h);let p=h.getBBox();d.attr("transform","translate("+-p.width/2+", "+-p.height/2+")"),f.node().appendChild(h),Ta[e.id]||(Ta[e.id]={}),Ta[e.id].endRight=f,NE(u,e.endLabelRight)}return i},"insertEdgeLabel");o(NE,"setTerminalWidth");OE=o((t,e)=>{V.debug("Moving label abc88 ",t.id,t.label,ME[t.id],e);let r=e.updatedPath?e.updatedPath:e.originalPath,n=de(),{subGraphTitleTotalMargin:i}=io(n);if(t.label){let a=ME[t.id],s=t.x,l=t.y;if(r){let u=Lt.calcLabelPosition(r);V.debug("Moving label "+t.label+" from (",s,",",l,") to (",u.x,",",u.y,") abc88"),e.updatedPath&&(s=u.x,l=u.y)}a.attr("transform",`translate(${s}, ${l+i/2})`)}if(t.startLabelLeft){let a=Ta[t.id].startLeft,s=t.x,l=t.y;if(r){let u=Lt.calcTerminalLabelPosition(t.arrowTypeStart?10:0,"start_left",r);s=u.x,l=u.y}a.attr("transform",`translate(${s}, ${l})`)}if(t.startLabelRight){let a=Ta[t.id].startRight,s=t.x,l=t.y;if(r){let u=Lt.calcTerminalLabelPosition(t.arrowTypeStart?10:0,"start_right",r);s=u.x,l=u.y}a.attr("transform",`translate(${s}, ${l})`)}if(t.endLabelLeft){let a=Ta[t.id].endLeft,s=t.x,l=t.y;if(r){let u=Lt.calcTerminalLabelPosition(t.arrowTypeEnd?10:0,"end_left",r);s=u.x,l=u.y}a.attr("transform",`translate(${s}, ${l})`)}if(t.endLabelRight){let a=Ta[t.id].endRight,s=t.x,l=t.y;if(r){let u=Lt.calcTerminalLabelPosition(t.arrowTypeEnd?10:0,"end_right",r);s=u.x,l=u.y}a.attr("transform",`translate(${s}, ${l})`)}},"positionEdgeLabel"),uVe=o((t,e)=>{let r=t.x,n=t.y,i=Math.abs(e.x-r),a=Math.abs(e.y-n),s=t.width/2,l=t.height/2;return i>=s||a>=l},"outsideNode"),hVe=o((t,e,r)=>{V.debug(`intersection calc abc89: + outsidePoint: ${JSON.stringify(e)} + insidePoint : ${JSON.stringify(r)} + node : x:${t.x} y:${t.y} w:${t.width} h:${t.height}`);let n=t.x,i=t.y,a=Math.abs(n-r.x),s=t.width/2,l=r.xMath.abs(n-e.x)*u){let d=r.y{V.debug("abc88 cutPathAtIntersect",t,e);let r=[],n=t[0],i=!1;return t.forEach(a=>{if(!uVe(e,a)&&!i){let s=hVe(e,n,a),l=!1;r.forEach(u=>{l=l||u.x===s.x&&u.y===s.y}),r.some(u=>u.x===s.x&&u.y===s.y)||r.push(s),i=!0}else n=a,i||r.push(a)}),r},"cutPathAtIntersect"),PE=o(function(t,e,r,n,i,a,s){let l=r.points;V.debug("abc88 InsertEdge: edge=",r,"e=",e);let u=!1,h=a.node(e.v);var f=a.node(e.w);f?.intersect&&h?.intersect&&(l=l.slice(1,r.points.length-1),l.unshift(h.intersect(l[0])),l.push(f.intersect(l[l.length-1]))),r.toCluster&&(V.debug("to cluster abc88",n[r.toCluster]),l=Zhe(r.points,n[r.toCluster].node),u=!0),r.fromCluster&&(V.debug("from cluster abc88",n[r.fromCluster]),l=Zhe(l.reverse(),n[r.fromCluster].node).reverse(),u=!0);let d=l.filter(S=>!Number.isNaN(S.y)),p=vs;r.curve&&(i==="graph"||i==="flowchart")&&(p=r.curve);let{x:m,y:g}=X5(r),y=ha().x(m).y(g).curve(p),v;switch(r.thickness){case"normal":v="edge-thickness-normal";break;case"thick":v="edge-thickness-thick";break;case"invisible":v="edge-thickness-thick";break;default:v=""}switch(r.pattern){case"solid":v+=" edge-pattern-solid";break;case"dotted":v+=" edge-pattern-dotted";break;case"dashed":v+=" edge-pattern-dashed";break}let x=t.append("path").attr("d",y(d)).attr("id",r.id).attr("class"," "+v+(r.classes?" "+r.classes:"")).attr("style",r.style),b="";(de().flowchart.arrowMarkerAbsolute||de().state.arrowMarkerAbsolute)&&(b=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search,b=b.replace(/\(/g,"\\("),b=b.replace(/\)/g,"\\)")),Khe(x,r,b,s,i);let w={};return u&&(w.updatedPath=l),w.originalPath=r.points,w},"insertEdge")});var efe,tfe,rfe=R(()=>{"use strict";Vd();Pv();LO();N5();Hhe();M5();Xhe();DO();ut();_d();_t();efe=o(async(t,e,r,n,i,a)=>{V.info("Graph in recursive render: XXX",zn(e),i);let s=e.graph().rankdir;V.trace("Dir in recursive render - dir:",s);let l=t.insert("g").attr("class","root");e.nodes()?V.info("Recursive render XXX",e.nodes()):V.info("No nodes found for",e),e.edges().length>0&&V.trace("Recursive edges",e.edge(e.edges()[0]));let u=l.insert("g").attr("class","clusters"),h=l.insert("g").attr("class","edgePaths"),f=l.insert("g").attr("class","edgeLabels"),d=l.insert("g").attr("class","nodes");await Promise.all(e.nodes().map(async function(g){let y=e.node(g);if(i!==void 0){let v=JSON.parse(JSON.stringify(i.clusterData));V.info("Setting data for cluster XXX (",g,") ",v,i),e.setNode(i.id,v),e.parent(g)||(V.trace("Setting parent",g,i.id),e.setParent(g,i.id,v))}if(V.info("(Insert) Node XXX"+g+": "+JSON.stringify(e.node(g))),y?.clusterNode){V.info("Cluster identified",g,y.width,e.node(g));let{ranksep:v,nodesep:x}=e.graph();y.graph.setGraph({...y.graph.graph(),ranksep:v,nodesep:x});let b=await efe(d,y.graph,r,n,e.node(g),a),w=b.elem;kn(y,w),y.diff=b.diff||0,V.info("Node bounds (abc123)",g,y,y.width,y.x,y.y),Bj(w,y),V.warn("Recursive render complete ",w,y)}else e.children(g).length>0?(V.info("Cluster - the non recursive path XXX",g,y.id,y,e),V.info(Fg(y.id,e)),tr[y.id]={id:Fg(y.id,e),node:y}):(V.info("Node - the non recursive path",g,y.id,y),await pm(d,e.node(g),s))})),e.edges().forEach(async function(g){let y=e.edge(g.v,g.w,g.name);V.info("Edge "+g.v+" -> "+g.w+": "+JSON.stringify(g)),V.info("Edge "+g.v+" -> "+g.w+": ",g," ",JSON.stringify(e.edge(g))),V.info("Fix",tr,"ids:",g.v,g.w,"Translating: ",tr[g.v],tr[g.w]),await IE(f,y)}),e.edges().forEach(function(g){V.info("Edge "+g.v+" -> "+g.w+": "+JSON.stringify(g))}),V.info("Graph before layout:",JSON.stringify(zn(e))),V.info("#############################################"),V.info("### Layout ###"),V.info("#############################################"),V.info(e),lo(e),V.info("Graph after layout:",JSON.stringify(zn(e)));let p=0,{subGraphTitleTotalMargin:m}=io(a);return Uhe(e).forEach(function(g){let y=e.node(g);V.info("Position "+g+": "+JSON.stringify(e.node(g))),V.info("Position "+g+": ("+y.x,","+y.y,") width: ",y.width," height: ",y.height),y?.clusterNode?(y.y+=m,wv(y)):e.children(g).length>0?(y.height+=m,Whe(u,y),tr[y.id].node=y):(y.y+=m/2,wv(y))}),e.edges().forEach(function(g){let y=e.edge(g);V.info("Edge "+g.v+" -> "+g.w+": "+JSON.stringify(y),y),y.points.forEach(x=>x.y+=m/2);let v=PE(h,g,y,tr,r,e,n);OE(y,v)}),e.nodes().forEach(function(g){let y=e.node(g);V.info(g,y.type,y.diff),y.type==="group"&&(p=y.diff)}),{elem:l,diff:p}},"recursiveRender"),tfe=o(async(t,e,r,n,i)=>{LE(t,r,n,i),Fj(),Jhe(),qhe(),Bhe(),V.warn("Graph at first:",JSON.stringify(zn(e))),Ghe(e),V.warn("Graph after:",JSON.stringify(zn(e)));let a=de();await efe(t,e,n,i,void 0,a)},"render")});function nfe(t){let e;switch(t){case 0:e="aggregation";break;case 1:e="extension";break;case 2:e="composition";break;case 3:e="dependency";break;case 4:e="lollipop";break;default:e="none"}return e}var NO,RO,fVe,ife,dVe,pVe,mVe,gVe,afe,sfe=R(()=>{"use strict";Zt();ya();ut();_t();rfe();xr();xr();Yn();rr();NO=o(t=>We.sanitizeText(t,de()),"sanitizeText"),RO={dividerMargin:10,padding:5,textHeight:10,curve:void 0},fVe=o(function(t,e,r,n){V.info("keys:",[...t.keys()]),V.info(t),t.forEach(function(i){let s={shape:"rect",id:i.id,domId:i.domId,labelText:NO(i.id),labelStyle:"",style:"fill: none; stroke: black",padding:de().flowchart?.padding??de().class?.padding};e.setNode(i.id,s),ife(i.classes,e,r,n,i.id),V.info("setNode",s)})},"addNamespaces"),ife=o(function(t,e,r,n,i){V.info("keys:",[...t.keys()]),V.info(t),[...t.values()].filter(a=>a.parent===i).forEach(function(a){let s=a.cssClasses.join(" "),l=lm(a.styles),u=a.label??a.id,h=0,d={labelStyle:l.labelStyle,shape:"class_box",labelText:NO(u),classData:a,rx:h,ry:h,class:s,style:l.style,id:a.id,domId:a.domId,tooltip:n.db.getTooltip(a.id,i)||"",haveCallback:a.haveCallback,link:a.link,width:a.type==="group"?500:void 0,type:a.type,padding:de().flowchart?.padding??de().class?.padding};e.setNode(a.id,d),i&&e.setParent(a.id,i),V.info("setNode",d)})},"addClasses"),dVe=o(function(t,e,r,n){V.info(t),t.forEach(function(i,a){let s=i,l="",u={labelStyle:"",style:""},h=s.text,f=0,p={labelStyle:u.labelStyle,shape:"note",labelText:NO(h),noteData:s,rx:f,ry:f,class:l,style:u.style,id:s.id,domId:s.id,tooltip:"",type:"note",padding:de().flowchart?.padding??de().class?.padding};if(e.setNode(s.id,p),V.info("setNode",p),!s.class||!n.has(s.class))return;let m=r+a,g={id:`edgeNote${m}`,classes:"relation",pattern:"dotted",arrowhead:"none",startLabelRight:"",endLabelLeft:"",arrowTypeStart:"none",arrowTypeEnd:"none",style:"fill:none",labelStyle:"",curve:om(RO.curve,xu)};e.setEdge(s.id,s.class,g,m)})},"addNotes"),pVe=o(function(t,e){let r=de().flowchart,n=0;t.forEach(function(i){n++;let a={classes:"relation",pattern:i.relation.lineType==1?"dashed":"solid",id:y5(i.id1,i.id2,{prefix:"id",counter:n}),arrowhead:i.type==="arrow_open"?"none":"normal",startLabelRight:i.relationTitle1==="none"?"":i.relationTitle1,endLabelLeft:i.relationTitle2==="none"?"":i.relationTitle2,arrowTypeStart:nfe(i.relation.type1),arrowTypeEnd:nfe(i.relation.type2),style:"fill:none",labelStyle:"",curve:om(r?.curve,xu)};if(V.info(a,i),i.style!==void 0){let s=lm(i.style);a.style=s.style,a.labelStyle=s.labelStyle}i.text=i.title,i.text===void 0?i.style!==void 0&&(a.arrowheadStyle="fill: #333"):(a.arrowheadStyle="fill: #333",a.labelpos="c",de().flowchart?.htmlLabels??de().htmlLabels?(a.labelType="html",a.label=''+i.text+""):(a.labelType="text",a.label=i.text.replace(We.lineBreakRegex,` +`),i.style===void 0&&(a.style=a.style||"stroke: #333; stroke-width: 1.5px;fill:none"),a.labelStyle=a.labelStyle.replace("color:","fill:"))),e.setEdge(i.id1,i.id2,a,n)})},"addRelations"),mVe=o(function(t){RO={...RO,...t}},"setConf"),gVe=o(async function(t,e,r,n){V.info("Drawing class - ",e);let i=de().flowchart??de().class,a=de().securityLevel;V.info("config:",i);let s=i?.nodeSpacing??50,l=i?.rankSpacing??50,u=new lr({multigraph:!0,compound:!0}).setGraph({rankdir:n.db.getDirection(),nodesep:s,ranksep:l,marginx:8,marginy:8}).setDefaultEdgeLabel(function(){return{}}),h=n.db.getNamespaces(),f=n.db.getClasses(),d=n.db.getRelations(),p=n.db.getNotes();V.info(d),fVe(h,u,e,n),ife(f,u,e,n),pVe(d,u),dVe(p,u,d.length+1,f);let m;a==="sandbox"&&(m=$e("#i"+e));let g=a==="sandbox"?$e(m.nodes()[0].contentDocument.body):$e("body"),y=g.select(`[id="${e}"]`),v=g.select("#"+e+" g");if(await tfe(v,u,["aggregation","extension","composition","dependency","lollipop"],"classDiagram",e),Lt.insertTitle(y,"classTitleText",i?.titleTopMargin??5,n.db.getDiagramTitle()),Lo(u,y,i?.diagramPadding,i?.useMaxWidth),!i?.htmlLabels){let x=a==="sandbox"?m.nodes()[0].contentDocument:document,b=x.querySelectorAll('[id="'+e+'"] .edgeLabel .label');for(let w of b){let S=w.getBBox(),T=x.createElementNS("http://www.w3.org/2000/svg","rect");T.setAttribute("rx",0),T.setAttribute("ry",0),T.setAttribute("width",S.width),T.setAttribute("height",S.height),w.insertBefore(T,w.firstChild)}}},"draw");o(nfe,"getArrowMarker");afe={setConf:mVe,draw:gVe}});var ofe={};hr(ofe,{diagram:()=>yVe});var yVe,lfe=R(()=>{"use strict";TO();AO();_O();sfe();yVe={parser:wE,db:Bg,renderer:afe,styles:CE,init:o(t=>{t.class||(t.class={}),t.class.arrowMarkerAbsolute=t.arrowMarkerAbsolute,Bg.clear()},"init")}});var MO,BE,IO=R(()=>{"use strict";MO=function(){var t=o(function(F,B,$,z){for($=$||{},z=F.length;z--;$[F[z]]=B);return $},"o"),e=[1,2],r=[1,3],n=[1,4],i=[2,4],a=[1,9],s=[1,11],l=[1,16],u=[1,17],h=[1,18],f=[1,19],d=[1,32],p=[1,20],m=[1,21],g=[1,22],y=[1,23],v=[1,24],x=[1,26],b=[1,27],w=[1,28],S=[1,29],T=[1,30],E=[1,31],_=[1,34],A=[1,35],L=[1,36],M=[1,37],N=[1,33],k=[1,4,5,16,17,19,21,22,24,25,26,27,28,29,33,35,37,38,42,45,48,49,50,51,54],I=[1,4,5,14,15,16,17,19,21,22,24,25,26,27,28,29,33,35,37,38,42,45,48,49,50,51,54],C=[4,5,16,17,19,21,22,24,25,26,27,28,29,33,35,37,38,42,45,48,49,50,51,54],O={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,start:3,SPACE:4,NL:5,SD:6,document:7,line:8,statement:9,classDefStatement:10,styleStatement:11,cssClassStatement:12,idStatement:13,DESCR:14,"-->":15,HIDE_EMPTY:16,scale:17,WIDTH:18,COMPOSIT_STATE:19,STRUCT_START:20,STRUCT_STOP:21,STATE_DESCR:22,AS:23,ID:24,FORK:25,JOIN:26,CHOICE:27,CONCURRENT:28,note:29,notePosition:30,NOTE_TEXT:31,direction:32,acc_title:33,acc_title_value:34,acc_descr:35,acc_descr_value:36,acc_descr_multiline_value:37,classDef:38,CLASSDEF_ID:39,CLASSDEF_STYLEOPTS:40,DEFAULT:41,style:42,STYLE_IDS:43,STYLEDEF_STYLEOPTS:44,class:45,CLASSENTITY_IDS:46,STYLECLASS:47,direction_tb:48,direction_bt:49,direction_rl:50,direction_lr:51,eol:52,";":53,EDGE_STATE:54,STYLE_SEPARATOR:55,left_of:56,right_of:57,$accept:0,$end:1},terminals_:{2:"error",4:"SPACE",5:"NL",6:"SD",14:"DESCR",15:"-->",16:"HIDE_EMPTY",17:"scale",18:"WIDTH",19:"COMPOSIT_STATE",20:"STRUCT_START",21:"STRUCT_STOP",22:"STATE_DESCR",23:"AS",24:"ID",25:"FORK",26:"JOIN",27:"CHOICE",28:"CONCURRENT",29:"note",31:"NOTE_TEXT",33:"acc_title",34:"acc_title_value",35:"acc_descr",36:"acc_descr_value",37:"acc_descr_multiline_value",38:"classDef",39:"CLASSDEF_ID",40:"CLASSDEF_STYLEOPTS",41:"DEFAULT",42:"style",43:"STYLE_IDS",44:"STYLEDEF_STYLEOPTS",45:"class",46:"CLASSENTITY_IDS",47:"STYLECLASS",48:"direction_tb",49:"direction_bt",50:"direction_rl",51:"direction_lr",53:";",54:"EDGE_STATE",55:"STYLE_SEPARATOR",56:"left_of",57:"right_of"},productions_:[0,[3,2],[3,2],[3,2],[7,0],[7,2],[8,2],[8,1],[8,1],[9,1],[9,1],[9,1],[9,1],[9,2],[9,3],[9,4],[9,1],[9,2],[9,1],[9,4],[9,3],[9,6],[9,1],[9,1],[9,1],[9,1],[9,4],[9,4],[9,1],[9,2],[9,2],[9,1],[10,3],[10,3],[11,3],[12,3],[32,1],[32,1],[32,1],[32,1],[52,1],[52,1],[13,1],[13,1],[13,3],[13,3],[30,1],[30,1]],performAction:o(function(B,$,z,Y,Q,X,ie){var j=X.length-1;switch(Q){case 3:return Y.setRootDoc(X[j]),X[j];break;case 4:this.$=[];break;case 5:X[j]!="nl"&&(X[j-1].push(X[j]),this.$=X[j-1]);break;case 6:case 7:this.$=X[j];break;case 8:this.$="nl";break;case 12:this.$=X[j];break;case 13:let q=X[j-1];q.description=Y.trimColon(X[j]),this.$=q;break;case 14:this.$={stmt:"relation",state1:X[j-2],state2:X[j]};break;case 15:let K=Y.trimColon(X[j]);this.$={stmt:"relation",state1:X[j-3],state2:X[j-1],description:K};break;case 19:this.$={stmt:"state",id:X[j-3],type:"default",description:"",doc:X[j-1]};break;case 20:var J=X[j],Z=X[j-2].trim();if(X[j].match(":")){var H=X[j].split(":");J=H[0],Z=[Z,H[1]]}this.$={stmt:"state",id:J,type:"default",description:Z};break;case 21:this.$={stmt:"state",id:X[j-3],type:"default",description:X[j-5],doc:X[j-1]};break;case 22:this.$={stmt:"state",id:X[j],type:"fork"};break;case 23:this.$={stmt:"state",id:X[j],type:"join"};break;case 24:this.$={stmt:"state",id:X[j],type:"choice"};break;case 25:this.$={stmt:"state",id:Y.getDividerId(),type:"divider"};break;case 26:this.$={stmt:"state",id:X[j-1].trim(),note:{position:X[j-2].trim(),text:X[j].trim()}};break;case 29:this.$=X[j].trim(),Y.setAccTitle(this.$);break;case 30:case 31:this.$=X[j].trim(),Y.setAccDescription(this.$);break;case 32:case 33:this.$={stmt:"classDef",id:X[j-1].trim(),classes:X[j].trim()};break;case 34:this.$={stmt:"style",id:X[j-1].trim(),styleClass:X[j].trim()};break;case 35:this.$={stmt:"applyClass",id:X[j-1].trim(),styleClass:X[j].trim()};break;case 36:Y.setDirection("TB"),this.$={stmt:"dir",value:"TB"};break;case 37:Y.setDirection("BT"),this.$={stmt:"dir",value:"BT"};break;case 38:Y.setDirection("RL"),this.$={stmt:"dir",value:"RL"};break;case 39:Y.setDirection("LR"),this.$={stmt:"dir",value:"LR"};break;case 42:case 43:this.$={stmt:"state",id:X[j].trim(),type:"default",description:""};break;case 44:this.$={stmt:"state",id:X[j-2].trim(),classes:[X[j].trim()],type:"default",description:""};break;case 45:this.$={stmt:"state",id:X[j-2].trim(),classes:[X[j].trim()],type:"default",description:""};break}},"anonymous"),table:[{3:1,4:e,5:r,6:n},{1:[3]},{3:5,4:e,5:r,6:n},{3:6,4:e,5:r,6:n},t([1,4,5,16,17,19,22,24,25,26,27,28,29,33,35,37,38,42,45,48,49,50,51,54],i,{7:7}),{1:[2,1]},{1:[2,2]},{1:[2,3],4:a,5:s,8:8,9:10,10:12,11:13,12:14,13:15,16:l,17:u,19:h,22:f,24:d,25:p,26:m,27:g,28:y,29:v,32:25,33:x,35:b,37:w,38:S,42:T,45:E,48:_,49:A,50:L,51:M,54:N},t(k,[2,5]),{9:38,10:12,11:13,12:14,13:15,16:l,17:u,19:h,22:f,24:d,25:p,26:m,27:g,28:y,29:v,32:25,33:x,35:b,37:w,38:S,42:T,45:E,48:_,49:A,50:L,51:M,54:N},t(k,[2,7]),t(k,[2,8]),t(k,[2,9]),t(k,[2,10]),t(k,[2,11]),t(k,[2,12],{14:[1,39],15:[1,40]}),t(k,[2,16]),{18:[1,41]},t(k,[2,18],{20:[1,42]}),{23:[1,43]},t(k,[2,22]),t(k,[2,23]),t(k,[2,24]),t(k,[2,25]),{30:44,31:[1,45],56:[1,46],57:[1,47]},t(k,[2,28]),{34:[1,48]},{36:[1,49]},t(k,[2,31]),{39:[1,50],41:[1,51]},{43:[1,52]},{46:[1,53]},t(I,[2,42],{55:[1,54]}),t(I,[2,43],{55:[1,55]}),t(k,[2,36]),t(k,[2,37]),t(k,[2,38]),t(k,[2,39]),t(k,[2,6]),t(k,[2,13]),{13:56,24:d,54:N},t(k,[2,17]),t(C,i,{7:57}),{24:[1,58]},{24:[1,59]},{23:[1,60]},{24:[2,46]},{24:[2,47]},t(k,[2,29]),t(k,[2,30]),{40:[1,61]},{40:[1,62]},{44:[1,63]},{47:[1,64]},{24:[1,65]},{24:[1,66]},t(k,[2,14],{14:[1,67]}),{4:a,5:s,8:8,9:10,10:12,11:13,12:14,13:15,16:l,17:u,19:h,21:[1,68],22:f,24:d,25:p,26:m,27:g,28:y,29:v,32:25,33:x,35:b,37:w,38:S,42:T,45:E,48:_,49:A,50:L,51:M,54:N},t(k,[2,20],{20:[1,69]}),{31:[1,70]},{24:[1,71]},t(k,[2,32]),t(k,[2,33]),t(k,[2,34]),t(k,[2,35]),t(I,[2,44]),t(I,[2,45]),t(k,[2,15]),t(k,[2,19]),t(C,i,{7:72}),t(k,[2,26]),t(k,[2,27]),{4:a,5:s,8:8,9:10,10:12,11:13,12:14,13:15,16:l,17:u,19:h,21:[1,73],22:f,24:d,25:p,26:m,27:g,28:y,29:v,32:25,33:x,35:b,37:w,38:S,42:T,45:E,48:_,49:A,50:L,51:M,54:N},t(k,[2,21])],defaultActions:{5:[2,1],6:[2,2],46:[2,46],47:[2,47]},parseError:o(function(B,$){if($.recoverable)this.trace(B);else{var z=new Error(B);throw z.hash=$,z}},"parseError"),parse:o(function(B){var $=this,z=[0],Y=[],Q=[null],X=[],ie=this.table,j="",J=0,Z=0,H=0,q=2,K=1,se=X.slice.call(arguments,1),ce=Object.create(this.lexer),ue={yy:{}};for(var te in this.yy)Object.prototype.hasOwnProperty.call(this.yy,te)&&(ue.yy[te]=this.yy[te]);ce.setInput(B,ue.yy),ue.yy.lexer=ce,ue.yy.parser=this,typeof ce.yylloc>"u"&&(ce.yylloc={});var De=ce.yylloc;X.push(De);var oe=ce.options&&ce.options.ranges;typeof ue.yy.parseError=="function"?this.parseError=ue.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function ke(we){z.length=z.length-2*we,Q.length=Q.length-we,X.length=X.length-we}o(ke,"popStack");function Ie(){var we;return we=Y.pop()||ce.lex()||K,typeof we!="number"&&(we instanceof Array&&(Y=we,we=Y.pop()),we=$.symbols_[we]||we),we}o(Ie,"lex");for(var Se,Ue,Pe,_e,me,W,fe={},ge,re,he,ne;;){if(Pe=z[z.length-1],this.defaultActions[Pe]?_e=this.defaultActions[Pe]:((Se===null||typeof Se>"u")&&(Se=Ie()),_e=ie[Pe]&&ie[Pe][Se]),typeof _e>"u"||!_e.length||!_e[0]){var ae="";ne=[];for(ge in ie[Pe])this.terminals_[ge]&&ge>q&&ne.push("'"+this.terminals_[ge]+"'");ce.showPosition?ae="Parse error on line "+(J+1)+`: +`+ce.showPosition()+` +Expecting `+ne.join(", ")+", got '"+(this.terminals_[Se]||Se)+"'":ae="Parse error on line "+(J+1)+": Unexpected "+(Se==K?"end of input":"'"+(this.terminals_[Se]||Se)+"'"),this.parseError(ae,{text:ce.match,token:this.terminals_[Se]||Se,line:ce.yylineno,loc:De,expected:ne})}if(_e[0]instanceof Array&&_e.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Pe+", token: "+Se);switch(_e[0]){case 1:z.push(Se),Q.push(ce.yytext),X.push(ce.yylloc),z.push(_e[1]),Se=null,Ue?(Se=Ue,Ue=null):(Z=ce.yyleng,j=ce.yytext,J=ce.yylineno,De=ce.yylloc,H>0&&H--);break;case 2:if(re=this.productions_[_e[1]][1],fe.$=Q[Q.length-re],fe._$={first_line:X[X.length-(re||1)].first_line,last_line:X[X.length-1].last_line,first_column:X[X.length-(re||1)].first_column,last_column:X[X.length-1].last_column},oe&&(fe._$.range=[X[X.length-(re||1)].range[0],X[X.length-1].range[1]]),W=this.performAction.apply(fe,[j,Z,J,ue.yy,_e[1],Q,X].concat(se)),typeof W<"u")return W;re&&(z=z.slice(0,-1*re*2),Q=Q.slice(0,-1*re),X=X.slice(0,-1*re)),z.push(this.productions_[_e[1]][0]),Q.push(fe.$),X.push(fe._$),he=ie[z[z.length-2]][z[z.length-1]],z.push(he);break;case 3:return!0}}return!0},"parse")},D=function(){var F={EOF:1,parseError:o(function($,z){if(this.yy.parser)this.yy.parser.parseError($,z);else throw new Error($)},"parseError"),setInput:o(function(B,$){return this.yy=$||this.yy||{},this._input=B,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var B=this._input[0];this.yytext+=B,this.yyleng++,this.offset++,this.match+=B,this.matched+=B;var $=B.match(/(?:\r\n?|\n).*/g);return $?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),B},"input"),unput:o(function(B){var $=B.length,z=B.split(/(?:\r\n?|\n)/g);this._input=B+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-$),this.offset-=$;var Y=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),z.length-1&&(this.yylineno-=z.length-1);var Q=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:z?(z.length===Y.length?this.yylloc.first_column:0)+Y[Y.length-z.length].length-z[0].length:this.yylloc.first_column-$},this.options.ranges&&(this.yylloc.range=[Q[0],Q[0]+this.yyleng-$]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(B){this.unput(this.match.slice(B))},"less"),pastInput:o(function(){var B=this.matched.substr(0,this.matched.length-this.match.length);return(B.length>20?"...":"")+B.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var B=this.match;return B.length<20&&(B+=this._input.substr(0,20-B.length)),(B.substr(0,20)+(B.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var B=this.pastInput(),$=new Array(B.length+1).join("-");return B+this.upcomingInput()+` +`+$+"^"},"showPosition"),test_match:o(function(B,$){var z,Y,Q;if(this.options.backtrack_lexer&&(Q={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(Q.yylloc.range=this.yylloc.range.slice(0))),Y=B[0].match(/(?:\r\n?|\n).*/g),Y&&(this.yylineno+=Y.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:Y?Y[Y.length-1].length-Y[Y.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+B[0].length},this.yytext+=B[0],this.match+=B[0],this.matches=B,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(B[0].length),this.matched+=B[0],z=this.performAction.call(this,this.yy,this,$,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),z)return z;if(this._backtrack){for(var X in Q)this[X]=Q[X];return!1}return!1},"test_match"),next:o(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var B,$,z,Y;this._more||(this.yytext="",this.match="");for(var Q=this._currentRules(),X=0;X$[0].length)){if($=z,Y=X,this.options.backtrack_lexer){if(B=this.test_match(z,Q[X]),B!==!1)return B;if(this._backtrack){$=!1;continue}else return!1}else if(!this.options.flex)break}return $?(B=this.test_match($,Q[Y]),B!==!1?B:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:o(function(){var $=this.next();return $||this.lex()},"lex"),begin:o(function($){this.conditionStack.push($)},"begin"),popState:o(function(){var $=this.conditionStack.length-1;return $>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:o(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:o(function($){return $=this.conditionStack.length-1-Math.abs($||0),$>=0?this.conditionStack[$]:"INITIAL"},"topState"),pushState:o(function($){this.begin($)},"pushState"),stateStackSize:o(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:o(function($,z,Y,Q){var X=Q;switch(Y){case 0:return 41;case 1:return 48;case 2:return 49;case 3:return 50;case 4:return 51;case 5:break;case 6:break;case 7:return 5;case 8:break;case 9:break;case 10:break;case 11:break;case 12:return this.pushState("SCALE"),17;break;case 13:return 18;case 14:this.popState();break;case 15:return this.begin("acc_title"),33;break;case 16:return this.popState(),"acc_title_value";break;case 17:return this.begin("acc_descr"),35;break;case 18:return this.popState(),"acc_descr_value";break;case 19:this.begin("acc_descr_multiline");break;case 20:this.popState();break;case 21:return"acc_descr_multiline_value";case 22:return this.pushState("CLASSDEF"),38;break;case 23:return this.popState(),this.pushState("CLASSDEFID"),"DEFAULT_CLASSDEF_ID";break;case 24:return this.popState(),this.pushState("CLASSDEFID"),39;break;case 25:return this.popState(),40;break;case 26:return this.pushState("CLASS"),45;break;case 27:return this.popState(),this.pushState("CLASS_STYLE"),46;break;case 28:return this.popState(),47;break;case 29:return this.pushState("STYLE"),42;break;case 30:return this.popState(),this.pushState("STYLEDEF_STYLES"),43;break;case 31:return this.popState(),44;break;case 32:return this.pushState("SCALE"),17;break;case 33:return 18;case 34:this.popState();break;case 35:this.pushState("STATE");break;case 36:return this.popState(),z.yytext=z.yytext.slice(0,-8).trim(),25;break;case 37:return this.popState(),z.yytext=z.yytext.slice(0,-8).trim(),26;break;case 38:return this.popState(),z.yytext=z.yytext.slice(0,-10).trim(),27;break;case 39:return this.popState(),z.yytext=z.yytext.slice(0,-8).trim(),25;break;case 40:return this.popState(),z.yytext=z.yytext.slice(0,-8).trim(),26;break;case 41:return this.popState(),z.yytext=z.yytext.slice(0,-10).trim(),27;break;case 42:return 48;case 43:return 49;case 44:return 50;case 45:return 51;case 46:this.pushState("STATE_STRING");break;case 47:return this.pushState("STATE_ID"),"AS";break;case 48:return this.popState(),"ID";break;case 49:this.popState();break;case 50:return"STATE_DESCR";case 51:return 19;case 52:this.popState();break;case 53:return this.popState(),this.pushState("struct"),20;break;case 54:break;case 55:return this.popState(),21;break;case 56:break;case 57:return this.begin("NOTE"),29;break;case 58:return this.popState(),this.pushState("NOTE_ID"),56;break;case 59:return this.popState(),this.pushState("NOTE_ID"),57;break;case 60:this.popState(),this.pushState("FLOATING_NOTE");break;case 61:return this.popState(),this.pushState("FLOATING_NOTE_ID"),"AS";break;case 62:break;case 63:return"NOTE_TEXT";case 64:return this.popState(),"ID";break;case 65:return this.popState(),this.pushState("NOTE_TEXT"),24;break;case 66:return this.popState(),z.yytext=z.yytext.substr(2).trim(),31;break;case 67:return this.popState(),z.yytext=z.yytext.slice(0,-8).trim(),31;break;case 68:return 6;case 69:return 6;case 70:return 16;case 71:return 54;case 72:return 24;case 73:return z.yytext=z.yytext.trim(),14;break;case 74:return 15;case 75:return 28;case 76:return 55;case 77:return 5;case 78:return"INVALID"}},"anonymous"),rules:[/^(?:default\b)/i,/^(?:.*direction\s+TB[^\n]*)/i,/^(?:.*direction\s+BT[^\n]*)/i,/^(?:.*direction\s+RL[^\n]*)/i,/^(?:.*direction\s+LR[^\n]*)/i,/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:[\s]+)/i,/^(?:((?!\n)\s)+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:scale\s+)/i,/^(?:\d+)/i,/^(?:\s+width\b)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?:classDef\s+)/i,/^(?:DEFAULT\s+)/i,/^(?:\w+\s+)/i,/^(?:[^\n]*)/i,/^(?:class\s+)/i,/^(?:(\w+)+((,\s*\w+)*))/i,/^(?:[^\n]*)/i,/^(?:style\s+)/i,/^(?:[\w,]+\s+)/i,/^(?:[^\n]*)/i,/^(?:scale\s+)/i,/^(?:\d+)/i,/^(?:\s+width\b)/i,/^(?:state\s+)/i,/^(?:.*<>)/i,/^(?:.*<>)/i,/^(?:.*<>)/i,/^(?:.*\[\[fork\]\])/i,/^(?:.*\[\[join\]\])/i,/^(?:.*\[\[choice\]\])/i,/^(?:.*direction\s+TB[^\n]*)/i,/^(?:.*direction\s+BT[^\n]*)/i,/^(?:.*direction\s+RL[^\n]*)/i,/^(?:.*direction\s+LR[^\n]*)/i,/^(?:["])/i,/^(?:\s*as\s+)/i,/^(?:[^\n\{]*)/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[^\n\s\{]+)/i,/^(?:\n)/i,/^(?:\{)/i,/^(?:%%(?!\{)[^\n]*)/i,/^(?:\})/i,/^(?:[\n])/i,/^(?:note\s+)/i,/^(?:left of\b)/i,/^(?:right of\b)/i,/^(?:")/i,/^(?:\s*as\s*)/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[^\n]*)/i,/^(?:\s*[^:\n\s\-]+)/i,/^(?:\s*:[^:\n;]+)/i,/^(?:[\s\S]*?end note\b)/i,/^(?:stateDiagram\s+)/i,/^(?:stateDiagram-v2\s+)/i,/^(?:hide empty description\b)/i,/^(?:\[\*\])/i,/^(?:[^:\n\s\-\{]+)/i,/^(?:\s*:[^:\n;]+)/i,/^(?:-->)/i,/^(?:--)/i,/^(?::::)/i,/^(?:$)/i,/^(?:.)/i],conditions:{LINE:{rules:[9,10],inclusive:!1},struct:{rules:[9,10,22,26,29,35,42,43,44,45,54,55,56,57,71,72,73,74,75],inclusive:!1},FLOATING_NOTE_ID:{rules:[64],inclusive:!1},FLOATING_NOTE:{rules:[61,62,63],inclusive:!1},NOTE_TEXT:{rules:[66,67],inclusive:!1},NOTE_ID:{rules:[65],inclusive:!1},NOTE:{rules:[58,59,60],inclusive:!1},STYLEDEF_STYLEOPTS:{rules:[],inclusive:!1},STYLEDEF_STYLES:{rules:[31],inclusive:!1},STYLE_IDS:{rules:[],inclusive:!1},STYLE:{rules:[30],inclusive:!1},CLASS_STYLE:{rules:[28],inclusive:!1},CLASS:{rules:[27],inclusive:!1},CLASSDEFID:{rules:[25],inclusive:!1},CLASSDEF:{rules:[23,24],inclusive:!1},acc_descr_multiline:{rules:[20,21],inclusive:!1},acc_descr:{rules:[18],inclusive:!1},acc_title:{rules:[16],inclusive:!1},SCALE:{rules:[13,14,33,34],inclusive:!1},ALIAS:{rules:[],inclusive:!1},STATE_ID:{rules:[48],inclusive:!1},STATE_STRING:{rules:[49,50],inclusive:!1},FORK_STATE:{rules:[],inclusive:!1},STATE:{rules:[9,10,36,37,38,39,40,41,46,47,51,52,53],inclusive:!1},ID:{rules:[9,10],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,7,8,10,11,12,15,17,19,22,26,29,32,35,53,57,68,69,70,71,72,73,74,76,77,78],inclusive:!0}}};return F}();O.lexer=D;function P(){this.yy={}}return o(P,"Parser"),P.prototype=O,O.Parser=P,new P}();MO.parser=MO;BE=MO});var hfe,FE,zg,Ex,ffe,dfe,pfe,N0,zE,OO,PO,BO,FO,zO,GE,$E,mfe,gfe,GO,$O,yfe,vfe,Gg,wVe,xfe,VO,TVe,kVe,bfe,wfe,EVe,Tfe,CVe,kfe,UO,HO,Efe,VE,Cfe,YO,UE=R(()=>{"use strict";hfe="LR",FE="TB",zg="state",Ex="relation",ffe="classDef",dfe="style",pfe="applyClass",N0="default",zE="divider",OO="fill:none",PO="fill: #333",BO="c",FO="text",zO="normal",GE="rect",$E="rectWithTitle",mfe="stateStart",gfe="stateEnd",GO="divider",$O="roundedWithTitle",yfe="note",vfe="noteGroup",Gg="statediagram",wVe="state",xfe=`${Gg}-${wVe}`,VO="transition",TVe="note",kVe="note-edge",bfe=`${VO} ${kVe}`,wfe=`${Gg}-${TVe}`,EVe="cluster",Tfe=`${Gg}-${EVe}`,CVe="cluster-alt",kfe=`${Gg}-${CVe}`,UO="parent",HO="note",Efe="state",VE="----",Cfe=`${VE}${HO}`,YO=`${VE}${UO}`});function WO(t="",e=0,r="",n=VE){let i=r!==null&&r.length>0?`${n}${r}`:"";return`${Efe}-${t}${i}-${e}`}function HE(t,e,r){if(!e.id||e.id===""||e.id==="")return;e.cssClasses&&(Array.isArray(e.cssCompiledStyles)||(e.cssCompiledStyles=[]),e.cssClasses.split(" ").forEach(i=>{if(r.get(i)){let a=r.get(i);e.cssCompiledStyles=[...e.cssCompiledStyles,...a.styles]}}));let n=t.find(i=>i.id===e.id);n?Object.assign(n,e):t.push(e)}function AVe(t){return t?.classes?.join(" ")??""}function _Ve(t){return t?.styles??[]}var YE,yf,SVe,Sfe,$g,Afe,_fe=R(()=>{"use strict";_t();ut();rr();UE();YE=new Map,yf=0;o(WO,"stateDomId");SVe=o((t,e,r,n,i,a,s,l)=>{V.trace("items",e),e.forEach(u=>{switch(u.stmt){case zg:$g(t,u,r,n,i,a,s,l);break;case N0:$g(t,u,r,n,i,a,s,l);break;case Ex:{$g(t,u.state1,r,n,i,a,s,l),$g(t,u.state2,r,n,i,a,s,l);let h={id:"edge"+yf,start:u.state1.id,end:u.state2.id,arrowhead:"normal",arrowTypeEnd:"arrow_barb",style:OO,labelStyle:"",label:We.sanitizeText(u.description,de()),arrowheadStyle:PO,labelpos:BO,labelType:FO,thickness:zO,classes:VO,look:s};i.push(h),yf++}break}})},"setupDoc"),Sfe=o((t,e=FE)=>{let r=e;if(t.doc)for(let n of t.doc)n.stmt==="dir"&&(r=n.value);return r},"getDir");o(HE,"insertOrUpdateNode");o(AVe,"getClassesFromDbInfo");o(_Ve,"getStylesFromDbInfo");$g=o((t,e,r,n,i,a,s,l)=>{let u=e.id,h=r.get(u),f=AVe(h),d=_Ve(h);if(V.info("dataFetcher parsedItem",e,h,d),u!=="root"){let p=GE;e.start===!0?p=mfe:e.start===!1&&(p=gfe),e.type!==N0&&(p=e.type),YE.get(u)||YE.set(u,{id:u,shape:p,description:We.sanitizeText(u,de()),cssClasses:`${f} ${xfe}`,cssStyles:d});let m=YE.get(u);e.description&&(Array.isArray(m.description)?(m.shape=$E,m.description.push(e.description)):m.description?.length>0?(m.shape=$E,m.description===u?m.description=[e.description]:m.description=[m.description,e.description]):(m.shape=GE,m.description=e.description),m.description=We.sanitizeTextOrArray(m.description,de())),m.description?.length===1&&m.shape===$E&&(m.type==="group"?m.shape=$O:m.shape=GE),!m.type&&e.doc&&(V.info("Setting cluster for XCX",u,Sfe(e)),m.type="group",m.isGroup=!0,m.dir=Sfe(e),m.shape=e.type===zE?GO:$O,m.cssClasses=`${m.cssClasses} ${Tfe} ${a?kfe:""}`);let g={labelStyle:"",shape:m.shape,label:m.description,cssClasses:m.cssClasses,cssCompiledStyles:[],cssStyles:m.cssStyles,id:u,dir:m.dir,domId:WO(u,yf),type:m.type,isGroup:m.type==="group",padding:8,rx:10,ry:10,look:s};if(g.shape===GO&&(g.label=""),t&&t.id!=="root"&&(V.trace("Setting node ",u," to be child of its parent ",t.id),g.parentId=t.id),g.centerLabel=!0,e.note){let y={labelStyle:"",shape:yfe,label:e.note.text,cssClasses:wfe,cssStyles:[],cssCompilesStyles:[],id:u+Cfe+"-"+yf,domId:WO(u,yf,HO),type:m.type,isGroup:m.type==="group",padding:de().flowchart.padding,look:s,position:e.note.position},v=u+YO,x={labelStyle:"",shape:vfe,label:e.note.text,cssClasses:m.cssClasses,cssStyles:[],id:u+YO,domId:WO(u,yf,UO),type:"group",isGroup:!0,padding:16,look:s,position:e.note.position};yf++,x.id=v,y.parentId=v,HE(n,x,l),HE(n,y,l),HE(n,g,l);let b=u,w=y.id;e.note.position==="left of"&&(b=y.id,w=u),i.push({id:b+"-"+w,start:b,end:w,arrowhead:"none",arrowTypeEnd:"",style:OO,labelStyle:"",classes:bfe,arrowheadStyle:PO,labelpos:BO,labelType:FO,thickness:zO,look:s})}else HE(n,g,l)}e.doc&&(V.trace("Adding nodes children "),SVe(e,e.doc,r,n,i,!a,s,l))},"dataFetcher"),Afe=o(()=>{YE.clear(),yf=0},"reset")});var qO,LVe,DVe,Lfe,XO=R(()=>{"use strict";_t();ut();L9();oT();yD();xr();UE();qO=o((t,e=FE)=>{if(!t.doc)return e;let r=e;for(let n of t.doc)n.stmt==="dir"&&(r=n.value);return r},"getDir"),LVe=o(function(t,e){return e.db.extract(e.db.getRootDocV2()),e.db.getClasses()},"getClasses"),DVe=o(async function(t,e,r,n){V.info("REF0:"),V.info("Drawing state diagram (v2)",e);let{securityLevel:i,state:a,layout:s}=de();n.db.extract(n.db.getRootDocV2());let l=n.db.getData(),u=I5(e,i);l.type=n.type,l.layoutAlgorithm=s,l.nodeSpacing=a?.nodeSpacing||50,l.rankSpacing=a?.rankSpacing||50,l.markers=["barb"],l.diagramId=e,await sT(l,u);let h=8;Lt.insertTitle(u,"statediagramTitleText",a?.titleTopMargin??25,n.db.getDiagramTitle()),lT(u,h,Gg,a?.useMaxWidth??!0)},"draw"),Lfe={getClasses:LVe,draw:DVe,getDir:qO}});function Pfe(){return new Map}function jO(t=""){let e=t;return t===ZO&&(Cx++,e=`${Mfe}${Cx}`),e}function KO(t="",e=N0){return t===ZO?Mfe:e}function GVe(t=""){let e=t;return t===Ife&&(Cx++,e=`${Ofe}${Cx}`),e}function $Ve(t="",e=N0){return t===Ife?Ofe:e}function VVe(t,e,r){let n=jO(t.id.trim()),i=KO(t.id.trim(),t.type),a=jO(e.id.trim()),s=KO(e.id.trim(),e.type);vf(n,i,t.doc,t.description,t.note,t.classes,t.styles,t.textStyles),vf(a,s,e.doc,e.description,e.note,e.classes,e.styles,e.textStyles),Fs.relations.push({id1:n,id2:a,relationTitle:We.sanitizeText(r,de())})}var ZO,Mfe,Ife,Ofe,Dfe,Rfe,RVe,NVe,XE,JO,Bfe,jE,Vg,Ffe,KE,Fs,Cx,Nfe,MVe,IVe,WE,OVe,PVe,qE,eP,BVe,vf,zfe,M0,Gfe,FVe,zVe,$fe,QO,UVe,HVe,Vfe,YVe,tP,WVe,qVe,XVe,jVe,KVe,QVe,Qo,QE=R(()=>{"use strict";ut();xr();rr();_t();bi();_fe();XO();UE();ZO="[*]",Mfe="start",Ife=ZO,Ofe="end",Dfe="color",Rfe="fill",RVe="bgFill",NVe=",";o(Pfe,"newClassesList");XE=[],JO=[],Bfe=hfe,jE=[],Vg=Pfe(),Ffe=o(()=>({relations:[],states:new Map,documents:{}}),"newDoc"),KE={root:Ffe()},Fs=KE.root,Cx=0,Nfe=0,MVe={LINE:0,DOTTED_LINE:1},IVe={AGGREGATION:0,EXTENSION:1,COMPOSITION:2,DEPENDENCY:3},WE=o(t=>JSON.parse(JSON.stringify(t)),"clone"),OVe=o(t=>{V.info("Setting root doc",t),jE=t},"setRootDoc"),PVe=o(()=>jE,"getRootDoc"),qE=o((t,e,r)=>{if(e.stmt===Ex)qE(t,e.state1,!0),qE(t,e.state2,!1);else if(e.stmt===zg&&(e.id==="[*]"?(e.id=r?t.id+"_start":t.id+"_end",e.start=r):e.id=e.id.trim()),e.doc){let n=[],i=[],a;for(a=0;a0&&i.length>0){let s={stmt:zg,id:Z_(),type:"divider",doc:WE(i)};n.push(WE(s)),e.doc=n}e.doc.forEach(s=>qE(e,s,!0))}},"docTranslator"),eP=o(()=>(qE({id:"root"},{id:"root",doc:jE},!0),{id:"root",doc:jE}),"getRootDocV2"),BVe=o(t=>{let e;t.doc?e=t.doc:e=t,V.info(e),zfe(!0),V.info("Extract initial document:",e),e.forEach(a=>{switch(V.warn("Statement",a.stmt),a.stmt){case zg:vf(a.id.trim(),a.type,a.doc,a.description,a.note,a.classes,a.styles,a.textStyles);break;case Ex:$fe(a.state1,a.state2,a.description);break;case ffe:Vfe(a.id.trim(),a.classes);break;case dfe:{let s=a.id.trim().split(","),l=a.styleClass.split(",");s.forEach(u=>{let h=M0(u);if(h===void 0){let f=u.trim();vf(f),h=M0(f)}h.styles=l.map(f=>f.replace(/;/g,"")?.trim())})}break;case pfe:tP(a.id.trim(),a.styleClass);break}});let r=Gfe(),i=de().look;Afe(),$g(void 0,eP(),r,XE,JO,!0,i,Vg),XE.forEach(a=>{if(Array.isArray(a.label)){if(a.description=a.label.slice(1),a.isGroup&&a.description.length>0)throw new Error("Group nodes can only have label. Remove the additional description for node ["+a.id+"]");a.label=a.label[0]}})},"extract"),vf=o(function(t,e=N0,r=null,n=null,i=null,a=null,s=null,l=null){let u=t?.trim();if(Fs.states.has(u)?(Fs.states.get(u).doc||(Fs.states.get(u).doc=r),Fs.states.get(u).type||(Fs.states.get(u).type=e)):(V.info("Adding state ",u,n),Fs.states.set(u,{id:u,descriptions:[],type:e,doc:r,note:i,classes:[],styles:[],textStyles:[]})),n&&(V.info("Setting state description",u,n),typeof n=="string"&&QO(u,n.trim()),typeof n=="object"&&n.forEach(h=>QO(u,h.trim()))),i){let h=Fs.states.get(u);h.note=i,h.note.text=We.sanitizeText(h.note.text,de())}a&&(V.info("Setting state classes",u,a),(typeof a=="string"?[a]:a).forEach(f=>tP(u,f.trim()))),s&&(V.info("Setting state styles",u,s),(typeof s=="string"?[s]:s).forEach(f=>WVe(u,f.trim()))),l&&(V.info("Setting state styles",u,s),(typeof l=="string"?[l]:l).forEach(f=>qVe(u,f.trim())))},"addState"),zfe=o(function(t){XE=[],JO=[],KE={root:Ffe()},Fs=KE.root,Cx=0,Vg=Pfe(),t||vr()},"clear"),M0=o(function(t){return Fs.states.get(t)},"getState"),Gfe=o(function(){return Fs.states},"getStates"),FVe=o(function(){V.info("Documents = ",KE)},"logDocuments"),zVe=o(function(){return Fs.relations},"getRelations");o(jO,"startIdIfNeeded");o(KO,"startTypeIfNeeded");o(GVe,"endIdIfNeeded");o($Ve,"endTypeIfNeeded");o(VVe,"addRelationObjs");$fe=o(function(t,e,r){if(typeof t=="object")VVe(t,e,r);else{let n=jO(t.trim()),i=KO(t),a=GVe(e.trim()),s=$Ve(e);vf(n,i),vf(a,s),Fs.relations.push({id1:n,id2:a,title:We.sanitizeText(r,de())})}},"addRelation"),QO=o(function(t,e){let r=Fs.states.get(t),n=e.startsWith(":")?e.replace(":","").trim():e;r.descriptions.push(We.sanitizeText(n,de()))},"addDescription"),UVe=o(function(t){return t.substring(0,1)===":"?t.substr(2).trim():t.trim()},"cleanupLabel"),HVe=o(()=>(Nfe++,"divider-id-"+Nfe),"getDividerId"),Vfe=o(function(t,e=""){Vg.has(t)||Vg.set(t,{id:t,styles:[],textStyles:[]});let r=Vg.get(t);e?.split(NVe).forEach(n=>{let i=n.replace(/([^;]*);/,"$1").trim();if(RegExp(Dfe).exec(n)){let s=i.replace(Rfe,RVe).replace(Dfe,Rfe);r.textStyles.push(s)}r.styles.push(i)})},"addStyleClass"),YVe=o(function(){return Vg},"getClasses"),tP=o(function(t,e){t.split(",").forEach(function(r){let n=M0(r);if(n===void 0){let i=r.trim();vf(i),n=M0(i)}n.classes.push(e)})},"setCssClass"),WVe=o(function(t,e){let r=M0(t);r!==void 0&&r.styles.push(e)},"setStyle"),qVe=o(function(t,e){let r=M0(t);r!==void 0&&r.textStyles.push(e)},"setTextStyle"),XVe=o(()=>Bfe,"getDirection"),jVe=o(t=>{Bfe=t},"setDirection"),KVe=o(t=>t&&t[0]===":"?t.substr(1).trim():t.trim(),"trimColon"),QVe=o(()=>{let t=de();return{nodes:XE,edges:JO,other:{},config:t,direction:qO(eP())}},"getData"),Qo={getConfig:o(()=>de().state,"getConfig"),getData:QVe,addState:vf,clear:zfe,getState:M0,getStates:Gfe,getRelations:zVe,getClasses:YVe,getDirection:XVe,addRelation:$fe,getDividerId:HVe,setDirection:jVe,cleanupLabel:UVe,lineType:MVe,relationType:IVe,logDocuments:FVe,getRootDoc:PVe,setRootDoc:OVe,getRootDocV2:eP,extract:BVe,trimColon:KVe,getAccTitle:Ar,setAccTitle:kr,getAccDescription:Lr,setAccDescription:_r,addStyleClass:Vfe,setCssClass:tP,addDescription:QO,setDiagramTitle:nn,getDiagramTitle:Xr}});var ZVe,ZE,rP=R(()=>{"use strict";ZVe=o(t=>` +defs #statediagram-barbEnd { + fill: ${t.transitionColor}; + stroke: ${t.transitionColor}; + } +g.stateGroup text { + fill: ${t.nodeBorder}; + stroke: none; + font-size: 10px; +} +g.stateGroup text { + fill: ${t.textColor}; + stroke: none; + font-size: 10px; + +} +g.stateGroup .state-title { + font-weight: bolder; + fill: ${t.stateLabelColor}; +} + +g.stateGroup rect { + fill: ${t.mainBkg}; + stroke: ${t.nodeBorder}; +} + +g.stateGroup line { + stroke: ${t.lineColor}; + stroke-width: 1; +} + +.transition { + stroke: ${t.transitionColor}; + stroke-width: 1; + fill: none; +} + +.stateGroup .composit { + fill: ${t.background}; + border-bottom: 1px +} + +.stateGroup .alt-composit { + fill: #e0e0e0; + border-bottom: 1px +} + +.state-note { + stroke: ${t.noteBorderColor}; + fill: ${t.noteBkgColor}; + + text { + fill: ${t.noteTextColor}; + stroke: none; + font-size: 10px; + } +} + +.stateLabel .box { + stroke: none; + stroke-width: 0; + fill: ${t.mainBkg}; + opacity: 0.5; +} + +.edgeLabel .label rect { + fill: ${t.labelBackgroundColor}; + opacity: 0.5; +} +.edgeLabel { + background-color: ${t.edgeLabelBackground}; + p { + background-color: ${t.edgeLabelBackground}; + } + rect { + opacity: 0.5; + background-color: ${t.edgeLabelBackground}; + fill: ${t.edgeLabelBackground}; + } + text-align: center; +} +.edgeLabel .label text { + fill: ${t.transitionLabelColor||t.tertiaryTextColor}; +} +.label div .edgeLabel { + color: ${t.transitionLabelColor||t.tertiaryTextColor}; +} + +.stateLabel text { + fill: ${t.stateLabelColor}; + font-size: 10px; + font-weight: bold; +} + +.node circle.state-start { + fill: ${t.specialStateColor}; + stroke: ${t.specialStateColor}; +} + +.node .fork-join { + fill: ${t.specialStateColor}; + stroke: ${t.specialStateColor}; +} + +.node circle.state-end { + fill: ${t.innerEndBackground}; + stroke: ${t.background}; + stroke-width: 1.5 +} +.end-state-inner { + fill: ${t.compositeBackground||t.background}; + // stroke: ${t.background}; + stroke-width: 1.5 +} + +.node rect { + fill: ${t.stateBkg||t.mainBkg}; + stroke: ${t.stateBorder||t.nodeBorder}; + stroke-width: 1px; +} +.node polygon { + fill: ${t.mainBkg}; + stroke: ${t.stateBorder||t.nodeBorder};; + stroke-width: 1px; +} +#statediagram-barbEnd { + fill: ${t.lineColor}; +} + +.statediagram-cluster rect { + fill: ${t.compositeTitleBackground}; + stroke: ${t.stateBorder||t.nodeBorder}; + stroke-width: 1px; +} + +.cluster-label, .nodeLabel { + color: ${t.stateLabelColor}; + // line-height: 1; +} + +.statediagram-cluster rect.outer { + rx: 5px; + ry: 5px; +} +.statediagram-state .divider { + stroke: ${t.stateBorder||t.nodeBorder}; +} + +.statediagram-state .title-state { + rx: 5px; + ry: 5px; +} +.statediagram-cluster.statediagram-cluster .inner { + fill: ${t.compositeBackground||t.background}; +} +.statediagram-cluster.statediagram-cluster-alt .inner { + fill: ${t.altBackground?t.altBackground:"#efefef"}; +} + +.statediagram-cluster .inner { + rx:0; + ry:0; +} + +.statediagram-state rect.basic { + rx: 5px; + ry: 5px; +} +.statediagram-state rect.divider { + stroke-dasharray: 10,10; + fill: ${t.altBackground?t.altBackground:"#efefef"}; +} + +.note-edge { + stroke-dasharray: 5; +} + +.statediagram-note rect { + fill: ${t.noteBkgColor}; + stroke: ${t.noteBorderColor}; + stroke-width: 1px; + rx: 0; + ry: 0; +} +.statediagram-note rect { + fill: ${t.noteBkgColor}; + stroke: ${t.noteBorderColor}; + stroke-width: 1px; + rx: 0; + ry: 0; +} + +.statediagram-note text { + fill: ${t.noteTextColor}; +} + +.statediagram-note .nodeLabel { + color: ${t.noteTextColor}; +} +.statediagram .edgeLabel { + color: red; // ${t.noteTextColor}; +} + +#dependencyStart, #dependencyEnd { + fill: ${t.lineColor}; + stroke: ${t.lineColor}; + stroke-width: 1; +} + +.statediagramTitleText { + text-anchor: middle; + font-size: 18px; + fill: ${t.textColor}; +} +`,"getStyles"),ZE=ZVe});var nP,JVe,eUe,Ufe,tUe,Hfe,Yfe=R(()=>{"use strict";nP={},JVe=o((t,e)=>{nP[t]=e},"set"),eUe=o(t=>nP[t],"get"),Ufe=o(()=>Object.keys(nP),"keys"),tUe=o(()=>Ufe().length,"size"),Hfe={get:eUe,set:JVe,keys:Ufe,size:tUe}});var rUe,nUe,iUe,aUe,qfe,sUe,oUe,lUe,cUe,iP,Wfe,Xfe,jfe=R(()=>{"use strict";Zt();Yfe();QE();xr();rr();_t();ut();rUe=o(t=>t.append("circle").attr("class","start-state").attr("r",de().state.sizeUnit).attr("cx",de().state.padding+de().state.sizeUnit).attr("cy",de().state.padding+de().state.sizeUnit),"drawStartState"),nUe=o(t=>t.append("line").style("stroke","grey").style("stroke-dasharray","3").attr("x1",de().state.textHeight).attr("class","divider").attr("x2",de().state.textHeight*2).attr("y1",0).attr("y2",0),"drawDivider"),iUe=o((t,e)=>{let r=t.append("text").attr("x",2*de().state.padding).attr("y",de().state.textHeight+2*de().state.padding).attr("font-size",de().state.fontSize).attr("class","state-title").text(e.id),n=r.node().getBBox();return t.insert("rect",":first-child").attr("x",de().state.padding).attr("y",de().state.padding).attr("width",n.width+2*de().state.padding).attr("height",n.height+2*de().state.padding).attr("rx",de().state.radius),r},"drawSimpleState"),aUe=o((t,e)=>{let r=o(function(p,m,g){let y=p.append("tspan").attr("x",2*de().state.padding).text(m);g||y.attr("dy",de().state.textHeight)},"addTspan"),i=t.append("text").attr("x",2*de().state.padding).attr("y",de().state.textHeight+1.3*de().state.padding).attr("font-size",de().state.fontSize).attr("class","state-title").text(e.descriptions[0]).node().getBBox(),a=i.height,s=t.append("text").attr("x",de().state.padding).attr("y",a+de().state.padding*.4+de().state.dividerMargin+de().state.textHeight).attr("class","state-description"),l=!0,u=!0;e.descriptions.forEach(function(p){l||(r(s,p,u),u=!1),l=!1});let h=t.append("line").attr("x1",de().state.padding).attr("y1",de().state.padding+a+de().state.dividerMargin/2).attr("y2",de().state.padding+a+de().state.dividerMargin/2).attr("class","descr-divider"),f=s.node().getBBox(),d=Math.max(f.width,i.width);return h.attr("x2",d+3*de().state.padding),t.insert("rect",":first-child").attr("x",de().state.padding).attr("y",de().state.padding).attr("width",d+2*de().state.padding).attr("height",f.height+a+2*de().state.padding).attr("rx",de().state.radius),t},"drawDescrState"),qfe=o((t,e,r)=>{let n=de().state.padding,i=2*de().state.padding,a=t.node().getBBox(),s=a.width,l=a.x,u=t.append("text").attr("x",0).attr("y",de().state.titleShift).attr("font-size",de().state.fontSize).attr("class","state-title").text(e.id),f=u.node().getBBox().width+i,d=Math.max(f,s);d===s&&(d=d+i);let p,m=t.node().getBBox();e.doc,p=l-n,f>s&&(p=(s-d)/2+n),Math.abs(l-m.x)s&&(p=l-(f-s)/2);let g=1-de().state.textHeight;return t.insert("rect",":first-child").attr("x",p).attr("y",g).attr("class",r?"alt-composit":"composit").attr("width",d).attr("height",m.height+de().state.textHeight+de().state.titleShift+1).attr("rx","0"),u.attr("x",p+n),f<=s&&u.attr("x",l+(d-i)/2-f/2+n),t.insert("rect",":first-child").attr("x",p).attr("y",de().state.titleShift-de().state.textHeight-de().state.padding).attr("width",d).attr("height",de().state.textHeight*3).attr("rx",de().state.radius),t.insert("rect",":first-child").attr("x",p).attr("y",de().state.titleShift-de().state.textHeight-de().state.padding).attr("width",d).attr("height",m.height+3+2*de().state.textHeight).attr("rx",de().state.radius),t},"addTitleAndBox"),sUe=o(t=>(t.append("circle").attr("class","end-state-outer").attr("r",de().state.sizeUnit+de().state.miniPadding).attr("cx",de().state.padding+de().state.sizeUnit+de().state.miniPadding).attr("cy",de().state.padding+de().state.sizeUnit+de().state.miniPadding),t.append("circle").attr("class","end-state-inner").attr("r",de().state.sizeUnit).attr("cx",de().state.padding+de().state.sizeUnit+2).attr("cy",de().state.padding+de().state.sizeUnit+2)),"drawEndState"),oUe=o((t,e)=>{let r=de().state.forkWidth,n=de().state.forkHeight;if(e.parentId){let i=r;r=n,n=i}return t.append("rect").style("stroke","black").style("fill","black").attr("width",r).attr("height",n).attr("x",de().state.padding).attr("y",de().state.padding)},"drawForkJoinState"),lUe=o((t,e,r,n)=>{let i=0,a=n.append("text");a.style("text-anchor","start"),a.attr("class","noteText");let s=t.replace(/\r\n/g,"
    ");s=s.replace(/\n/g,"
    ");let l=s.split(We.lineBreakRegex),u=1.25*de().state.noteMargin;for(let h of l){let f=h.trim();if(f.length>0){let d=a.append("tspan");if(d.text(f),u===0){let p=d.node().getBBox();u+=p.height}i+=u,d.attr("x",e+de().state.noteMargin),d.attr("y",r+i+1.25*de().state.noteMargin)}}return{textWidth:a.node().getBBox().width,textHeight:i}},"_drawLongText"),cUe=o((t,e)=>{e.attr("class","state-note");let r=e.append("rect").attr("x",0).attr("y",de().state.padding),n=e.append("g"),{textWidth:i,textHeight:a}=lUe(t,0,0,n);return r.attr("height",a+2*de().state.noteMargin),r.attr("width",i+de().state.noteMargin*2),r},"drawNote"),iP=o(function(t,e){let r=e.id,n={id:r,label:e.id,width:0,height:0},i=t.append("g").attr("id",r).attr("class","stateGroup");e.type==="start"&&rUe(i),e.type==="end"&&sUe(i),(e.type==="fork"||e.type==="join")&&oUe(i,e),e.type==="note"&&cUe(e.note.text,i),e.type==="divider"&&nUe(i),e.type==="default"&&e.descriptions.length===0&&iUe(i,e),e.type==="default"&&e.descriptions.length>0&&aUe(i,e);let a=i.node().getBBox();return n.width=a.width+2*de().state.padding,n.height=a.height+2*de().state.padding,Hfe.set(r,n),n},"drawState"),Wfe=0,Xfe=o(function(t,e,r){let n=o(function(u){switch(u){case Qo.relationType.AGGREGATION:return"aggregation";case Qo.relationType.EXTENSION:return"extension";case Qo.relationType.COMPOSITION:return"composition";case Qo.relationType.DEPENDENCY:return"dependency"}},"getRelationType");e.points=e.points.filter(u=>!Number.isNaN(u.y));let i=e.points,a=ha().x(function(u){return u.x}).y(function(u){return u.y}).curve(vs),s=t.append("path").attr("d",a(i)).attr("id","edge"+Wfe).attr("class","transition"),l="";if(de().state.arrowMarkerAbsolute&&(l=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search,l=l.replace(/\(/g,"\\("),l=l.replace(/\)/g,"\\)")),s.attr("marker-end","url("+l+"#"+n(Qo.relationType.DEPENDENCY)+"End)"),r.title!==void 0){let u=t.append("g").attr("class","stateLabel"),{x:h,y:f}=Lt.calcLabelPosition(e.points),d=We.getRows(r.title),p=0,m=[],g=0,y=0;for(let b=0;b<=d.length;b++){let w=u.append("text").attr("text-anchor","middle").text(d[b]).attr("x",h).attr("y",f+p),S=w.node().getBBox();g=Math.max(g,S.width),y=Math.min(y,S.x),V.info(S.x,h,f+p),p===0&&(p=w.node().getBBox().height,V.info("Title height",p,f)),m.push(w)}let v=p*d.length;if(d.length>1){let b=(d.length-1)*p*.5;m.forEach((w,S)=>w.attr("y",f+S*p-b)),v=p*d.length}let x=u.node().getBBox();u.insert("rect",":first-child").attr("class","box").attr("x",h-g/2-de().state.padding/2).attr("y",f-v/2-de().state.padding/2-3.5).attr("width",g+de().state.padding).attr("height",v+de().state.padding),V.info(x)}Wfe++},"drawEdge")});var vo,aP,uUe,hUe,fUe,dUe,Kfe,Qfe,Zfe=R(()=>{"use strict";Zt();Vd();ya();ut();rr();jfe();_t();Yn();aP={},uUe=o(function(){},"setConf"),hUe=o(function(t){t.append("defs").append("marker").attr("id","dependencyEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 19,7 L9,13 L14,7 L9,1 Z")},"insertMarkers"),fUe=o(function(t,e,r,n){vo=de().state;let i=de().securityLevel,a;i==="sandbox"&&(a=$e("#i"+e));let s=i==="sandbox"?$e(a.nodes()[0].contentDocument.body):$e("body"),l=i==="sandbox"?a.nodes()[0].contentDocument:document;V.debug("Rendering diagram "+t);let u=s.select(`[id='${e}']`);hUe(u);let h=n.db.getRootDoc();Kfe(h,u,void 0,!1,s,l,n);let f=vo.padding,d=u.node().getBBox(),p=d.width+f*2,m=d.height+f*2,g=p*1.75;Sr(u,m,g,vo.useMaxWidth),u.attr("viewBox",`${d.x-vo.padding} ${d.y-vo.padding} `+p+" "+m)},"draw"),dUe=o(t=>t?t.length*vo.fontSizeFactor:1,"getLabelWidth"),Kfe=o((t,e,r,n,i,a,s)=>{let l=new lr({compound:!0,multigraph:!0}),u,h=!0;for(u=0;u{let T=S.parentElement,E=0,_=0;T&&(T.parentElement&&(E=T.parentElement.getBBox().width),_=parseInt(T.getAttribute("data-x-shift"),10),Number.isNaN(_)&&(_=0)),S.setAttribute("x1",0-_+8),S.setAttribute("x2",E-_-8)})):V.debug("No Node "+b+": "+JSON.stringify(l.node(b)))});let v=y.getBBox();l.edges().forEach(function(b){b!==void 0&&l.edge(b)!==void 0&&(V.debug("Edge "+b.v+" -> "+b.w+": "+JSON.stringify(l.edge(b))),Xfe(e,l.edge(b),l.edge(b).relation))}),v=y.getBBox();let x={id:r||"root",label:r||"root",width:0,height:0};return x.width=v.width+2*vo.padding,x.height=v.height+2*vo.padding,V.debug("Doc rendered",x,l),x},"renderDoc"),Qfe={setConf:uUe,draw:fUe}});var Jfe={};hr(Jfe,{diagram:()=>pUe});var pUe,ede=R(()=>{"use strict";IO();QE();rP();Zfe();pUe={parser:BE,db:Qo,renderer:Qfe,styles:ZE,init:o(t=>{t.state||(t.state={}),t.state.arrowMarkerAbsolute=t.arrowMarkerAbsolute,Qo.clear()},"init")}});var nde={};hr(nde,{diagram:()=>vUe});var vUe,ide=R(()=>{"use strict";IO();QE();rP();XO();vUe={parser:BE,db:Qo,renderer:Lfe,styles:ZE,init:o(t=>{t.state||(t.state={}),t.state.arrowMarkerAbsolute=t.arrowMarkerAbsolute,Qo.clear()},"init")}});var sP,ode,lde=R(()=>{"use strict";sP=function(){var t=o(function(d,p,m,g){for(m=m||{},g=d.length;g--;m[d[g]]=p);return m},"o"),e=[6,8,10,11,12,14,16,17,18],r=[1,9],n=[1,10],i=[1,11],a=[1,12],s=[1,13],l=[1,14],u={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,start:3,journey:4,document:5,EOF:6,line:7,SPACE:8,statement:9,NEWLINE:10,title:11,acc_title:12,acc_title_value:13,acc_descr:14,acc_descr_value:15,acc_descr_multiline_value:16,section:17,taskName:18,taskData:19,$accept:0,$end:1},terminals_:{2:"error",4:"journey",6:"EOF",8:"SPACE",10:"NEWLINE",11:"title",12:"acc_title",13:"acc_title_value",14:"acc_descr",15:"acc_descr_value",16:"acc_descr_multiline_value",17:"section",18:"taskName",19:"taskData"},productions_:[0,[3,3],[5,0],[5,2],[7,2],[7,1],[7,1],[7,1],[9,1],[9,2],[9,2],[9,1],[9,1],[9,2]],performAction:o(function(p,m,g,y,v,x,b){var w=x.length-1;switch(v){case 1:return x[w-1];case 2:this.$=[];break;case 3:x[w-1].push(x[w]),this.$=x[w-1];break;case 4:case 5:this.$=x[w];break;case 6:case 7:this.$=[];break;case 8:y.setDiagramTitle(x[w].substr(6)),this.$=x[w].substr(6);break;case 9:this.$=x[w].trim(),y.setAccTitle(this.$);break;case 10:case 11:this.$=x[w].trim(),y.setAccDescription(this.$);break;case 12:y.addSection(x[w].substr(8)),this.$=x[w].substr(8);break;case 13:y.addTask(x[w-1],x[w]),this.$="task";break}},"anonymous"),table:[{3:1,4:[1,2]},{1:[3]},t(e,[2,2],{5:3}),{6:[1,4],7:5,8:[1,6],9:7,10:[1,8],11:r,12:n,14:i,16:a,17:s,18:l},t(e,[2,7],{1:[2,1]}),t(e,[2,3]),{9:15,11:r,12:n,14:i,16:a,17:s,18:l},t(e,[2,5]),t(e,[2,6]),t(e,[2,8]),{13:[1,16]},{15:[1,17]},t(e,[2,11]),t(e,[2,12]),{19:[1,18]},t(e,[2,4]),t(e,[2,9]),t(e,[2,10]),t(e,[2,13])],defaultActions:{},parseError:o(function(p,m){if(m.recoverable)this.trace(p);else{var g=new Error(p);throw g.hash=m,g}},"parseError"),parse:o(function(p){var m=this,g=[0],y=[],v=[null],x=[],b=this.table,w="",S=0,T=0,E=0,_=2,A=1,L=x.slice.call(arguments,1),M=Object.create(this.lexer),N={yy:{}};for(var k in this.yy)Object.prototype.hasOwnProperty.call(this.yy,k)&&(N.yy[k]=this.yy[k]);M.setInput(p,N.yy),N.yy.lexer=M,N.yy.parser=this,typeof M.yylloc>"u"&&(M.yylloc={});var I=M.yylloc;x.push(I);var C=M.options&&M.options.ranges;typeof N.yy.parseError=="function"?this.parseError=N.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function O(H){g.length=g.length-2*H,v.length=v.length-H,x.length=x.length-H}o(O,"popStack");function D(){var H;return H=y.pop()||M.lex()||A,typeof H!="number"&&(H instanceof Array&&(y=H,H=y.pop()),H=m.symbols_[H]||H),H}o(D,"lex");for(var P,F,B,$,z,Y,Q={},X,ie,j,J;;){if(B=g[g.length-1],this.defaultActions[B]?$=this.defaultActions[B]:((P===null||typeof P>"u")&&(P=D()),$=b[B]&&b[B][P]),typeof $>"u"||!$.length||!$[0]){var Z="";J=[];for(X in b[B])this.terminals_[X]&&X>_&&J.push("'"+this.terminals_[X]+"'");M.showPosition?Z="Parse error on line "+(S+1)+`: +`+M.showPosition()+` +Expecting `+J.join(", ")+", got '"+(this.terminals_[P]||P)+"'":Z="Parse error on line "+(S+1)+": Unexpected "+(P==A?"end of input":"'"+(this.terminals_[P]||P)+"'"),this.parseError(Z,{text:M.match,token:this.terminals_[P]||P,line:M.yylineno,loc:I,expected:J})}if($[0]instanceof Array&&$.length>1)throw new Error("Parse Error: multiple actions possible at state: "+B+", token: "+P);switch($[0]){case 1:g.push(P),v.push(M.yytext),x.push(M.yylloc),g.push($[1]),P=null,F?(P=F,F=null):(T=M.yyleng,w=M.yytext,S=M.yylineno,I=M.yylloc,E>0&&E--);break;case 2:if(ie=this.productions_[$[1]][1],Q.$=v[v.length-ie],Q._$={first_line:x[x.length-(ie||1)].first_line,last_line:x[x.length-1].last_line,first_column:x[x.length-(ie||1)].first_column,last_column:x[x.length-1].last_column},C&&(Q._$.range=[x[x.length-(ie||1)].range[0],x[x.length-1].range[1]]),Y=this.performAction.apply(Q,[w,T,S,N.yy,$[1],v,x].concat(L)),typeof Y<"u")return Y;ie&&(g=g.slice(0,-1*ie*2),v=v.slice(0,-1*ie),x=x.slice(0,-1*ie)),g.push(this.productions_[$[1]][0]),v.push(Q.$),x.push(Q._$),j=b[g[g.length-2]][g[g.length-1]],g.push(j);break;case 3:return!0}}return!0},"parse")},h=function(){var d={EOF:1,parseError:o(function(m,g){if(this.yy.parser)this.yy.parser.parseError(m,g);else throw new Error(m)},"parseError"),setInput:o(function(p,m){return this.yy=m||this.yy||{},this._input=p,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var p=this._input[0];this.yytext+=p,this.yyleng++,this.offset++,this.match+=p,this.matched+=p;var m=p.match(/(?:\r\n?|\n).*/g);return m?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),p},"input"),unput:o(function(p){var m=p.length,g=p.split(/(?:\r\n?|\n)/g);this._input=p+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-m),this.offset-=m;var y=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),g.length-1&&(this.yylineno-=g.length-1);var v=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:g?(g.length===y.length?this.yylloc.first_column:0)+y[y.length-g.length].length-g[0].length:this.yylloc.first_column-m},this.options.ranges&&(this.yylloc.range=[v[0],v[0]+this.yyleng-m]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(p){this.unput(this.match.slice(p))},"less"),pastInput:o(function(){var p=this.matched.substr(0,this.matched.length-this.match.length);return(p.length>20?"...":"")+p.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var p=this.match;return p.length<20&&(p+=this._input.substr(0,20-p.length)),(p.substr(0,20)+(p.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var p=this.pastInput(),m=new Array(p.length+1).join("-");return p+this.upcomingInput()+` +`+m+"^"},"showPosition"),test_match:o(function(p,m){var g,y,v;if(this.options.backtrack_lexer&&(v={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(v.yylloc.range=this.yylloc.range.slice(0))),y=p[0].match(/(?:\r\n?|\n).*/g),y&&(this.yylineno+=y.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:y?y[y.length-1].length-y[y.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+p[0].length},this.yytext+=p[0],this.match+=p[0],this.matches=p,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(p[0].length),this.matched+=p[0],g=this.performAction.call(this,this.yy,this,m,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),g)return g;if(this._backtrack){for(var x in v)this[x]=v[x];return!1}return!1},"test_match"),next:o(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var p,m,g,y;this._more||(this.yytext="",this.match="");for(var v=this._currentRules(),x=0;xm[0].length)){if(m=g,y=x,this.options.backtrack_lexer){if(p=this.test_match(g,v[x]),p!==!1)return p;if(this._backtrack){m=!1;continue}else return!1}else if(!this.options.flex)break}return m?(p=this.test_match(m,v[y]),p!==!1?p:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:o(function(){var m=this.next();return m||this.lex()},"lex"),begin:o(function(m){this.conditionStack.push(m)},"begin"),popState:o(function(){var m=this.conditionStack.length-1;return m>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:o(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:o(function(m){return m=this.conditionStack.length-1-Math.abs(m||0),m>=0?this.conditionStack[m]:"INITIAL"},"topState"),pushState:o(function(m){this.begin(m)},"pushState"),stateStackSize:o(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:o(function(m,g,y,v){var x=v;switch(y){case 0:break;case 1:break;case 2:return 10;case 3:break;case 4:break;case 5:return 4;case 6:return 11;case 7:return this.begin("acc_title"),12;break;case 8:return this.popState(),"acc_title_value";break;case 9:return this.begin("acc_descr"),14;break;case 10:return this.popState(),"acc_descr_value";break;case 11:this.begin("acc_descr_multiline");break;case 12:this.popState();break;case 13:return"acc_descr_multiline_value";case 14:return 17;case 15:return 18;case 16:return 19;case 17:return":";case 18:return 6;case 19:return"INVALID"}},"anonymous"),rules:[/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:journey\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?:section\s[^#:\n;]+)/i,/^(?:[^#:\n;]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],conditions:{acc_descr_multiline:{rules:[12,13],inclusive:!1},acc_descr:{rules:[10],inclusive:!1},acc_title:{rules:[8],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,7,9,11,14,15,16,17,18,19],inclusive:!0}}};return d}();u.lexer=h;function f(){this.yy={}}return o(f,"Parser"),f.prototype=u,u.Parser=f,new f}();sP.parser=sP;ode=sP});var Ug,oP,Sx,Ax,TUe,kUe,EUe,CUe,SUe,AUe,_Ue,cde,LUe,lP,ude=R(()=>{"use strict";_t();bi();Ug="",oP=[],Sx=[],Ax=[],TUe=o(function(){oP.length=0,Sx.length=0,Ug="",Ax.length=0,vr()},"clear"),kUe=o(function(t){Ug=t,oP.push(t)},"addSection"),EUe=o(function(){return oP},"getSections"),CUe=o(function(){let t=cde(),e=100,r=0;for(;!t&&r{r.people&&t.push(...r.people)}),[...new Set(t)].sort()},"updateActors"),AUe=o(function(t,e){let r=e.substr(1).split(":"),n=0,i=[];r.length===1?(n=Number(r[0]),i=[]):(n=Number(r[0]),i=r[1].split(","));let a=i.map(l=>l.trim()),s={section:Ug,type:Ug,people:a,task:t,score:n};Ax.push(s)},"addTask"),_Ue=o(function(t){let e={section:Ug,type:Ug,description:t,task:t,classes:[]};Sx.push(e)},"addTaskOrg"),cde=o(function(){let t=o(function(r){return Ax[r].processed},"compileTask"),e=!0;for(let[r,n]of Ax.entries())t(r),e=e&&n.processed;return e},"compileTasks"),LUe=o(function(){return SUe()},"getActors"),lP={getConfig:o(()=>de().journey,"getConfig"),clear:TUe,setDiagramTitle:nn,getDiagramTitle:Xr,setAccTitle:kr,getAccTitle:Ar,setAccDescription:_r,getAccDescription:Lr,addSection:kUe,getSections:EUe,getTasks:CUe,addTask:AUe,addTaskOrg:_Ue,getActors:LUe}});var DUe,hde,fde=R(()=>{"use strict";DUe=o(t=>`.label { + font-family: 'trebuchet ms', verdana, arial, sans-serif; + font-family: var(--mermaid-font-family); + color: ${t.textColor}; + } + .mouth { + stroke: #666; + } + + line { + stroke: ${t.textColor} + } + + .legend { + fill: ${t.textColor}; + } + + .label text { + fill: #333; + } + .label { + color: ${t.textColor} + } + + .face { + ${t.faceColor?`fill: ${t.faceColor}`:"fill: #FFF8DC"}; + stroke: #999; + } + + .node rect, + .node circle, + .node ellipse, + .node polygon, + .node path { + fill: ${t.mainBkg}; + stroke: ${t.nodeBorder}; + stroke-width: 1px; + } + + .node .label { + text-align: center; + } + .node.clickable { + cursor: pointer; + } + + .arrowheadPath { + fill: ${t.arrowheadColor}; + } + + .edgePath .path { + stroke: ${t.lineColor}; + stroke-width: 1.5px; + } + + .flowchart-link { + stroke: ${t.lineColor}; + fill: none; + } + + .edgeLabel { + background-color: ${t.edgeLabelBackground}; + rect { + opacity: 0.5; + } + text-align: center; + } + + .cluster rect { + } + + .cluster text { + fill: ${t.titleColor}; + } + + div.mermaidTooltip { + position: absolute; + text-align: center; + max-width: 200px; + padding: 2px; + font-family: 'trebuchet ms', verdana, arial, sans-serif; + font-family: var(--mermaid-font-family); + font-size: 12px; + background: ${t.tertiaryColor}; + border: 1px solid ${t.border2}; + border-radius: 2px; + pointer-events: none; + z-index: 100; + } + + .task-type-0, .section-type-0 { + ${t.fillType0?`fill: ${t.fillType0}`:""}; + } + .task-type-1, .section-type-1 { + ${t.fillType0?`fill: ${t.fillType1}`:""}; + } + .task-type-2, .section-type-2 { + ${t.fillType0?`fill: ${t.fillType2}`:""}; + } + .task-type-3, .section-type-3 { + ${t.fillType0?`fill: ${t.fillType3}`:""}; + } + .task-type-4, .section-type-4 { + ${t.fillType0?`fill: ${t.fillType4}`:""}; + } + .task-type-5, .section-type-5 { + ${t.fillType0?`fill: ${t.fillType5}`:""}; + } + .task-type-6, .section-type-6 { + ${t.fillType0?`fill: ${t.fillType6}`:""}; + } + .task-type-7, .section-type-7 { + ${t.fillType0?`fill: ${t.fillType7}`:""}; + } + + .actor-0 { + ${t.actor0?`fill: ${t.actor0}`:""}; + } + .actor-1 { + ${t.actor1?`fill: ${t.actor1}`:""}; + } + .actor-2 { + ${t.actor2?`fill: ${t.actor2}`:""}; + } + .actor-3 { + ${t.actor3?`fill: ${t.actor3}`:""}; + } + .actor-4 { + ${t.actor4?`fill: ${t.actor4}`:""}; + } + .actor-5 { + ${t.actor5?`fill: ${t.actor5}`:""}; + } +`,"getStyles"),hde=DUe});var cP,RUe,pde,mde,NUe,MUe,dde,IUe,OUe,gde,PUe,Hg,yde=R(()=>{"use strict";Zt();Qy();cP=o(function(t,e){return yd(t,e)},"drawRect"),RUe=o(function(t,e){let n=t.append("circle").attr("cx",e.cx).attr("cy",e.cy).attr("class","face").attr("r",15).attr("stroke-width",2).attr("overflow","visible"),i=t.append("g");i.append("circle").attr("cx",e.cx-15/3).attr("cy",e.cy-15/3).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666"),i.append("circle").attr("cx",e.cx+15/3).attr("cy",e.cy-15/3).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666");function a(u){let h=bl().startAngle(Math.PI/2).endAngle(3*(Math.PI/2)).innerRadius(7.5).outerRadius(6.8181818181818175);u.append("path").attr("class","mouth").attr("d",h).attr("transform","translate("+e.cx+","+(e.cy+2)+")")}o(a,"smile");function s(u){let h=bl().startAngle(3*Math.PI/2).endAngle(5*(Math.PI/2)).innerRadius(7.5).outerRadius(6.8181818181818175);u.append("path").attr("class","mouth").attr("d",h).attr("transform","translate("+e.cx+","+(e.cy+7)+")")}o(s,"sad");function l(u){u.append("line").attr("class","mouth").attr("stroke",2).attr("x1",e.cx-5).attr("y1",e.cy+7).attr("x2",e.cx+5).attr("y2",e.cy+7).attr("class","mouth").attr("stroke-width","1px").attr("stroke","#666")}return o(l,"ambivalent"),e.score>3?a(i):e.score<3?s(i):l(i),n},"drawFace"),pde=o(function(t,e){let r=t.append("circle");return r.attr("cx",e.cx),r.attr("cy",e.cy),r.attr("class","actor-"+e.pos),r.attr("fill",e.fill),r.attr("stroke",e.stroke),r.attr("r",e.r),r.class!==void 0&&r.attr("class",r.class),e.title!==void 0&&r.append("title").text(e.title),r},"drawCircle"),mde=o(function(t,e){return TW(t,e)},"drawText"),NUe=o(function(t,e){function r(i,a,s,l,u){return i+","+a+" "+(i+s)+","+a+" "+(i+s)+","+(a+l-u)+" "+(i+s-u*1.2)+","+(a+l)+" "+i+","+(a+l)}o(r,"genPoints");let n=t.append("polygon");n.attr("points",r(e.x,e.y,50,20,7)),n.attr("class","labelBox"),e.y=e.y+e.labelMargin,e.x=e.x+.5*e.labelMargin,mde(t,e)},"drawLabel"),MUe=o(function(t,e,r){let n=t.append("g"),i=wl();i.x=e.x,i.y=e.y,i.fill=e.fill,i.width=r.width*e.taskCount+r.diagramMarginX*(e.taskCount-1),i.height=r.height,i.class="journey-section section-type-"+e.num,i.rx=3,i.ry=3,cP(n,i),gde(r)(e.text,n,i.x,i.y,i.width,i.height,{class:"journey-section section-type-"+e.num},r,e.colour)},"drawSection"),dde=-1,IUe=o(function(t,e,r){let n=e.x+r.width/2,i=t.append("g");dde++;let a=300+5*30;i.append("line").attr("id","task"+dde).attr("x1",n).attr("y1",e.y).attr("x2",n).attr("y2",a).attr("class","task-line").attr("stroke-width","1px").attr("stroke-dasharray","4 2").attr("stroke","#666"),RUe(i,{cx:n,cy:300+(5-e.score)*30,score:e.score});let s=wl();s.x=e.x,s.y=e.y,s.fill=e.fill,s.width=r.width,s.height=r.height,s.class="task task-type-"+e.num,s.rx=3,s.ry=3,cP(i,s);let l=e.x+14;e.people.forEach(u=>{let h=e.actors[u].color,f={cx:l,cy:e.y,r:7,fill:h,stroke:"#000",title:u,pos:e.actors[u].position};pde(i,f),l+=10}),gde(r)(e.task,i,s.x,s.y,s.width,s.height,{class:"task"},r,e.colour)},"drawTask"),OUe=o(function(t,e){j3(t,e)},"drawBackgroundRect"),gde=function(){function t(i,a,s,l,u,h,f,d){let p=a.append("text").attr("x",s+u/2).attr("y",l+h/2+5).style("font-color",d).style("text-anchor","middle").text(i);n(p,f)}o(t,"byText");function e(i,a,s,l,u,h,f,d,p){let{taskFontSize:m,taskFontFamily:g}=d,y=i.split(//gi);for(let v=0;v{let i=Xu[n].color,a={cx:20,cy:r,r:7,fill:i,stroke:"#000",pos:Xu[n].position};Hg.drawCircle(t,a);let s={x:40,y:r+7,fill:"#666",text:n,textMargin:e.boxTextMargin|5};Hg.drawText(t,s),r+=20})}var BUe,Xu,JE,I0,zUe,Zo,uP,vde,GUe,hP,xde=R(()=>{"use strict";Zt();yde();_t();Yn();BUe=o(function(t){Object.keys(t).forEach(function(r){JE[r]=t[r]})},"setConf"),Xu={};o(FUe,"drawActorLegend");JE=de().journey,I0=JE.leftMargin,zUe=o(function(t,e,r,n){let i=de().journey,a=de().securityLevel,s;a==="sandbox"&&(s=$e("#i"+e));let l=a==="sandbox"?$e(s.nodes()[0].contentDocument.body):$e("body");Zo.init();let u=l.select("#"+e);Hg.initGraphics(u);let h=n.db.getTasks(),f=n.db.getDiagramTitle(),d=n.db.getActors();for(let x in Xu)delete Xu[x];let p=0;d.forEach(x=>{Xu[x]={color:i.actorColours[p%i.actorColours.length],position:p},p++}),FUe(u),Zo.insert(0,0,I0,Object.keys(Xu).length*50),GUe(u,h,0);let m=Zo.getBounds();f&&u.append("text").text(f).attr("x",I0).attr("font-size","4ex").attr("font-weight","bold").attr("y",25);let g=m.stopy-m.starty+2*i.diagramMarginY,y=I0+m.stopx+2*i.diagramMarginX;Sr(u,g,y,i.useMaxWidth),u.append("line").attr("x1",I0).attr("y1",i.height*4).attr("x2",y-I0-4).attr("y2",i.height*4).attr("stroke-width",4).attr("stroke","black").attr("marker-end","url(#arrowhead)");let v=f?70:0;u.attr("viewBox",`${m.startx} -25 ${y} ${g+v}`),u.attr("preserveAspectRatio","xMinYMin meet"),u.attr("height",g+v+25)},"draw"),Zo={data:{startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},verticalPos:0,sequenceItems:[],init:o(function(){this.sequenceItems=[],this.data={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},this.verticalPos=0},"init"),updateVal:o(function(t,e,r,n){t[e]===void 0?t[e]=r:t[e]=n(r,t[e])},"updateVal"),updateBounds:o(function(t,e,r,n){let i=de().journey,a=this,s=0;function l(u){return o(function(f){s++;let d=a.sequenceItems.length-s+1;a.updateVal(f,"starty",e-d*i.boxMargin,Math.min),a.updateVal(f,"stopy",n+d*i.boxMargin,Math.max),a.updateVal(Zo.data,"startx",t-d*i.boxMargin,Math.min),a.updateVal(Zo.data,"stopx",r+d*i.boxMargin,Math.max),u!=="activation"&&(a.updateVal(f,"startx",t-d*i.boxMargin,Math.min),a.updateVal(f,"stopx",r+d*i.boxMargin,Math.max),a.updateVal(Zo.data,"starty",e-d*i.boxMargin,Math.min),a.updateVal(Zo.data,"stopy",n+d*i.boxMargin,Math.max))},"updateItemBounds")}o(l,"updateFn"),this.sequenceItems.forEach(l())},"updateBounds"),insert:o(function(t,e,r,n){let i=Math.min(t,r),a=Math.max(t,r),s=Math.min(e,n),l=Math.max(e,n);this.updateVal(Zo.data,"startx",i,Math.min),this.updateVal(Zo.data,"starty",s,Math.min),this.updateVal(Zo.data,"stopx",a,Math.max),this.updateVal(Zo.data,"stopy",l,Math.max),this.updateBounds(i,s,a,l)},"insert"),bumpVerticalPos:o(function(t){this.verticalPos=this.verticalPos+t,this.data.stopy=this.verticalPos},"bumpVerticalPos"),getVerticalPos:o(function(){return this.verticalPos},"getVerticalPos"),getBounds:o(function(){return this.data},"getBounds")},uP=JE.sectionFills,vde=JE.sectionColours,GUe=o(function(t,e,r){let n=de().journey,i="",a=n.height*2+n.diagramMarginY,s=r+a,l=0,u="#CCC",h="black",f=0;for(let[d,p]of e.entries()){if(i!==p.section){u=uP[l%uP.length],f=l%uP.length,h=vde[l%vde.length];let g=0,y=p.section;for(let x=d;x(Xu[y]&&(g[y]=Xu[y]),g),{});p.x=d*n.taskMargin+d*n.width+I0,p.y=s,p.width=n.diagramMarginX,p.height=n.diagramMarginY,p.colour=h,p.fill=u,p.num=f,p.actors=m,Hg.drawTask(t,p,n),Zo.insert(p.x,p.y,p.x+p.width+n.taskMargin,300+5*30)}},"drawTasks"),hP={setConf:BUe,draw:zUe}});var bde={};hr(bde,{diagram:()=>$Ue});var $Ue,wde=R(()=>{"use strict";lde();ude();fde();xde();$Ue={parser:ode,db:lP,renderer:hP,styles:hde,init:o(t=>{hP.setConf(t.journey),lP.clear()},"init")}});var dP,_de,Lde=R(()=>{"use strict";dP=function(){var t=o(function(p,m,g,y){for(g=g||{},y=p.length;y--;g[p[y]]=m);return g},"o"),e=[6,8,10,11,12,14,16,17,20,21],r=[1,9],n=[1,10],i=[1,11],a=[1,12],s=[1,13],l=[1,16],u=[1,17],h={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,start:3,timeline:4,document:5,EOF:6,line:7,SPACE:8,statement:9,NEWLINE:10,title:11,acc_title:12,acc_title_value:13,acc_descr:14,acc_descr_value:15,acc_descr_multiline_value:16,section:17,period_statement:18,event_statement:19,period:20,event:21,$accept:0,$end:1},terminals_:{2:"error",4:"timeline",6:"EOF",8:"SPACE",10:"NEWLINE",11:"title",12:"acc_title",13:"acc_title_value",14:"acc_descr",15:"acc_descr_value",16:"acc_descr_multiline_value",17:"section",20:"period",21:"event"},productions_:[0,[3,3],[5,0],[5,2],[7,2],[7,1],[7,1],[7,1],[9,1],[9,2],[9,2],[9,1],[9,1],[9,1],[9,1],[18,1],[19,1]],performAction:o(function(m,g,y,v,x,b,w){var S=b.length-1;switch(x){case 1:return b[S-1];case 2:this.$=[];break;case 3:b[S-1].push(b[S]),this.$=b[S-1];break;case 4:case 5:this.$=b[S];break;case 6:case 7:this.$=[];break;case 8:v.getCommonDb().setDiagramTitle(b[S].substr(6)),this.$=b[S].substr(6);break;case 9:this.$=b[S].trim(),v.getCommonDb().setAccTitle(this.$);break;case 10:case 11:this.$=b[S].trim(),v.getCommonDb().setAccDescription(this.$);break;case 12:v.addSection(b[S].substr(8)),this.$=b[S].substr(8);break;case 15:v.addTask(b[S],0,""),this.$=b[S];break;case 16:v.addEvent(b[S].substr(2)),this.$=b[S];break}},"anonymous"),table:[{3:1,4:[1,2]},{1:[3]},t(e,[2,2],{5:3}),{6:[1,4],7:5,8:[1,6],9:7,10:[1,8],11:r,12:n,14:i,16:a,17:s,18:14,19:15,20:l,21:u},t(e,[2,7],{1:[2,1]}),t(e,[2,3]),{9:18,11:r,12:n,14:i,16:a,17:s,18:14,19:15,20:l,21:u},t(e,[2,5]),t(e,[2,6]),t(e,[2,8]),{13:[1,19]},{15:[1,20]},t(e,[2,11]),t(e,[2,12]),t(e,[2,13]),t(e,[2,14]),t(e,[2,15]),t(e,[2,16]),t(e,[2,4]),t(e,[2,9]),t(e,[2,10])],defaultActions:{},parseError:o(function(m,g){if(g.recoverable)this.trace(m);else{var y=new Error(m);throw y.hash=g,y}},"parseError"),parse:o(function(m){var g=this,y=[0],v=[],x=[null],b=[],w=this.table,S="",T=0,E=0,_=0,A=2,L=1,M=b.slice.call(arguments,1),N=Object.create(this.lexer),k={yy:{}};for(var I in this.yy)Object.prototype.hasOwnProperty.call(this.yy,I)&&(k.yy[I]=this.yy[I]);N.setInput(m,k.yy),k.yy.lexer=N,k.yy.parser=this,typeof N.yylloc>"u"&&(N.yylloc={});var C=N.yylloc;b.push(C);var O=N.options&&N.options.ranges;typeof k.yy.parseError=="function"?this.parseError=k.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function D(q){y.length=y.length-2*q,x.length=x.length-q,b.length=b.length-q}o(D,"popStack");function P(){var q;return q=v.pop()||N.lex()||L,typeof q!="number"&&(q instanceof Array&&(v=q,q=v.pop()),q=g.symbols_[q]||q),q}o(P,"lex");for(var F,B,$,z,Y,Q,X={},ie,j,J,Z;;){if($=y[y.length-1],this.defaultActions[$]?z=this.defaultActions[$]:((F===null||typeof F>"u")&&(F=P()),z=w[$]&&w[$][F]),typeof z>"u"||!z.length||!z[0]){var H="";Z=[];for(ie in w[$])this.terminals_[ie]&&ie>A&&Z.push("'"+this.terminals_[ie]+"'");N.showPosition?H="Parse error on line "+(T+1)+`: +`+N.showPosition()+` +Expecting `+Z.join(", ")+", got '"+(this.terminals_[F]||F)+"'":H="Parse error on line "+(T+1)+": Unexpected "+(F==L?"end of input":"'"+(this.terminals_[F]||F)+"'"),this.parseError(H,{text:N.match,token:this.terminals_[F]||F,line:N.yylineno,loc:C,expected:Z})}if(z[0]instanceof Array&&z.length>1)throw new Error("Parse Error: multiple actions possible at state: "+$+", token: "+F);switch(z[0]){case 1:y.push(F),x.push(N.yytext),b.push(N.yylloc),y.push(z[1]),F=null,B?(F=B,B=null):(E=N.yyleng,S=N.yytext,T=N.yylineno,C=N.yylloc,_>0&&_--);break;case 2:if(j=this.productions_[z[1]][1],X.$=x[x.length-j],X._$={first_line:b[b.length-(j||1)].first_line,last_line:b[b.length-1].last_line,first_column:b[b.length-(j||1)].first_column,last_column:b[b.length-1].last_column},O&&(X._$.range=[b[b.length-(j||1)].range[0],b[b.length-1].range[1]]),Q=this.performAction.apply(X,[S,E,T,k.yy,z[1],x,b].concat(M)),typeof Q<"u")return Q;j&&(y=y.slice(0,-1*j*2),x=x.slice(0,-1*j),b=b.slice(0,-1*j)),y.push(this.productions_[z[1]][0]),x.push(X.$),b.push(X._$),J=w[y[y.length-2]][y[y.length-1]],y.push(J);break;case 3:return!0}}return!0},"parse")},f=function(){var p={EOF:1,parseError:o(function(g,y){if(this.yy.parser)this.yy.parser.parseError(g,y);else throw new Error(g)},"parseError"),setInput:o(function(m,g){return this.yy=g||this.yy||{},this._input=m,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var m=this._input[0];this.yytext+=m,this.yyleng++,this.offset++,this.match+=m,this.matched+=m;var g=m.match(/(?:\r\n?|\n).*/g);return g?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),m},"input"),unput:o(function(m){var g=m.length,y=m.split(/(?:\r\n?|\n)/g);this._input=m+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-g),this.offset-=g;var v=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),y.length-1&&(this.yylineno-=y.length-1);var x=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:y?(y.length===v.length?this.yylloc.first_column:0)+v[v.length-y.length].length-y[0].length:this.yylloc.first_column-g},this.options.ranges&&(this.yylloc.range=[x[0],x[0]+this.yyleng-g]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(m){this.unput(this.match.slice(m))},"less"),pastInput:o(function(){var m=this.matched.substr(0,this.matched.length-this.match.length);return(m.length>20?"...":"")+m.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var m=this.match;return m.length<20&&(m+=this._input.substr(0,20-m.length)),(m.substr(0,20)+(m.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var m=this.pastInput(),g=new Array(m.length+1).join("-");return m+this.upcomingInput()+` +`+g+"^"},"showPosition"),test_match:o(function(m,g){var y,v,x;if(this.options.backtrack_lexer&&(x={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(x.yylloc.range=this.yylloc.range.slice(0))),v=m[0].match(/(?:\r\n?|\n).*/g),v&&(this.yylineno+=v.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:v?v[v.length-1].length-v[v.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+m[0].length},this.yytext+=m[0],this.match+=m[0],this.matches=m,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(m[0].length),this.matched+=m[0],y=this.performAction.call(this,this.yy,this,g,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),y)return y;if(this._backtrack){for(var b in x)this[b]=x[b];return!1}return!1},"test_match"),next:o(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var m,g,y,v;this._more||(this.yytext="",this.match="");for(var x=this._currentRules(),b=0;bg[0].length)){if(g=y,v=b,this.options.backtrack_lexer){if(m=this.test_match(y,x[b]),m!==!1)return m;if(this._backtrack){g=!1;continue}else return!1}else if(!this.options.flex)break}return g?(m=this.test_match(g,x[v]),m!==!1?m:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:o(function(){var g=this.next();return g||this.lex()},"lex"),begin:o(function(g){this.conditionStack.push(g)},"begin"),popState:o(function(){var g=this.conditionStack.length-1;return g>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:o(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:o(function(g){return g=this.conditionStack.length-1-Math.abs(g||0),g>=0?this.conditionStack[g]:"INITIAL"},"topState"),pushState:o(function(g){this.begin(g)},"pushState"),stateStackSize:o(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:o(function(g,y,v,x){var b=x;switch(v){case 0:break;case 1:break;case 2:return 10;case 3:break;case 4:break;case 5:return 4;case 6:return 11;case 7:return this.begin("acc_title"),12;break;case 8:return this.popState(),"acc_title_value";break;case 9:return this.begin("acc_descr"),14;break;case 10:return this.popState(),"acc_descr_value";break;case 11:this.begin("acc_descr_multiline");break;case 12:this.popState();break;case 13:return"acc_descr_multiline_value";case 14:return 17;case 15:return 21;case 16:return 20;case 17:return 6;case 18:return"INVALID"}},"anonymous"),rules:[/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:timeline\b)/i,/^(?:title\s[^\n]+)/i,/^(?:accTitle\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*:\s*)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:accDescr\s*\{\s*)/i,/^(?:[\}])/i,/^(?:[^\}]*)/i,/^(?:section\s[^:\n]+)/i,/^(?::\s[^:\n]+)/i,/^(?:[^#:\n]+)/i,/^(?:$)/i,/^(?:.)/i],conditions:{acc_descr_multiline:{rules:[12,13],inclusive:!1},acc_descr:{rules:[10],inclusive:!1},acc_title:{rules:[8],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,7,9,11,14,15,16,17,18],inclusive:!0}}};return p}();h.lexer=f;function d(){this.yy={}}return o(d,"Parser"),d.prototype=h,h.Parser=d,new d}();dP.parser=dP;_de=dP});var mP={};hr(mP,{addEvent:()=>Fde,addSection:()=>Ide,addTask:()=>Bde,addTaskOrg:()=>zde,clear:()=>Mde,default:()=>KUe,getCommonDb:()=>Nde,getSections:()=>Ode,getTasks:()=>Pde});var Yg,Rde,pP,e6,Wg,Nde,Mde,Ide,Ode,Pde,Bde,Fde,zde,Dde,KUe,Gde=R(()=>{"use strict";bi();Yg="",Rde=0,pP=[],e6=[],Wg=[],Nde=o(()=>ly,"getCommonDb"),Mde=o(function(){pP.length=0,e6.length=0,Yg="",Wg.length=0,vr()},"clear"),Ide=o(function(t){Yg=t,pP.push(t)},"addSection"),Ode=o(function(){return pP},"getSections"),Pde=o(function(){let t=Dde(),e=100,r=0;for(;!t&&rr.id===Rde-1).events.push(t)},"addEvent"),zde=o(function(t){let e={section:Yg,type:Yg,description:t,task:t,classes:[]};e6.push(e)},"addTaskOrg"),Dde=o(function(){let t=o(function(r){return Wg[r].processed},"compileTask"),e=!0;for(let[r,n]of Wg.entries())t(r),e=e&&n.processed;return e},"compileTasks"),KUe={clear:Mde,getCommonDb:Nde,addSection:Ide,getSections:Ode,getTasks:Pde,addTask:Bde,addTaskOrg:zde,addEvent:Fde}});function Hde(t,e){t.each(function(){var r=$e(this),n=r.text().split(/(\s+|
    )/).reverse(),i,a=[],s=1.1,l=r.attr("y"),u=parseFloat(r.attr("dy")),h=r.text(null).append("tspan").attr("x",0).attr("y",l).attr("dy",u+"em");for(let f=0;fe||i==="
    ")&&(a.pop(),h.text(a.join(" ").trim()),i==="
    "?a=[""]:a=[i],h=r.append("tspan").attr("x",0).attr("y",l).attr("dy",s+"em").text(i))})}var QUe,t6,ZUe,JUe,Vde,eHe,tHe,$de,rHe,nHe,iHe,gP,Ude,aHe,sHe,oHe,lHe,xf,Yde=R(()=>{"use strict";Zt();QUe=12,t6=o(function(t,e){let r=t.append("rect");return r.attr("x",e.x),r.attr("y",e.y),r.attr("fill",e.fill),r.attr("stroke",e.stroke),r.attr("width",e.width),r.attr("height",e.height),r.attr("rx",e.rx),r.attr("ry",e.ry),e.class!==void 0&&r.attr("class",e.class),r},"drawRect"),ZUe=o(function(t,e){let n=t.append("circle").attr("cx",e.cx).attr("cy",e.cy).attr("class","face").attr("r",15).attr("stroke-width",2).attr("overflow","visible"),i=t.append("g");i.append("circle").attr("cx",e.cx-15/3).attr("cy",e.cy-15/3).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666"),i.append("circle").attr("cx",e.cx+15/3).attr("cy",e.cy-15/3).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666");function a(u){let h=bl().startAngle(Math.PI/2).endAngle(3*(Math.PI/2)).innerRadius(7.5).outerRadius(6.8181818181818175);u.append("path").attr("class","mouth").attr("d",h).attr("transform","translate("+e.cx+","+(e.cy+2)+")")}o(a,"smile");function s(u){let h=bl().startAngle(3*Math.PI/2).endAngle(5*(Math.PI/2)).innerRadius(7.5).outerRadius(6.8181818181818175);u.append("path").attr("class","mouth").attr("d",h).attr("transform","translate("+e.cx+","+(e.cy+7)+")")}o(s,"sad");function l(u){u.append("line").attr("class","mouth").attr("stroke",2).attr("x1",e.cx-5).attr("y1",e.cy+7).attr("x2",e.cx+5).attr("y2",e.cy+7).attr("class","mouth").attr("stroke-width","1px").attr("stroke","#666")}return o(l,"ambivalent"),e.score>3?a(i):e.score<3?s(i):l(i),n},"drawFace"),JUe=o(function(t,e){let r=t.append("circle");return r.attr("cx",e.cx),r.attr("cy",e.cy),r.attr("class","actor-"+e.pos),r.attr("fill",e.fill),r.attr("stroke",e.stroke),r.attr("r",e.r),r.class!==void 0&&r.attr("class",r.class),e.title!==void 0&&r.append("title").text(e.title),r},"drawCircle"),Vde=o(function(t,e){let r=e.text.replace(//gi," "),n=t.append("text");n.attr("x",e.x),n.attr("y",e.y),n.attr("class","legend"),n.style("text-anchor",e.anchor),e.class!==void 0&&n.attr("class",e.class);let i=n.append("tspan");return i.attr("x",e.x+e.textMargin*2),i.text(r),n},"drawText"),eHe=o(function(t,e){function r(i,a,s,l,u){return i+","+a+" "+(i+s)+","+a+" "+(i+s)+","+(a+l-u)+" "+(i+s-u*1.2)+","+(a+l)+" "+i+","+(a+l)}o(r,"genPoints");let n=t.append("polygon");n.attr("points",r(e.x,e.y,50,20,7)),n.attr("class","labelBox"),e.y=e.y+e.labelMargin,e.x=e.x+.5*e.labelMargin,Vde(t,e)},"drawLabel"),tHe=o(function(t,e,r){let n=t.append("g"),i=gP();i.x=e.x,i.y=e.y,i.fill=e.fill,i.width=r.width,i.height=r.height,i.class="journey-section section-type-"+e.num,i.rx=3,i.ry=3,t6(n,i),Ude(r)(e.text,n,i.x,i.y,i.width,i.height,{class:"journey-section section-type-"+e.num},r,e.colour)},"drawSection"),$de=-1,rHe=o(function(t,e,r){let n=e.x+r.width/2,i=t.append("g");$de++;let a=300+5*30;i.append("line").attr("id","task"+$de).attr("x1",n).attr("y1",e.y).attr("x2",n).attr("y2",a).attr("class","task-line").attr("stroke-width","1px").attr("stroke-dasharray","4 2").attr("stroke","#666"),ZUe(i,{cx:n,cy:300+(5-e.score)*30,score:e.score});let s=gP();s.x=e.x,s.y=e.y,s.fill=e.fill,s.width=r.width,s.height=r.height,s.class="task task-type-"+e.num,s.rx=3,s.ry=3,t6(i,s),Ude(r)(e.task,i,s.x,s.y,s.width,s.height,{class:"task"},r,e.colour)},"drawTask"),nHe=o(function(t,e){t6(t,{x:e.startx,y:e.starty,width:e.stopx-e.startx,height:e.stopy-e.starty,fill:e.fill,class:"rect"}).lower()},"drawBackgroundRect"),iHe=o(function(){return{x:0,y:0,fill:void 0,"text-anchor":"start",width:100,height:100,textMargin:0,rx:0,ry:0}},"getTextObj"),gP=o(function(){return{x:0,y:0,width:100,anchor:"start",height:100,rx:0,ry:0}},"getNoteRect"),Ude=function(){function t(i,a,s,l,u,h,f,d){let p=a.append("text").attr("x",s+u/2).attr("y",l+h/2+5).style("font-color",d).style("text-anchor","middle").text(i);n(p,f)}o(t,"byText");function e(i,a,s,l,u,h,f,d,p){let{taskFontSize:m,taskFontFamily:g}=d,y=i.split(//gi);for(let v=0;v{"use strict";Zt();Yde();ut();_t();Yn();cHe=o(function(t,e,r,n){let i=de(),a=i.leftMargin??50;V.debug("timeline",n.db);let s=i.securityLevel,l;s==="sandbox"&&(l=$e("#i"+e));let h=(s==="sandbox"?$e(l.nodes()[0].contentDocument.body):$e("body")).select("#"+e);h.append("g");let f=n.db.getTasks(),d=n.db.getCommonDb().getDiagramTitle();V.debug("task",f),xf.initGraphics(h);let p=n.db.getSections();V.debug("sections",p);let m=0,g=0,y=0,v=0,x=50+a,b=50;v=50;let w=0,S=!0;p.forEach(function(L){let M={number:w,descr:L,section:w,width:150,padding:20,maxHeight:m},N=xf.getVirtualNodeHeight(h,M,i);V.debug("sectionHeight before draw",N),m=Math.max(m,N+20)});let T=0,E=0;V.debug("tasks.length",f.length);for(let[L,M]of f.entries()){let N={number:L,descr:M,section:M.section,width:150,padding:20,maxHeight:g},k=xf.getVirtualNodeHeight(h,N,i);V.debug("taskHeight before draw",k),g=Math.max(g,k+20),T=Math.max(T,M.events.length);let I=0;for(let C of M.events){let O={descr:C,section:M.section,number:M.section,width:150,padding:20,maxHeight:50};I+=xf.getVirtualNodeHeight(h,O,i)}E=Math.max(E,I)}V.debug("maxSectionHeight before draw",m),V.debug("maxTaskHeight before draw",g),p&&p.length>0?p.forEach(L=>{let M=f.filter(C=>C.section===L),N={number:w,descr:L,section:w,width:200*Math.max(M.length,1)-50,padding:20,maxHeight:m};V.debug("sectionNode",N);let k=h.append("g"),I=xf.drawNode(k,N,w,i);V.debug("sectionNode output",I),k.attr("transform",`translate(${x}, ${v})`),b+=m+50,M.length>0&&Wde(h,M,w,x,b,g,i,T,E,m,!1),x+=200*Math.max(M.length,1),b=v,w++}):(S=!1,Wde(h,f,w,x,b,g,i,T,E,m,!0));let _=h.node().getBBox();V.debug("bounds",_),d&&h.append("text").text(d).attr("x",_.width/2-a).attr("font-size","4ex").attr("font-weight","bold").attr("y",20),y=S?m+g+150:g+100,h.append("g").attr("class","lineWrapper").append("line").attr("x1",a).attr("y1",y).attr("x2",_.width+3*a).attr("y2",y).attr("stroke-width",4).attr("stroke","black").attr("marker-end","url(#arrowhead)"),Lo(void 0,h,i.timeline?.padding??50,i.timeline?.useMaxWidth??!1)},"draw"),Wde=o(function(t,e,r,n,i,a,s,l,u,h,f){for(let d of e){let p={descr:d.task,section:r,number:r,width:150,padding:20,maxHeight:a};V.debug("taskNode",p);let m=t.append("g").attr("class","taskWrapper"),y=xf.drawNode(m,p,r,s).height;if(V.debug("taskHeight after draw",y),m.attr("transform",`translate(${n}, ${i})`),a=Math.max(a,y),d.events){let v=t.append("g").attr("class","lineWrapper"),x=a;i+=100,x=x+uHe(t,d.events,r,n,i,s),i-=100,v.append("line").attr("x1",n+190/2).attr("y1",i+a).attr("x2",n+190/2).attr("y2",i+a+(f?a:h)+u+120).attr("stroke-width",2).attr("stroke","black").attr("marker-end","url(#arrowhead)").attr("stroke-dasharray","5,5")}n=n+200,f&&!s.timeline?.disableMulticolor&&r++}i=i-10},"drawTasks"),uHe=o(function(t,e,r,n,i,a){let s=0,l=i;i=i+100;for(let u of e){let h={descr:u,section:r,number:r,width:150,padding:20,maxHeight:50};V.debug("eventNode",h);let f=t.append("g").attr("class","eventWrapper"),p=xf.drawNode(f,h,r,a).height;s=s+p,f.attr("transform",`translate(${n}, ${i})`),i=i+10+p}return i=l,s},"drawEvents"),qde={setConf:o(()=>{},"setConf"),draw:cHe}});var hHe,fHe,jde,Kde=R(()=>{"use strict";al();hHe=o(t=>{let e="";for(let r=0;r` + .edge { + stroke-width: 3; + } + ${hHe(t)} + .section-root rect, .section-root path, .section-root circle { + fill: ${t.git0}; + } + .section-root text { + fill: ${t.gitBranchLabel0}; + } + .icon-container { + height:100%; + display: flex; + justify-content: center; + align-items: center; + } + .edge { + fill: none; + } + .eventWrapper { + filter: brightness(120%); + } +`,"getStyles"),jde=fHe});var Qde={};hr(Qde,{diagram:()=>dHe});var dHe,Zde=R(()=>{"use strict";Lde();Gde();Xde();Kde();dHe={db:mP,renderer:qde,parser:_de,styles:jde}});var yP,t0e,r0e=R(()=>{"use strict";yP=function(){var t=o(function(S,T,E,_){for(E=E||{},_=S.length;_--;E[S[_]]=T);return E},"o"),e=[1,4],r=[1,13],n=[1,12],i=[1,15],a=[1,16],s=[1,20],l=[1,19],u=[6,7,8],h=[1,26],f=[1,24],d=[1,25],p=[6,7,11],m=[1,6,13,15,16,19,22],g=[1,33],y=[1,34],v=[1,6,7,11,13,15,16,19,22],x={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,start:3,mindMap:4,spaceLines:5,SPACELINE:6,NL:7,MINDMAP:8,document:9,stop:10,EOF:11,statement:12,SPACELIST:13,node:14,ICON:15,CLASS:16,nodeWithId:17,nodeWithoutId:18,NODE_DSTART:19,NODE_DESCR:20,NODE_DEND:21,NODE_ID:22,$accept:0,$end:1},terminals_:{2:"error",6:"SPACELINE",7:"NL",8:"MINDMAP",11:"EOF",13:"SPACELIST",15:"ICON",16:"CLASS",19:"NODE_DSTART",20:"NODE_DESCR",21:"NODE_DEND",22:"NODE_ID"},productions_:[0,[3,1],[3,2],[5,1],[5,2],[5,2],[4,2],[4,3],[10,1],[10,1],[10,1],[10,2],[10,2],[9,3],[9,2],[12,2],[12,2],[12,2],[12,1],[12,1],[12,1],[12,1],[12,1],[14,1],[14,1],[18,3],[17,1],[17,4]],performAction:o(function(T,E,_,A,L,M,N){var k=M.length-1;switch(L){case 6:case 7:return A;case 8:A.getLogger().trace("Stop NL ");break;case 9:A.getLogger().trace("Stop EOF ");break;case 11:A.getLogger().trace("Stop NL2 ");break;case 12:A.getLogger().trace("Stop EOF2 ");break;case 15:A.getLogger().info("Node: ",M[k].id),A.addNode(M[k-1].length,M[k].id,M[k].descr,M[k].type);break;case 16:A.getLogger().trace("Icon: ",M[k]),A.decorateNode({icon:M[k]});break;case 17:case 21:A.decorateNode({class:M[k]});break;case 18:A.getLogger().trace("SPACELIST");break;case 19:A.getLogger().trace("Node: ",M[k].id),A.addNode(0,M[k].id,M[k].descr,M[k].type);break;case 20:A.decorateNode({icon:M[k]});break;case 25:A.getLogger().trace("node found ..",M[k-2]),this.$={id:M[k-1],descr:M[k-1],type:A.getType(M[k-2],M[k])};break;case 26:this.$={id:M[k],descr:M[k],type:A.nodeType.DEFAULT};break;case 27:A.getLogger().trace("node found ..",M[k-3]),this.$={id:M[k-3],descr:M[k-1],type:A.getType(M[k-2],M[k])};break}},"anonymous"),table:[{3:1,4:2,5:3,6:[1,5],8:e},{1:[3]},{1:[2,1]},{4:6,6:[1,7],7:[1,8],8:e},{6:r,7:[1,10],9:9,12:11,13:n,14:14,15:i,16:a,17:17,18:18,19:s,22:l},t(u,[2,3]),{1:[2,2]},t(u,[2,4]),t(u,[2,5]),{1:[2,6],6:r,12:21,13:n,14:14,15:i,16:a,17:17,18:18,19:s,22:l},{6:r,9:22,12:11,13:n,14:14,15:i,16:a,17:17,18:18,19:s,22:l},{6:h,7:f,10:23,11:d},t(p,[2,22],{17:17,18:18,14:27,15:[1,28],16:[1,29],19:s,22:l}),t(p,[2,18]),t(p,[2,19]),t(p,[2,20]),t(p,[2,21]),t(p,[2,23]),t(p,[2,24]),t(p,[2,26],{19:[1,30]}),{20:[1,31]},{6:h,7:f,10:32,11:d},{1:[2,7],6:r,12:21,13:n,14:14,15:i,16:a,17:17,18:18,19:s,22:l},t(m,[2,14],{7:g,11:y}),t(v,[2,8]),t(v,[2,9]),t(v,[2,10]),t(p,[2,15]),t(p,[2,16]),t(p,[2,17]),{20:[1,35]},{21:[1,36]},t(m,[2,13],{7:g,11:y}),t(v,[2,11]),t(v,[2,12]),{21:[1,37]},t(p,[2,25]),t(p,[2,27])],defaultActions:{2:[2,1],6:[2,2]},parseError:o(function(T,E){if(E.recoverable)this.trace(T);else{var _=new Error(T);throw _.hash=E,_}},"parseError"),parse:o(function(T){var E=this,_=[0],A=[],L=[null],M=[],N=this.table,k="",I=0,C=0,O=0,D=2,P=1,F=M.slice.call(arguments,1),B=Object.create(this.lexer),$={yy:{}};for(var z in this.yy)Object.prototype.hasOwnProperty.call(this.yy,z)&&($.yy[z]=this.yy[z]);B.setInput(T,$.yy),$.yy.lexer=B,$.yy.parser=this,typeof B.yylloc>"u"&&(B.yylloc={});var Y=B.yylloc;M.push(Y);var Q=B.options&&B.options.ranges;typeof $.yy.parseError=="function"?this.parseError=$.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function X(ke){_.length=_.length-2*ke,L.length=L.length-ke,M.length=M.length-ke}o(X,"popStack");function ie(){var ke;return ke=A.pop()||B.lex()||P,typeof ke!="number"&&(ke instanceof Array&&(A=ke,ke=A.pop()),ke=E.symbols_[ke]||ke),ke}o(ie,"lex");for(var j,J,Z,H,q,K,se={},ce,ue,te,De;;){if(Z=_[_.length-1],this.defaultActions[Z]?H=this.defaultActions[Z]:((j===null||typeof j>"u")&&(j=ie()),H=N[Z]&&N[Z][j]),typeof H>"u"||!H.length||!H[0]){var oe="";De=[];for(ce in N[Z])this.terminals_[ce]&&ce>D&&De.push("'"+this.terminals_[ce]+"'");B.showPosition?oe="Parse error on line "+(I+1)+`: +`+B.showPosition()+` +Expecting `+De.join(", ")+", got '"+(this.terminals_[j]||j)+"'":oe="Parse error on line "+(I+1)+": Unexpected "+(j==P?"end of input":"'"+(this.terminals_[j]||j)+"'"),this.parseError(oe,{text:B.match,token:this.terminals_[j]||j,line:B.yylineno,loc:Y,expected:De})}if(H[0]instanceof Array&&H.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Z+", token: "+j);switch(H[0]){case 1:_.push(j),L.push(B.yytext),M.push(B.yylloc),_.push(H[1]),j=null,J?(j=J,J=null):(C=B.yyleng,k=B.yytext,I=B.yylineno,Y=B.yylloc,O>0&&O--);break;case 2:if(ue=this.productions_[H[1]][1],se.$=L[L.length-ue],se._$={first_line:M[M.length-(ue||1)].first_line,last_line:M[M.length-1].last_line,first_column:M[M.length-(ue||1)].first_column,last_column:M[M.length-1].last_column},Q&&(se._$.range=[M[M.length-(ue||1)].range[0],M[M.length-1].range[1]]),K=this.performAction.apply(se,[k,C,I,$.yy,H[1],L,M].concat(F)),typeof K<"u")return K;ue&&(_=_.slice(0,-1*ue*2),L=L.slice(0,-1*ue),M=M.slice(0,-1*ue)),_.push(this.productions_[H[1]][0]),L.push(se.$),M.push(se._$),te=N[_[_.length-2]][_[_.length-1]],_.push(te);break;case 3:return!0}}return!0},"parse")},b=function(){var S={EOF:1,parseError:o(function(E,_){if(this.yy.parser)this.yy.parser.parseError(E,_);else throw new Error(E)},"parseError"),setInput:o(function(T,E){return this.yy=E||this.yy||{},this._input=T,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var T=this._input[0];this.yytext+=T,this.yyleng++,this.offset++,this.match+=T,this.matched+=T;var E=T.match(/(?:\r\n?|\n).*/g);return E?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),T},"input"),unput:o(function(T){var E=T.length,_=T.split(/(?:\r\n?|\n)/g);this._input=T+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-E),this.offset-=E;var A=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),_.length-1&&(this.yylineno-=_.length-1);var L=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:_?(_.length===A.length?this.yylloc.first_column:0)+A[A.length-_.length].length-_[0].length:this.yylloc.first_column-E},this.options.ranges&&(this.yylloc.range=[L[0],L[0]+this.yyleng-E]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(T){this.unput(this.match.slice(T))},"less"),pastInput:o(function(){var T=this.matched.substr(0,this.matched.length-this.match.length);return(T.length>20?"...":"")+T.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var T=this.match;return T.length<20&&(T+=this._input.substr(0,20-T.length)),(T.substr(0,20)+(T.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var T=this.pastInput(),E=new Array(T.length+1).join("-");return T+this.upcomingInput()+` +`+E+"^"},"showPosition"),test_match:o(function(T,E){var _,A,L;if(this.options.backtrack_lexer&&(L={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(L.yylloc.range=this.yylloc.range.slice(0))),A=T[0].match(/(?:\r\n?|\n).*/g),A&&(this.yylineno+=A.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:A?A[A.length-1].length-A[A.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+T[0].length},this.yytext+=T[0],this.match+=T[0],this.matches=T,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(T[0].length),this.matched+=T[0],_=this.performAction.call(this,this.yy,this,E,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),_)return _;if(this._backtrack){for(var M in L)this[M]=L[M];return!1}return!1},"test_match"),next:o(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var T,E,_,A;this._more||(this.yytext="",this.match="");for(var L=this._currentRules(),M=0;ME[0].length)){if(E=_,A=M,this.options.backtrack_lexer){if(T=this.test_match(_,L[M]),T!==!1)return T;if(this._backtrack){E=!1;continue}else return!1}else if(!this.options.flex)break}return E?(T=this.test_match(E,L[A]),T!==!1?T:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:o(function(){var E=this.next();return E||this.lex()},"lex"),begin:o(function(E){this.conditionStack.push(E)},"begin"),popState:o(function(){var E=this.conditionStack.length-1;return E>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:o(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:o(function(E){return E=this.conditionStack.length-1-Math.abs(E||0),E>=0?this.conditionStack[E]:"INITIAL"},"topState"),pushState:o(function(E){this.begin(E)},"pushState"),stateStackSize:o(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:o(function(E,_,A,L){var M=L;switch(A){case 0:return E.getLogger().trace("Found comment",_.yytext),6;break;case 1:return 8;case 2:this.begin("CLASS");break;case 3:return this.popState(),16;break;case 4:this.popState();break;case 5:E.getLogger().trace("Begin icon"),this.begin("ICON");break;case 6:return E.getLogger().trace("SPACELINE"),6;break;case 7:return 7;case 8:return 15;case 9:E.getLogger().trace("end icon"),this.popState();break;case 10:return E.getLogger().trace("Exploding node"),this.begin("NODE"),19;break;case 11:return E.getLogger().trace("Cloud"),this.begin("NODE"),19;break;case 12:return E.getLogger().trace("Explosion Bang"),this.begin("NODE"),19;break;case 13:return E.getLogger().trace("Cloud Bang"),this.begin("NODE"),19;break;case 14:return this.begin("NODE"),19;break;case 15:return this.begin("NODE"),19;break;case 16:return this.begin("NODE"),19;break;case 17:return this.begin("NODE"),19;break;case 18:return 13;case 19:return 22;case 20:return 11;case 21:this.begin("NSTR2");break;case 22:return"NODE_DESCR";case 23:this.popState();break;case 24:E.getLogger().trace("Starting NSTR"),this.begin("NSTR");break;case 25:return E.getLogger().trace("description:",_.yytext),"NODE_DESCR";break;case 26:this.popState();break;case 27:return this.popState(),E.getLogger().trace("node end ))"),"NODE_DEND";break;case 28:return this.popState(),E.getLogger().trace("node end )"),"NODE_DEND";break;case 29:return this.popState(),E.getLogger().trace("node end ...",_.yytext),"NODE_DEND";break;case 30:return this.popState(),E.getLogger().trace("node end (("),"NODE_DEND";break;case 31:return this.popState(),E.getLogger().trace("node end (-"),"NODE_DEND";break;case 32:return this.popState(),E.getLogger().trace("node end (-"),"NODE_DEND";break;case 33:return this.popState(),E.getLogger().trace("node end (("),"NODE_DEND";break;case 34:return this.popState(),E.getLogger().trace("node end (("),"NODE_DEND";break;case 35:return E.getLogger().trace("Long description:",_.yytext),20;break;case 36:return E.getLogger().trace("Long description:",_.yytext),20;break}},"anonymous"),rules:[/^(?:\s*%%.*)/i,/^(?:mindmap\b)/i,/^(?::::)/i,/^(?:.+)/i,/^(?:\n)/i,/^(?:::icon\()/i,/^(?:[\s]+[\n])/i,/^(?:[\n]+)/i,/^(?:[^\)]+)/i,/^(?:\))/i,/^(?:-\))/i,/^(?:\(-)/i,/^(?:\)\))/i,/^(?:\))/i,/^(?:\(\()/i,/^(?:\{\{)/i,/^(?:\()/i,/^(?:\[)/i,/^(?:[\s]+)/i,/^(?:[^\(\[\n\)\{\}]+)/i,/^(?:$)/i,/^(?:["][`])/i,/^(?:[^`"]+)/i,/^(?:[`]["])/i,/^(?:["])/i,/^(?:[^"]+)/i,/^(?:["])/i,/^(?:[\)]\))/i,/^(?:[\)])/i,/^(?:[\]])/i,/^(?:\}\})/i,/^(?:\(-)/i,/^(?:-\))/i,/^(?:\(\()/i,/^(?:\()/i,/^(?:[^\)\]\(\}]+)/i,/^(?:.+(?!\(\())/i],conditions:{CLASS:{rules:[3,4],inclusive:!1},ICON:{rules:[8,9],inclusive:!1},NSTR2:{rules:[22,23],inclusive:!1},NSTR:{rules:[25,26],inclusive:!1},NODE:{rules:[21,24,27,28,29,30,31,32,33,34,35,36],inclusive:!1},INITIAL:{rules:[0,1,2,5,6,7,10,11,12,13,14,15,16,17,18,19,20],inclusive:!0}}};return S}();x.lexer=b;function w(){this.yy={}}return o(w,"Parser"),w.prototype=x,x.Parser=w,new w}();yP.parser=yP;t0e=yP});var Gl,n0e,vP,yHe,vHe,xHe,bHe,$i,wHe,THe,kHe,EHe,CHe,SHe,AHe,i0e,a0e=R(()=>{"use strict";_t();rr();ut();sl();Gl=[],n0e=0,vP={},yHe=o(()=>{Gl=[],n0e=0,vP={}},"clear"),vHe=o(function(t){for(let e=Gl.length-1;e>=0;e--)if(Gl[e].levelGl.length>0?Gl[0]:null,"getMindmap"),bHe=o((t,e,r,n)=>{V.info("addNode",t,e,r,n);let i=de(),a=i.mindmap?.padding??mr.mindmap.padding;switch(n){case $i.ROUNDED_RECT:case $i.RECT:case $i.HEXAGON:a*=2}let s={id:n0e++,nodeId:qr(e,i),level:t,descr:qr(r,i),type:n,children:[],width:i.mindmap?.maxNodeWidth??mr.mindmap.maxNodeWidth,padding:a},l=vHe(t);if(l)l.children.push(s),Gl.push(s);else if(Gl.length===0)Gl.push(s);else throw new Error('There can be only one root. No parent could be found for ("'+s.descr+'")')},"addNode"),$i={DEFAULT:0,NO_BORDER:0,ROUNDED_RECT:1,RECT:2,CIRCLE:3,CLOUD:4,BANG:5,HEXAGON:6},wHe=o((t,e)=>{switch(V.debug("In get type",t,e),t){case"[":return $i.RECT;case"(":return e===")"?$i.ROUNDED_RECT:$i.CLOUD;case"((":return $i.CIRCLE;case")":return $i.CLOUD;case"))":return $i.BANG;case"{{":return $i.HEXAGON;default:return $i.DEFAULT}},"getType"),THe=o((t,e)=>{vP[t]=e},"setElementForId"),kHe=o(t=>{if(!t)return;let e=de(),r=Gl[Gl.length-1];t.icon&&(r.icon=qr(t.icon,e)),t.class&&(r.class=qr(t.class,e))},"decorateNode"),EHe=o(t=>{switch(t){case $i.DEFAULT:return"no-border";case $i.RECT:return"rect";case $i.ROUNDED_RECT:return"rounded-rect";case $i.CIRCLE:return"circle";case $i.CLOUD:return"cloud";case $i.BANG:return"bang";case $i.HEXAGON:return"hexgon";default:return"no-border"}},"type2Str"),CHe=o(()=>V,"getLogger"),SHe=o(t=>vP[t],"getElementById"),AHe={clear:yHe,addNode:bHe,getMindmap:xHe,nodeType:$i,getType:wHe,setElementForId:THe,decorateNode:kHe,type2Str:EHe,getLogger:CHe,getElementById:SHe},i0e=AHe});function Hi(t){"@babel/helpers - typeof";return Hi=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(e){return typeof e}:function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Hi(t)}function XP(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function s0e(t,e){for(var r=0;rt.length)&&(e=t.length);for(var r=0,n=new Array(e);r=t.length?{done:!0}:{done:!1,value:t[n++]}},"n"),e:o(function(u){throw u},"e"),f:i}}throw new TypeError(`Invalid attempt to iterate non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}var a=!0,s=!1,l;return{s:o(function(){r=r.call(t)},"s"),n:o(function(){var u=r.next();return a=u.done,u},"n"),e:o(function(u){s=!0,l=u},"e"),f:o(function(){try{!a&&r.return!=null&&r.return()}finally{if(s)throw l}},"f")}}function eYe(t){var e=typeof t;return t!=null&&(e=="object"||e=="function")}function tYe(t,e){return e={exports:{}},t(e,e.exports),e.exports}function lYe(t){for(var e=t.length;e--&&oYe.test(t.charAt(e)););return e}function hYe(t){return t&&t.slice(0,cYe(t)+1).replace(uYe,"")}function gYe(t){var e=pYe.call(t,_x),r=t[_x];try{t[_x]=void 0;var n=!0}catch{}var i=mYe.call(t);return n&&(e?t[_x]=r:delete t[_x]),i}function bYe(t){return xYe.call(t)}function EYe(t){return t==null?t===void 0?kYe:TYe:u0e&&u0e in Object(t)?yYe(t):wYe(t)}function CYe(t){return t!=null&&typeof t=="object"}function _Ye(t){return typeof t=="symbol"||SYe(t)&&Rpe(t)==AYe}function MYe(t){if(typeof t=="number")return t;if(Jx(t))return h0e;if(V0(t)){var e=typeof t.valueOf=="function"?t.valueOf():t;t=V0(e)?e+"":e}if(typeof t!="string")return t===0?t:+t;t=fYe(t);var r=DYe.test(t);return r||RYe.test(t)?NYe(t.slice(2),r?2:8):LYe.test(t)?h0e:+t}function BYe(t,e,r){var n,i,a,s,l,u,h=0,f=!1,d=!1,p=!0;if(typeof t!="function")throw new TypeError(IYe);e=f0e(e)||0,V0(r)&&(f=!!r.leading,d="maxWait"in r,a=d?OYe(f0e(r.maxWait)||0,e):a,p="trailing"in r?!!r.trailing:p);function m(E){var _=n,A=i;return n=i=void 0,h=E,s=t.apply(A,_),s}o(m,"invokeFunc");function g(E){return h=E,l=setTimeout(x,e),f?m(E):s}o(g,"leadingEdge");function y(E){var _=E-u,A=E-h,L=e-_;return d?PYe(L,a-A):L}o(y,"remainingWait");function v(E){var _=E-u,A=E-h;return u===void 0||_>=e||_<0||d&&A>=a}o(v,"shouldInvoke");function x(){var E=xP();if(v(E))return b(E);l=setTimeout(x,y(E))}o(x,"timerExpired");function b(E){return l=void 0,p&&n?m(E):(n=i=void 0,s)}o(b,"trailingEdge");function w(){l!==void 0&&clearTimeout(l),h=0,n=u=i=l=void 0}o(w,"cancel");function S(){return l===void 0?s:b(xP())}o(S,"flush");function T(){var E=xP(),_=v(E);if(n=arguments,i=this,u=E,_){if(l===void 0)return g(u);if(d)return clearTimeout(l),l=setTimeout(x,e),m(u)}return l===void 0&&(l=setTimeout(x,e)),s}return o(T,"debounced"),T.cancel=w,T.flush=S,T}function z6(t,e,r,n,i,a){var s;return jn(t)?s=t:s=o1[t]||o1.euclidean,e===0&&jn(t)?s(i,a):s(e,r,n,i,a)}function Lqe(t,e){if(G6(t))return!1;var r=typeof t;return r=="number"||r=="symbol"||r=="boolean"||t==null||Jx(t)?!0:_qe.test(t)||!Aqe.test(t)||e!=null&&t in Object(e)}function Oqe(t){if(!V0(t))return!1;var e=Rpe(t);return e==Nqe||e==Mqe||e==Rqe||e==Iqe}function Fqe(t){return!!N0e&&N0e in t}function Vqe(t){if(t!=null){try{return $qe.call(t)}catch{}try{return t+""}catch{}}return""}function Qqe(t){if(!V0(t)||zqe(t))return!1;var e=Pqe(t)?Kqe:Yqe;return e.test(Uqe(t))}function Jqe(t,e){return t?.[e]}function tXe(t,e){var r=eXe(t,e);return Zqe(r)?r:void 0}function nXe(){this.__data__=Wx?Wx(null):{},this.size=0}function aXe(t){var e=this.has(t)&&delete this.__data__[t];return this.size-=e?1:0,e}function uXe(t){var e=this.__data__;if(Wx){var r=e[t];return r===oXe?void 0:r}return cXe.call(e,t)?e[t]:void 0}function pXe(t){var e=this.__data__;return Wx?e[t]!==void 0:dXe.call(e,t)}function yXe(t,e){var r=this.__data__;return this.size+=this.has(t)?0:1,r[t]=Wx&&e===void 0?gXe:e,this}function h1(t){var e=-1,r=t==null?0:t.length;for(this.clear();++e-1}function RXe(t,e){var r=this.__data__,n=$6(r,t);return n<0?(++this.size,r.push([t,e])):r[n][1]=e,this}function f1(t){var e=-1,r=t==null?0:t.length;for(this.clear();++e-1&&t%1==0&&t0;){var f=i.shift();e(f),a.add(f.id()),l&&n(i,a,f)}return t}function ume(t,e,r){if(r.isParent())for(var n=r._private.children,i=0;i0&&arguments[0]!==void 0?arguments[0]:mKe,e=arguments.length>1?arguments[1]:void 0,r=0;r0?k=C:N=C;while(Math.abs(I)>s&&++O=a?b(M,O):D===0?O:S(M,N,N+h)}o(T,"getTForX");var E=!1;function _(){E=!0,(t!==e||r!==n)&&w()}o(_,"precompute");var A=o(function(N){return E||_(),t===e&&r===n?N:N===0?0:N===1?1:v(T(N),e,n)},"f");A.getControlPoints=function(){return[{x:t,y:e},{x:r,y:n}]};var L="generateBezier("+[t,e,r,n]+")";return A.toString=function(){return L},A}function Q0e(t,e,r,n,i){if(n===1||e===r)return r;var a=i(e,r,n);return t==null||((t.roundValue||t.color)&&(a=Math.round(a)),t.min!==void 0&&(a=Math.max(a,t.min)),t.max!==void 0&&(a=Math.min(a,t.max))),a}function Z0e(t,e){return t.pfValue!=null||t.value!=null?t.pfValue!=null&&(e==null||e.type.units!=="%")?t.pfValue:t.value:t}function jg(t,e,r,n,i){var a=i!=null?i.type:null;r<0?r=0:r>1&&(r=1);var s=Z0e(t,i),l=Z0e(e,i);if(ft(s)&&ft(l))return Q0e(a,s,l,r,n);if(vn(s)&&vn(l)){for(var u=[],h=0;h0?(m==="spring"&&g.push(s.duration),s.easingImpl=v6[m].apply(null,g)):s.easingImpl=v6[m]}var y=s.easingImpl,v;if(s.duration===0?v=1:v=(r-u)/s.duration,s.applying&&(v=s.progress),v<0?v=0:v>1&&(v=1),s.delay==null){var x=s.startPosition,b=s.position;if(b&&i&&!t.locked()){var w={};Nx(x.x,b.x)&&(w.x=jg(x.x,b.x,v,y)),Nx(x.y,b.y)&&(w.y=jg(x.y,b.y,v,y)),t.position(w)}var S=s.startPan,T=s.pan,E=a.pan,_=T!=null&&n;_&&(Nx(S.x,T.x)&&(E.x=jg(S.x,T.x,v,y)),Nx(S.y,T.y)&&(E.y=jg(S.y,T.y,v,y)),t.emit("pan"));var A=s.startZoom,L=s.zoom,M=L!=null&&n;M&&(Nx(A,L)&&(a.zoom=Hx(a.minZoom,jg(A,L,v,y),a.maxZoom)),t.emit("zoom")),(_||M)&&t.emit("viewport");var N=s.style;if(N&&N.length>0&&i){for(var k=0;k=0;_--){var A=E[_];A()}E.splice(0,E.length)},"callbacks"),b=m.length-1;b>=0;b--){var w=m[b],S=w._private;if(S.stopped){m.splice(b,1),S.hooked=!1,S.playing=!1,S.started=!1,x(S.frames);continue}!S.playing&&!S.applying||(S.playing&&S.applying&&(S.applying=!1),S.started||LKe(f,w,t),_Ke(f,w,t,d),S.applying&&(S.applying=!1),x(S.frames),S.step!=null&&S.step(t),w.completed()&&(m.splice(b,1),S.hooked=!1,S.playing=!1,S.started=!1,x(S.completes)),y=!0)}return!d&&m.length===0&&g.length===0&&n.push(f),y}o(i,"stepOne");for(var a=!1,s=0;s0?e.notify("draw",r):e.notify("draw")),r.unmerge(n),e.emit("step")}function Ame(t){this.options=Wt({},BKe,FKe,t)}function _me(t){this.options=Wt({},zKe,t)}function Lme(t){this.options=Wt({},GKe,t)}function j6(t){this.options=Wt({},$Ke,t),this.options.layout=this;var e=this.options.eles.nodes(),r=this.options.eles.edges(),n=r.filter(function(i){var a=i.source().data("id"),s=i.target().data("id"),l=e.some(function(h){return h.data("id")===a}),u=e.some(function(h){return h.data("id")===s});return!l||!u});this.options.eles=this.options.eles.not(n)}function Rme(t){this.options=Wt({},iQe,t)}function dB(t){this.options=Wt({},aQe,t)}function Nme(t){this.options=Wt({},sQe,t)}function Mme(t){this.options=Wt({},oQe,t)}function Ime(t){this.options=t,this.notifications=0}function Bme(t,e){e.radius===0?t.lineTo(e.cx,e.cy):t.arc(e.cx,e.cy,e.radius,e.startAngle,e.endAngle,e.counterClockwise)}function mB(t,e,r,n){var i=arguments.length>4&&arguments[4]!==void 0?arguments[4]:!0;return n===0||e.radius===0?{cx:e.x,cy:e.y,radius:0,startX:e.x,startY:e.y,stopX:e.x,stopY:e.y,startAngle:void 0,endAngle:void 0,counterClockwise:void 0}:(uQe(t,e,r,n,i),{cx:GP,cy:$P,radius:z0,startX:Ome,startY:Pme,stopX:VP,stopY:UP,startAngle:Gc.ang+Math.PI/2*G0,endAngle:Jo.ang-Math.PI/2*G0,counterClockwise:w6})}function Fme(t){var e=[];if(t!=null){for(var r=0;r5&&arguments[5]!==void 0?arguments[5]:5,s=arguments.length>6?arguments[6]:void 0;t.beginPath(),t.moveTo(e+a,r),t.lineTo(e+n-a,r),t.quadraticCurveTo(e+n,r,e+n,r+a),t.lineTo(e+n,r+i-a),t.quadraticCurveTo(e+n,r+i,e+n-a,r+i),t.lineTo(e+a,r+i),t.quadraticCurveTo(e,r+i,e,r+i-a),t.lineTo(e,r+a),t.quadraticCurveTo(e,r,e+a,r),t.closePath(),s?t.stroke():t.fill()}function ZQe(t,e){for(var r=atob(t),n=new ArrayBuffer(r.length),i=new Uint8Array(n),a=0;a{"use strict";o(Hi,"_typeof");o(XP,"_classCallCheck");o(s0e,"_defineProperties");o(jP,"_createClass");o(bpe,"_defineProperty$1");o($l,"_slicedToArray");o(_He,"_arrayWithHoles");o(LHe,"_iterableToArrayLimit");o(wpe,"_unsupportedIterableToArray");o(o0e,"_arrayLikeToArray");o(DHe,"_nonIterableRest");o(Tpe,"_createForOfIteratorHelper");Vi=typeof window>"u"?null:window,l0e=Vi?Vi.navigator:null;Vi&&Vi.document;RHe=Hi(""),kpe=Hi({}),NHe=Hi(function(){}),MHe=typeof HTMLElement>"u"?"undefined":Hi(HTMLElement),Qx=o(function(e){return e&&e.instanceString&&jn(e.instanceString)?e.instanceString():null},"instanceStr"),zt=o(function(e){return e!=null&&Hi(e)==RHe},"string"),jn=o(function(e){return e!=null&&Hi(e)===NHe},"fn"),vn=o(function(e){return!xo(e)&&(Array.isArray?Array.isArray(e):e!=null&&e instanceof Array)},"array"),Mr=o(function(e){return e!=null&&Hi(e)===kpe&&!vn(e)&&e.constructor===Object},"plainObject"),IHe=o(function(e){return e!=null&&Hi(e)===kpe},"object"),ft=o(function(e){return e!=null&&Hi(e)===Hi(1)&&!isNaN(e)},"number"),OHe=o(function(e){return ft(e)&&Math.floor(e)===e},"integer"),k6=o(function(e){if(MHe!=="undefined")return e!=null&&e instanceof HTMLElement},"htmlElement"),xo=o(function(e){return Zx(e)||Epe(e)},"elementOrCollection"),Zx=o(function(e){return Qx(e)==="collection"&&e._private.single},"element"),Epe=o(function(e){return Qx(e)==="collection"&&!e._private.single},"collection"),KP=o(function(e){return Qx(e)==="core"},"core"),Cpe=o(function(e){return Qx(e)==="stylesheet"},"stylesheet"),PHe=o(function(e){return Qx(e)==="event"},"event"),Sf=o(function(e){return e==null?!0:!!(e===""||e.match(/^\s+$/))},"emptyString"),BHe=o(function(e){return typeof HTMLElement>"u"?!1:e instanceof HTMLElement},"domElement"),FHe=o(function(e){return Mr(e)&&ft(e.x1)&&ft(e.x2)&&ft(e.y1)&&ft(e.y2)},"boundingBox"),zHe=o(function(e){return IHe(e)&&jn(e.then)},"promise"),GHe=o(function(){return l0e&&l0e.userAgent.match(/msie|trident|edge/i)},"ms"),Gx=o(function(e,r){r||(r=o(function(){if(arguments.length===1)return arguments[0];if(arguments.length===0)return"undefined";for(var a=[],s=0;sr?1:0},"ascending"),qHe=o(function(e,r){return-1*Ape(e,r)},"descending"),Wt=Object.assign!=null?Object.assign.bind(Object):function(t){for(var e=arguments,r=1;r1&&(v-=1),v<1/6?g+(y-g)*6*v:v<1/2?y:v<2/3?g+(y-g)*(2/3-v)*6:g}o(f,"hue2rgb");var d=new RegExp("^"+UHe+"$").exec(e);if(d){if(n=parseInt(d[1]),n<0?n=(360- -1*n%360)%360:n>360&&(n=n%360),n/=360,i=parseFloat(d[2]),i<0||i>100||(i=i/100,a=parseFloat(d[3]),a<0||a>100)||(a=a/100,s=d[4],s!==void 0&&(s=parseFloat(s),s<0||s>1)))return;if(i===0)l=u=h=Math.round(a*255);else{var p=a<.5?a*(1+i):a+i-a*i,m=2*a-p;l=Math.round(255*f(m,p,n+1/3)),u=Math.round(255*f(m,p,n)),h=Math.round(255*f(m,p,n-1/3))}r=[l,u,h,s]}return r},"hsl2tuple"),KHe=o(function(e){var r,n=new RegExp("^"+$He+"$").exec(e);if(n){r=[];for(var i=[],a=1;a<=3;a++){var s=n[a];if(s[s.length-1]==="%"&&(i[a]=!0),s=parseFloat(s),i[a]&&(s=s/100*255),s<0||s>255)return;r.push(Math.floor(s))}var l=i[1]||i[2]||i[3],u=i[1]&&i[2]&&i[3];if(l&&!u)return;var h=n[4];if(h!==void 0){if(h=parseFloat(h),h<0||h>1)return;r.push(h)}}return r},"rgb2tuple"),QHe=o(function(e){return JHe[e.toLowerCase()]},"colorname2tuple"),ZHe=o(function(e){return(vn(e)?e:null)||QHe(e)||XHe(e)||KHe(e)||jHe(e)},"color2tuple"),JHe={transparent:[0,0,0,0],aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],grey:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},_pe=o(function(e){for(var r=e.map,n=e.keys,i=n.length,a=0;a1&&arguments[1]!==void 0?arguments[1]:Zg,n=r,i;i=e.next(),!i.done;)n=n*Mpe+i.value|0;return n},"hashIterableInts"),$x=o(function(e){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:Zg;return r*Mpe+e|0},"hashInt"),Vx=o(function(e){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:Ix;return(r<<5)+r+e|0},"hashIntAlt"),zYe=o(function(e,r){return e*2097152+r},"combineHashes"),bf=o(function(e){return e[0]*2097152+e[1]},"combineHashesArray"),r6=o(function(e,r){return[$x(e[0],r[0]),Vx(e[1],r[1])]},"hashArrays"),GYe=o(function(e,r){var n={value:0,done:!1},i=0,a=e.length,s={next:o(function(){return i=0&&!(e[i]===r&&(e.splice(i,1),n));i--);},"removeFromArray"),eB=o(function(e){e.splice(0,e.length)},"clearArray"),qYe=o(function(e,r){for(var n=0;n"u"?"undefined":Hi(Set))!==jYe?Set:KYe,B6=o(function(e,r){var n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!0;if(e===void 0||r===void 0||!KP(e)){oi("An element must have a core reference and parameters set");return}var i=r.group;if(i==null&&(r.data&&r.data.source!=null&&r.data.target!=null?i="edges":i="nodes"),i!=="nodes"&&i!=="edges"){oi("An element must be of type `nodes` or `edges`; you specified `"+i+"`");return}this.length=1,this[0]=this;var a=this._private={cy:e,single:!0,data:r.data||{},position:r.position||{x:0,y:0},autoWidth:void 0,autoHeight:void 0,autoPadding:void 0,compoundBoundsClean:!1,listeners:[],group:i,style:{},rstyle:{},styleCxts:[],styleKeys:{},removed:!0,selected:!!r.selected,selectable:r.selectable===void 0?!0:!!r.selectable,locked:!!r.locked,grabbed:!1,grabbable:r.grabbable===void 0?!0:!!r.grabbable,pannable:r.pannable===void 0?i==="edges":!!r.pannable,active:!1,classes:new c1,animation:{current:[],queue:[]},rscratch:{},scratch:r.scratch||{},edges:[],children:[],parent:r.parent&&r.parent.isNode()?r.parent:null,traversalCache:{},backgrounding:!1,bbCache:null,bbCacheShift:{x:0,y:0},bodyBounds:null,overlayBounds:null,labelBounds:{all:null,source:null,target:null,main:null},arrowBounds:{source:null,target:null,"mid-source":null,"mid-target":null}};if(a.position.x==null&&(a.position.x=0),a.position.y==null&&(a.position.y=0),r.renderedPosition){var s=r.renderedPosition,l=e.pan(),u=e.zoom();a.position={x:(s.x-l.x)/u,y:(s.y-l.y)/u}}var h=[];vn(r.classes)?h=r.classes:zt(r.classes)&&(h=r.classes.split(/\s+/));for(var f=0,d=h.length;fb?1:0},"defaultCmp"),f=o(function(x,b,w,S,T){var E;if(w==null&&(w=0),T==null&&(T=n),w<0)throw new Error("lo must be non-negative");for(S==null&&(S=x.length);wM;0<=M?L++:L--)A.push(L);return A}.apply(this).reverse(),_=[],S=0,T=E.length;SN;0<=N?++A:--A)k.push(s(x,w));return k},"nsmallest"),y=o(function(x,b,w,S){var T,E,_;for(S==null&&(S=n),T=x[w];w>b;){if(_=w-1>>1,E=x[_],S(T,E)<0){x[w]=E,w=_;continue}break}return x[w]=T},"_siftdown"),v=o(function(x,b,w){var S,T,E,_,A;for(w==null&&(w=n),T=x.length,A=b,E=x[b],S=2*b+1;S0;){var E=b.pop(),_=v(E),A=E.id();if(p[A]=_,_!==1/0)for(var L=E.neighborhood().intersect(g),M=0;M0)for(F.unshift(P);d[$];){var z=d[$];F.unshift(z.edge),F.unshift(z.node),B=z.node,$=B.id()}return l.spawn(F)},"pathTo")}},"dijkstra")},eWe={kruskal:o(function(e){e=e||function(w){return 1};for(var r=this.byGroup(),n=r.nodes,i=r.edges,a=n.length,s=new Array(a),l=n,u=o(function(S){for(var T=0;T0;){if(T(),_++,S===f){for(var A=[],L=a,M=f,N=x[M];A.unshift(L),N!=null&&A.unshift(N),L=v[M],L!=null;)M=L.id(),N=x[M];return{found:!0,distance:d[S],path:this.spawn(A),steps:_}}m[S]=!0;for(var k=w._private.edges,I=0;IN&&(g[M]=N,b[M]=L,w[M]=T),!a){var k=L*f+A;!a&&g[k]>N&&(g[k]=N,b[k]=A,w[k]=T)}}}for(var I=0;I1&&arguments[1]!==void 0?arguments[1]:s,Se=w(ke),Ue=[],Pe=Se;;){if(Pe==null)return r.spawn();var _e=b(Pe),me=_e.edge,W=_e.pred;if(Ue.unshift(Pe[0]),Pe.same(Ie)&&Ue.length>0)break;me!=null&&Ue.unshift(me),Pe=W}return u.spawn(Ue)},"pathTo"),E=0;E=0;f--){var d=h[f],p=d[1],m=d[2];(r[p]===l&&r[m]===u||r[p]===u&&r[m]===l)&&h.splice(f,1)}for(var g=0;gi;){var a=Math.floor(Math.random()*r.length);r=lWe(a,e,r),n--}return r},"contractUntil"),cWe={kargerStein:o(function(){var e=this,r=this.byGroup(),n=r.nodes,i=r.edges;i.unmergeBy(function(F){return F.isLoop()});var a=n.length,s=i.length,l=Math.ceil(Math.pow(Math.log(a)/Math.LN2,2)),u=Math.floor(a/oWe);if(a<2){oi("At least 2 nodes are required for Karger-Stein algorithm");return}for(var h=[],f=0;f1&&arguments[1]!==void 0?arguments[1]:0,n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:e.length,i=1/0,a=r;a1&&arguments[1]!==void 0?arguments[1]:0,n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:e.length,i=-1/0,a=r;a1&&arguments[1]!==void 0?arguments[1]:0,n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:e.length,i=0,a=0,s=r;s1&&arguments[1]!==void 0?arguments[1]:0,n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:e.length,i=arguments.length>3&&arguments[3]!==void 0?arguments[3]:!0,a=arguments.length>4&&arguments[4]!==void 0?arguments[4]:!0,s=arguments.length>5&&arguments[5]!==void 0?arguments[5]:!0;i?e=e.slice(r,n):(n0&&e.splice(0,r));for(var l=0,u=e.length-1;u>=0;u--){var h=e[u];s?isFinite(h)||(e[u]=-1/0,l++):e.splice(u,1)}a&&e.sort(function(p,m){return p-m});var f=e.length,d=Math.floor(f/2);return f%2!==0?e[d+1+l]:(e[d-1+l]+e[d+l])/2},"median"),mWe=o(function(e){return Math.PI*e/180},"deg2rad"),n6=o(function(e,r){return Math.atan2(r,e)-Math.PI/2},"getAngleFromDisp"),tB=Math.log2||function(t){return Math.log(t)/Math.log(2)},$pe=o(function(e){return e>0?1:e<0?-1:0},"signum"),H0=o(function(e,r){return Math.sqrt(B0(e,r))},"dist"),B0=o(function(e,r){var n=r.x-e.x,i=r.y-e.y;return n*n+i*i},"sqdist"),gWe=o(function(e){for(var r=e.length,n=0,i=0;i=e.x1&&e.y2>=e.y1)return{x1:e.x1,y1:e.y1,x2:e.x2,y2:e.y2,w:e.x2-e.x1,h:e.y2-e.y1};if(e.w!=null&&e.h!=null&&e.w>=0&&e.h>=0)return{x1:e.x1,y1:e.y1,x2:e.x1+e.w,y2:e.y1+e.h,w:e.w,h:e.h}}},"makeBoundingBox"),vWe=o(function(e){return{x1:e.x1,x2:e.x2,w:e.w,y1:e.y1,y2:e.y2,h:e.h}},"copyBoundingBox"),xWe=o(function(e){e.x1=1/0,e.y1=1/0,e.x2=-1/0,e.y2=-1/0,e.w=0,e.h=0},"clearBoundingBox"),bWe=o(function(e,r,n){return{x1:e.x1+r,x2:e.x2+r,y1:e.y1+n,y2:e.y2+n,w:e.w,h:e.h}},"shiftBoundingBox"),Vpe=o(function(e,r){e.x1=Math.min(e.x1,r.x1),e.x2=Math.max(e.x2,r.x2),e.w=e.x2-e.x1,e.y1=Math.min(e.y1,r.y1),e.y2=Math.max(e.y2,r.y2),e.h=e.y2-e.y1},"updateBoundingBox"),wWe=o(function(e,r,n){e.x1=Math.min(e.x1,r),e.x2=Math.max(e.x2,r),e.w=e.x2-e.x1,e.y1=Math.min(e.y1,n),e.y2=Math.max(e.y2,n),e.h=e.y2-e.y1},"expandBoundingBoxByPoint"),p6=o(function(e){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:0;return e.x1-=r,e.x2+=r,e.y1-=r,e.y2+=r,e.w=e.x2-e.x1,e.h=e.y2-e.y1,e},"expandBoundingBox"),m6=o(function(e){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:[0],n,i,a,s;if(r.length===1)n=i=a=s=r[0];else if(r.length===2)n=a=r[0],s=i=r[1];else if(r.length===4){var l=$l(r,4);n=l[0],i=l[1],a=l[2],s=l[3]}return e.x1-=s,e.x2+=i,e.y1-=n,e.y2+=a,e.w=e.x2-e.x1,e.h=e.y2-e.y1,e},"expandBoundingBoxSides"),g0e=o(function(e,r){e.x1=r.x1,e.y1=r.y1,e.x2=r.x2,e.y2=r.y2,e.w=e.x2-e.x1,e.h=e.y2-e.y1},"assignBoundingBox"),rB=o(function(e,r){return!(e.x1>r.x2||r.x1>e.x2||e.x2r.y2||r.y1>e.y2)},"boundingBoxesIntersect"),s1=o(function(e,r,n){return e.x1<=r&&r<=e.x2&&e.y1<=n&&n<=e.y2},"inBoundingBox"),TWe=o(function(e,r){return s1(e,r.x,r.y)},"pointInBoundingBox"),Upe=o(function(e,r){return s1(e,r.x1,r.y1)&&s1(e,r.x2,r.y2)},"boundingBoxInBoundingBox"),Hpe=o(function(e,r,n,i,a,s,l){var u=arguments.length>7&&arguments[7]!==void 0?arguments[7]:"auto",h=u==="auto"?Y0(a,s):u,f=a/2,d=s/2;h=Math.min(h,f,d);var p=h!==f,m=h!==d,g;if(p){var y=n-f+h-l,v=i-d-l,x=n+f-h+l,b=v;if(g=kf(e,r,n,i,y,v,x,b,!1),g.length>0)return g}if(m){var w=n+f+l,S=i-d+h-l,T=w,E=i+d-h+l;if(g=kf(e,r,n,i,w,S,T,E,!1),g.length>0)return g}if(p){var _=n-f+h-l,A=i+d+l,L=n+f-h+l,M=A;if(g=kf(e,r,n,i,_,A,L,M,!1),g.length>0)return g}if(m){var N=n-f-l,k=i-d+h-l,I=N,C=i+d-h+l;if(g=kf(e,r,n,i,N,k,I,C,!1),g.length>0)return g}var O;{var D=n-f+h,P=i-d+h;if(O=Ox(e,r,n,i,D,P,h+l),O.length>0&&O[0]<=D&&O[1]<=P)return[O[0],O[1]]}{var F=n+f-h,B=i-d+h;if(O=Ox(e,r,n,i,F,B,h+l),O.length>0&&O[0]>=F&&O[1]<=B)return[O[0],O[1]]}{var $=n+f-h,z=i+d-h;if(O=Ox(e,r,n,i,$,z,h+l),O.length>0&&O[0]>=$&&O[1]>=z)return[O[0],O[1]]}{var Y=n-f+h,Q=i+d-h;if(O=Ox(e,r,n,i,Y,Q,h+l),O.length>0&&O[0]<=Y&&O[1]>=Q)return[O[0],O[1]]}return[]},"roundRectangleIntersectLine"),kWe=o(function(e,r,n,i,a,s,l){var u=l,h=Math.min(n,a),f=Math.max(n,a),d=Math.min(i,s),p=Math.max(i,s);return h-u<=e&&e<=f+u&&d-u<=r&&r<=p+u},"inLineVicinity"),EWe=o(function(e,r,n,i,a,s,l,u,h){var f={x1:Math.min(n,l,a)-h,x2:Math.max(n,l,a)+h,y1:Math.min(i,u,s)-h,y2:Math.max(i,u,s)+h};return!(ef.x2||rf.y2)},"inBezierVicinity"),CWe=o(function(e,r,n,i){n-=i;var a=r*r-4*e*n;if(a<0)return[];var s=Math.sqrt(a),l=2*e,u=(-r+s)/l,h=(-r-s)/l;return[u,h]},"solveQuadratic"),SWe=o(function(e,r,n,i,a){var s=1e-5;e===0&&(e=s),r/=e,n/=e,i/=e;var l,u,h,f,d,p,m,g;if(u=(3*n-r*r)/9,h=-(27*i)+r*(9*n-2*(r*r)),h/=54,l=u*u*u+h*h,a[1]=0,m=r/3,l>0){d=h+Math.sqrt(l),d=d<0?-Math.pow(-d,1/3):Math.pow(d,1/3),p=h-Math.sqrt(l),p=p<0?-Math.pow(-p,1/3):Math.pow(p,1/3),a[0]=-m+d+p,m+=(d+p)/2,a[4]=a[2]=-m,m=Math.sqrt(3)*(-p+d)/2,a[3]=m,a[5]=-m;return}if(a[5]=a[3]=0,l===0){g=h<0?-Math.pow(-h,1/3):Math.pow(h,1/3),a[0]=-m+2*g,a[4]=a[2]=-(g+m);return}u=-u,f=u*u*u,f=Math.acos(h/Math.sqrt(f)),g=2*Math.sqrt(u),a[0]=-m+g*Math.cos(f/3),a[2]=-m+g*Math.cos((f+2*Math.PI)/3),a[4]=-m+g*Math.cos((f+4*Math.PI)/3)},"solveCubic"),AWe=o(function(e,r,n,i,a,s,l,u){var h=1*n*n-4*n*a+2*n*l+4*a*a-4*a*l+l*l+i*i-4*i*s+2*i*u+4*s*s-4*s*u+u*u,f=1*9*n*a-3*n*n-3*n*l-6*a*a+3*a*l+9*i*s-3*i*i-3*i*u-6*s*s+3*s*u,d=1*3*n*n-6*n*a+n*l-n*e+2*a*a+2*a*e-l*e+3*i*i-6*i*s+i*u-i*r+2*s*s+2*s*r-u*r,p=1*n*a-n*n+n*e-a*e+i*s-i*i+i*r-s*r,m=[];SWe(h,f,d,p,m);for(var g=1e-7,y=[],v=0;v<6;v+=2)Math.abs(m[v+1])=0&&m[v]<=1&&y.push(m[v]);y.push(1),y.push(0);for(var x=-1,b,w,S,T=0;T=0?Sh?(e-a)*(e-a)+(r-s)*(r-s):f-p},"sqdistToFiniteLine"),zs=o(function(e,r,n){for(var i,a,s,l,u,h=0,f=0;f=e&&e>=s||i<=e&&e<=s)u=(e-i)/(s-i)*(l-a)+a,u>r&&h++;else continue;return h%2!==0},"pointInsidePolygonPoints"),Qu=o(function(e,r,n,i,a,s,l,u,h){var f=new Array(n.length),d;u[0]!=null?(d=Math.atan(u[1]/u[0]),u[0]<0?d=d+Math.PI/2:d=-d-Math.PI/2):d=u;for(var p=Math.cos(-d),m=Math.sin(-d),g=0;g0){var v=A6(f,-h);y=S6(v)}else y=f;return zs(e,r,y)},"pointInsidePolygon"),LWe=o(function(e,r,n,i,a,s,l,u){for(var h=new Array(n.length*2),f=0;f=0&&v<=1&&b.push(v),x>=0&&x<=1&&b.push(x),b.length===0)return[];var w=b[0]*u[0]+e,S=b[0]*u[1]+r;if(b.length>1){if(b[0]==b[1])return[w,S];var T=b[1]*u[0]+e,E=b[1]*u[1]+r;return[w,S,T,E]}else return[w,S]},"intersectLineCircle"),TP=o(function(e,r,n){return r<=e&&e<=n||n<=e&&e<=r?e:e<=r&&r<=n||n<=r&&r<=e?r:n},"midOfThree"),kf=o(function(e,r,n,i,a,s,l,u,h){var f=e-a,d=n-e,p=l-a,m=r-s,g=i-r,y=u-s,v=p*m-y*f,x=d*m-g*f,b=y*d-p*g;if(b!==0){var w=v/b,S=x/b,T=.001,E=0-T,_=1+T;return E<=w&&w<=_&&E<=S&&S<=_?[e+w*d,r+w*g]:h?[e+w*d,r+w*g]:[]}else return v===0||x===0?TP(e,n,l)===l?[l,u]:TP(e,n,a)===a?[a,s]:TP(a,l,n)===n?[n,i]:[]:[]},"finiteLinesIntersect"),Yx=o(function(e,r,n,i,a,s,l,u){var h=[],f,d=new Array(n.length),p=!0;s==null&&(p=!1);var m;if(p){for(var g=0;g0){var y=A6(d,-u);m=S6(y)}else m=d}else m=n;for(var v,x,b,w,S=0;S2){for(var g=[f[0],f[1]],y=Math.pow(g[0]-e,2)+Math.pow(g[1]-r,2),v=1;vf&&(f=S)},"set"),get:o(function(w){return h[w]},"get")},p=0;p0?D=O.edgesTo(C)[0]:D=C.edgesTo(O)[0];var P=i(D);C=C.id(),A[C]>A[k]+P&&(A[C]=A[k]+P,L.nodes.indexOf(C)<0?L.push(C):L.updateItem(C),_[C]=0,E[C]=[]),A[C]==A[k]+P&&(_[C]=_[C]+_[k],E[C].push(k))}else for(var F=0;F0;){for(var Y=T.pop(),Q=0;Q0&&l.push(n[u]);l.length!==0&&a.push(i.collection(l))}return a},"assign"),YWe=o(function(e,r){for(var n=0;n5&&arguments[5]!==void 0?arguments[5]:XWe,l=i,u,h,f=0;f=2?Lx(e,r,n,0,w0e,jWe):Lx(e,r,n,0,b0e)},"euclidean"),squaredEuclidean:o(function(e,r,n){return Lx(e,r,n,0,w0e)},"squaredEuclidean"),manhattan:o(function(e,r,n){return Lx(e,r,n,0,b0e)},"manhattan"),max:o(function(e,r,n){return Lx(e,r,n,-1/0,KWe)},"max")};o1["squared-euclidean"]=o1.squaredEuclidean;o1.squaredeuclidean=o1.squaredEuclidean;o(z6,"clusteringDistance");QWe=Sa({k:2,m:2,sensitivityThreshold:1e-4,distance:"euclidean",maxIterations:10,attributes:[],testMode:!1,testCentroids:null}),iB=o(function(e){return QWe(e)},"setOptions"),_6=o(function(e,r,n,i,a){var s=a!=="kMedoids",l=s?function(d){return n[d]}:function(d){return i[d](n)},u=o(function(p){return i[p](r)},"getQ"),h=n,f=r;return z6(e,i.length,l,u,h,f)},"getDist"),kP=o(function(e,r,n){for(var i=n.length,a=new Array(i),s=new Array(i),l=new Array(r),u=null,h=0;hn)return!1}return!0},"haveMatricesConverged"),eqe=o(function(e,r,n){for(var i=0;il&&(l=r[h][f],u=f);a[u].push(e[h])}for(var d=0;d=a.threshold||a.mode==="dendrogram"&&e.length===1)return!1;var g=r[s],y=r[i[s]],v;a.mode==="dendrogram"?v={left:g,right:y,key:g.key}:v={value:g.value.concat(y.value),key:g.key},e[g.index]=v,e.splice(y.index,1),r[g.key]=v;for(var x=0;xn[y.key][b.key]&&(u=n[y.key][b.key])):a.linkage==="max"?(u=n[g.key][b.key],n[g.key][b.key]0&&i.push(a);return i},"findExemplars"),A0e=o(function(e,r,n){for(var i=[],a=0;al&&(s=h,l=r[a*e+h])}s>0&&i.push(s)}for(var f=0;fh&&(u=f,h=d)}n[a]=s[u]}return i=A0e(e,r,n),i},"assign"),_0e=o(function(e){for(var r=this.cy(),n=this.nodes(),i=pqe(e),a={},s=0;s=N?(k=N,N=C,I=O):C>k&&(k=C);for(var D=0;D0?1:0;_[L%i.minIterations*l+Y]=Q,z+=Q}if(z>0&&(L>=i.minIterations-1||L==i.maxIterations-1)){for(var X=0,ie=0;ie1||E>1)&&(l=!0),d[w]=[],b.outgoers().forEach(function(A){A.isEdge()&&d[w].push(A.id())})}else p[w]=[void 0,b.target().id()]}):s.forEach(function(b){var w=b.id();if(b.isNode()){var S=b.degree(!0);S%2&&(u?h?l=!0:h=w:u=w),d[w]=[],b.connectedEdges().forEach(function(T){return d[w].push(T.id())})}else p[w]=[b.source().id(),b.target().id()]});var m={found:!1,trail:void 0};if(l)return m;if(h&&u)if(a){if(f&&h!=f)return m;f=h}else{if(f&&h!=f&&u!=f)return m;f||(f=h)}else f||(f=s[0].id());var g=o(function(w){for(var S=w,T=[w],E,_,A;d[S].length;)E=d[S].shift(),_=p[E][0],A=p[E][1],S!=A?(d[A]=d[A].filter(function(L){return L!=E}),S=A):!a&&S!=_&&(d[_]=d[_].filter(function(L){return L!=E}),S=_),T.unshift(E),T.unshift(S);return T},"walk"),y=[],v=[];for(v=g(f);v.length!=1;)d[v[0]].length==0?(y.unshift(s.getElementById(v.shift())),y.unshift(s.getElementById(v.shift()))):v=g(v.shift()).concat(v);y.unshift(s.getElementById(v.shift()));for(var x in d)if(d[x].length)return m;return m.found=!0,m.trail=this.spawn(y,!0),m},"hierholzer")},s6=o(function(){var e=this,r={},n=0,i=0,a=[],s=[],l={},u=o(function(p,m){for(var g=s.length-1,y=[],v=e.spawn();s[g].x!=p||s[g].y!=m;)y.push(s.pop().edge),g--;y.push(s.pop().edge),y.forEach(function(x){var b=x.connectedNodes().intersection(e);v.merge(x),b.forEach(function(w){var S=w.id(),T=w.connectedEdges().intersection(e);v.merge(w),r[S].cutVertex?v.merge(T.filter(function(E){return E.isLoop()})):v.merge(T)})}),a.push(v)},"buildComponent"),h=o(function d(p,m,g){p===g&&(i+=1),r[m]={id:n,low:n++,cutVertex:!1};var y=e.getElementById(m).connectedEdges().intersection(e);if(y.size()===0)a.push(e.spawn(e.getElementById(m)));else{var v,x,b,w;y.forEach(function(S){v=S.source().id(),x=S.target().id(),b=v===m?x:v,b!==g&&(w=S.id(),l[w]||(l[w]=!0,s.push({x:m,y:b,edge:S})),b in r?r[m].low=Math.min(r[m].low,r[b].id):(d(p,b,m),r[m].low=Math.min(r[m].low,r[b].low),r[m].id<=r[b].low&&(r[m].cutVertex=!0,u(m,b))))})}},"biconnectedSearch");e.forEach(function(d){if(d.isNode()){var p=d.id();p in r||(i=0,h(p,p),r[p].cutVertex=i>1)}});var f=Object.keys(r).filter(function(d){return r[d].cutVertex}).map(function(d){return e.getElementById(d)});return{cut:e.spawn(f),components:a}},"hopcroftTarjanBiconnected"),Tqe={hopcroftTarjanBiconnected:s6,htbc:s6,htb:s6,hopcroftTarjanBiconnectedComponents:s6},o6=o(function(){var e=this,r={},n=0,i=[],a=[],s=e.spawn(e),l=o(function u(h){a.push(h),r[h]={index:n,low:n++,explored:!1};var f=e.getElementById(h).connectedEdges().intersection(e);if(f.forEach(function(y){var v=y.target().id();v!==h&&(v in r||u(v),r[v].explored||(r[h].low=Math.min(r[h].low,r[v].low)))}),r[h].index===r[h].low){for(var d=e.spawn();;){var p=a.pop();if(d.merge(e.getElementById(p)),r[p].low=r[h].index,r[p].explored=!0,p===h)break}var m=d.edgesWith(d),g=d.merge(m);i.push(g),s=s.difference(g)}},"stronglyConnectedSearch");return e.forEach(function(u){if(u.isNode()){var h=u.id();h in r||l(h)}}),{cut:s,components:i}},"tarjanStronglyConnected"),kqe={tarjanStronglyConnected:o6,tsc:o6,tscc:o6,tarjanStronglyConnectedComponents:o6},Qpe={};[Ux,JYe,eWe,rWe,iWe,sWe,cWe,IWe,r1,n1,IP,qWe,sqe,fqe,xqe,wqe,Tqe,kqe].forEach(function(t){Wt(Qpe,t)});Zpe=0,Jpe=1,eme=2,Zu=o(function t(e){if(!(this instanceof t))return new t(e);this.id="Thenable/1.0.7",this.state=Zpe,this.fulfillValue=void 0,this.rejectReason=void 0,this.onFulfilled=[],this.onRejected=[],this.proxy={then:this.then.bind(this)},typeof e=="function"&&e.call(this,this.fulfill.bind(this),this.reject.bind(this))},"api");Zu.prototype={fulfill:o(function(e){return L0e(this,Jpe,"fulfillValue",e)},"fulfill"),reject:o(function(e){return L0e(this,eme,"rejectReason",e)},"reject"),then:o(function(e,r){var n=this,i=new Zu;return n.onFulfilled.push(R0e(e,i,"fulfill")),n.onRejected.push(R0e(r,i,"reject")),tme(n),i.proxy},"then")};L0e=o(function(e,r,n,i){return e.state===Zpe&&(e.state=r,e[n]=i,tme(e)),e},"deliver"),tme=o(function(e){e.state===Jpe?D0e(e,"onFulfilled",e.fulfillValue):e.state===eme&&D0e(e,"onRejected",e.rejectReason)},"execute"),D0e=o(function(e,r,n){if(e[r].length!==0){var i=e[r];e[r]=[];var a=o(function(){for(var l=0;l0},"animatedImpl")},"animated"),clearQueue:o(function(){return o(function(){var r=this,n=r.length!==void 0,i=n?r:[r],a=this._private.cy||this;if(!a.styleEnabled())return this;for(var s=0;s0&&this.spawn(i).updateStyle().emit("class"),r},"classes"),addClass:o(function(e){return this.toggleClass(e,!0)},"addClass"),hasClass:o(function(e){var r=this[0];return r!=null&&r._private.classes.has(e)},"hasClass"),toggleClass:o(function(e,r){vn(e)||(e=e.match(/\S+/g)||[]);for(var n=this,i=r===void 0,a=[],s=0,l=n.length;s0&&this.spawn(a).updateStyle().emit("class"),n},"toggleClass"),removeClass:o(function(e){return this.toggleClass(e,!1)},"removeClass"),flashClass:o(function(e,r){var n=this;if(r==null)r=250;else if(r===0)return n;return n.addClass(e),setTimeout(function(){n.removeClass(e)},r),n},"flashClass")};g6.className=g6.classNames=g6.classes;Nr={metaChar:"[\\!\\\"\\#\\$\\%\\&\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]",comparatorOp:"=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=",boolOp:"\\?|\\!|\\^",string:`"(?:\\\\"|[^"])*"|'(?:\\\\'|[^'])*'`,number:Ui,meta:"degree|indegree|outdegree",separator:"\\s*,\\s*",descendant:"\\s+",child:"\\s+>\\s+",subject:"\\$",group:"node|edge|\\*",directedEdge:"\\s+->\\s+",undirectedEdge:"\\s+<->\\s+"};Nr.variable="(?:[\\w-.]|(?:\\\\"+Nr.metaChar+"))+";Nr.className="(?:[\\w-]|(?:\\\\"+Nr.metaChar+"))+";Nr.value=Nr.string+"|"+Nr.number;Nr.id=Nr.variable;(function(){var t,e,r;for(t=Nr.comparatorOp.split("|"),r=0;r=0)&&e!=="="&&(Nr.comparatorOp+="|\\!"+e)})();un=o(function(){return{checks:[]}},"newQuery"),Ct={GROUP:0,COLLECTION:1,FILTER:2,DATA_COMPARE:3,DATA_EXIST:4,DATA_BOOL:5,META_COMPARE:6,STATE:7,ID:8,CLASS:9,UNDIRECTED_EDGE:10,DIRECTED_EDGE:11,NODE_SOURCE:12,NODE_TARGET:13,NODE_NEIGHBOR:14,CHILD:15,DESCENDANT:16,PARENT:17,ANCESTOR:18,COMPOUND_SPLIT:19,TRUE:20},PP=[{selector:":selected",matches:o(function(e){return e.selected()},"matches")},{selector:":unselected",matches:o(function(e){return!e.selected()},"matches")},{selector:":selectable",matches:o(function(e){return e.selectable()},"matches")},{selector:":unselectable",matches:o(function(e){return!e.selectable()},"matches")},{selector:":locked",matches:o(function(e){return e.locked()},"matches")},{selector:":unlocked",matches:o(function(e){return!e.locked()},"matches")},{selector:":visible",matches:o(function(e){return e.visible()},"matches")},{selector:":hidden",matches:o(function(e){return!e.visible()},"matches")},{selector:":transparent",matches:o(function(e){return e.transparent()},"matches")},{selector:":grabbed",matches:o(function(e){return e.grabbed()},"matches")},{selector:":free",matches:o(function(e){return!e.grabbed()},"matches")},{selector:":removed",matches:o(function(e){return e.removed()},"matches")},{selector:":inside",matches:o(function(e){return!e.removed()},"matches")},{selector:":grabbable",matches:o(function(e){return e.grabbable()},"matches")},{selector:":ungrabbable",matches:o(function(e){return!e.grabbable()},"matches")},{selector:":animated",matches:o(function(e){return e.animated()},"matches")},{selector:":unanimated",matches:o(function(e){return!e.animated()},"matches")},{selector:":parent",matches:o(function(e){return e.isParent()},"matches")},{selector:":childless",matches:o(function(e){return e.isChildless()},"matches")},{selector:":child",matches:o(function(e){return e.isChild()},"matches")},{selector:":orphan",matches:o(function(e){return e.isOrphan()},"matches")},{selector:":nonorphan",matches:o(function(e){return e.isChild()},"matches")},{selector:":compound",matches:o(function(e){return e.isNode()?e.isParent():e.source().isParent()||e.target().isParent()},"matches")},{selector:":loop",matches:o(function(e){return e.isLoop()},"matches")},{selector:":simple",matches:o(function(e){return e.isSimple()},"matches")},{selector:":active",matches:o(function(e){return e.active()},"matches")},{selector:":inactive",matches:o(function(e){return!e.active()},"matches")},{selector:":backgrounding",matches:o(function(e){return e.backgrounding()},"matches")},{selector:":nonbackgrounding",matches:o(function(e){return!e.backgrounding()},"matches")}].sort(function(t,e){return qHe(t.selector,e.selector)}),Pje=function(){for(var t={},e,r=0;r0&&f.edgeCount>0)return tn("The selector `"+e+"` is invalid because it uses both a compound selector and an edge selector"),!1;if(f.edgeCount>1)return tn("The selector `"+e+"` is invalid because it uses multiple edge selectors"),!1;f.edgeCount===1&&tn("The selector `"+e+"` is deprecated. Edge selectors do not take effect on changes to source and target nodes after an edge is added, for performance reasons. Use a class or data selector on edges instead, updating the class or data of an edge when your app detects a change in source or target nodes.")}return!0},"parse"),Vje=o(function(){if(this.toStringCache!=null)return this.toStringCache;for(var e=o(function(f){return f??""},"clean"),r=o(function(f){return zt(f)?'"'+f+'"':e(f)},"cleanVal"),n=o(function(f){return" "+f+" "},"space"),i=o(function(f,d){var p=f.type,m=f.value;switch(p){case Ct.GROUP:{var g=e(m);return g.substring(0,g.length-1)}case Ct.DATA_COMPARE:{var y=f.field,v=f.operator;return"["+y+n(e(v))+r(m)+"]"}case Ct.DATA_BOOL:{var x=f.operator,b=f.field;return"["+e(x)+b+"]"}case Ct.DATA_EXIST:{var w=f.field;return"["+w+"]"}case Ct.META_COMPARE:{var S=f.operator,T=f.field;return"[["+T+n(e(S))+r(m)+"]]"}case Ct.STATE:return m;case Ct.ID:return"#"+m;case Ct.CLASS:return"."+m;case Ct.PARENT:case Ct.CHILD:return a(f.parent,d)+n(">")+a(f.child,d);case Ct.ANCESTOR:case Ct.DESCENDANT:return a(f.ancestor,d)+" "+a(f.descendant,d);case Ct.COMPOUND_SPLIT:{var E=a(f.left,d),_=a(f.subject,d),A=a(f.right,d);return E+(E.length>0?" ":"")+_+A}case Ct.TRUE:return""}},"checkToString"),a=o(function(f,d){return f.checks.reduce(function(p,m,g){return p+(d===f&&g===0?"$":"")+i(m,d)},"")},"queryToString"),s="",l=0;l1&&l=0&&(r=r.replace("!",""),d=!0),r.indexOf("@")>=0&&(r=r.replace("@",""),f=!0),(a||l||f)&&(u=!a&&!s?"":""+e,h=""+n),f&&(e=u=u.toLowerCase(),n=h=h.toLowerCase()),r){case"*=":i=u.indexOf(h)>=0;break;case"$=":i=u.indexOf(h,u.length-h.length)>=0;break;case"^=":i=u.indexOf(h)===0;break;case"=":i=e===n;break;case">":p=!0,i=e>n;break;case">=":p=!0,i=e>=n;break;case"<":p=!0,i=e1&&arguments[1]!==void 0?arguments[1]:!0;return cB(this,t,e,ume)};o(hme,"addParent");l1.forEachUp=function(t){var e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0;return cB(this,t,e,hme)};o(Kje,"addParentAndChildren");l1.forEachUpAndDown=function(t){var e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0;return cB(this,t,e,Kje)};l1.ancestors=l1.parents;qx=fme={data:en.data({field:"data",bindingEvent:"data",allowBinding:!0,allowSetting:!0,settingEvent:"data",settingTriggersEvent:!0,triggerFnName:"trigger",allowGetting:!0,immutableKeys:{id:!0,source:!0,target:!0,parent:!0},updateStyle:!0}),removeData:en.removeData({field:"data",event:"data",triggerFnName:"trigger",triggerEvent:!0,immutableKeys:{id:!0,source:!0,target:!0,parent:!0},updateStyle:!0}),scratch:en.data({field:"scratch",bindingEvent:"scratch",allowBinding:!0,allowSetting:!0,settingEvent:"scratch",settingTriggersEvent:!0,triggerFnName:"trigger",allowGetting:!0,updateStyle:!0}),removeScratch:en.removeData({field:"scratch",event:"scratch",triggerFnName:"trigger",triggerEvent:!0,updateStyle:!0}),rscratch:en.data({field:"rscratch",allowBinding:!1,allowSetting:!0,settingTriggersEvent:!1,allowGetting:!0}),removeRscratch:en.removeData({field:"rscratch",triggerEvent:!1}),id:o(function(){var e=this[0];if(e)return e._private.data.id},"id")};qx.attr=qx.data;qx.removeAttr=qx.removeData;Qje=fme,U6={};o(CP,"defineDegreeFunction");Wt(U6,{degree:CP(function(t,e){return e.source().same(e.target())?2:1}),indegree:CP(function(t,e){return e.target().same(t)?1:0}),outdegree:CP(function(t,e){return e.source().same(t)?1:0})});o(Xg,"defineDegreeBoundsFunction");Wt(U6,{minDegree:Xg("degree",function(t,e){return te}),minIndegree:Xg("indegree",function(t,e){return te}),minOutdegree:Xg("outdegree",function(t,e){return te})});Wt(U6,{totalDegree:o(function(e){for(var r=0,n=this.nodes(),i=0;i0,p=d;d&&(f=f[0]);var m=p?f.position():{x:0,y:0};r!==void 0?h.position(e,r+m[e]):a!==void 0&&h.position({x:a.x+m.x,y:a.y+m.y})}else{var g=n.position(),y=l?n.parent():null,v=y&&y.length>0,x=v;v&&(y=y[0]);var b=x?y.position():{x:0,y:0};return a={x:g.x-b.x,y:g.y-b.y},e===void 0?a:a[e]}else if(!s)return;return this},"relativePosition")};Hl.modelPosition=Hl.point=Hl.position;Hl.modelPositions=Hl.points=Hl.positions;Hl.renderedPoint=Hl.renderedPosition;Hl.relativePoint=Hl.relativePosition;Zje=dme;i1=Rf={};Rf.renderedBoundingBox=function(t){var e=this.boundingBox(t),r=this.cy(),n=r.zoom(),i=r.pan(),a=e.x1*n+i.x,s=e.x2*n+i.x,l=e.y1*n+i.y,u=e.y2*n+i.y;return{x1:a,x2:s,y1:l,y2:u,w:s-a,h:u-l}};Rf.dirtyCompoundBoundsCache=function(){var t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:!1,e=this.cy();return!e.styleEnabled()||!e.hasCompoundNodes()?this:(this.forEachUp(function(r){if(r.isParent()){var n=r._private;n.compoundBoundsClean=!1,n.bbCache=null,t||r.emitAndNotify("bounds")}}),this)};Rf.updateCompoundBounds=function(){var t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:!1,e=this.cy();if(!e.styleEnabled()||!e.hasCompoundNodes())return this;if(!t&&e.batching())return this;function r(s){if(!s.isParent())return;var l=s._private,u=s.children(),h=s.pstyle("compound-sizing-wrt-labels").value==="include",f={width:{val:s.pstyle("min-width").pfValue,left:s.pstyle("min-width-bias-left"),right:s.pstyle("min-width-bias-right")},height:{val:s.pstyle("min-height").pfValue,top:s.pstyle("min-height-bias-top"),bottom:s.pstyle("min-height-bias-bottom")}},d=u.boundingBox({includeLabels:h,includeOverlays:!1,useCache:!1}),p=l.position;(d.w===0||d.h===0)&&(d={w:s.pstyle("width").pfValue,h:s.pstyle("height").pfValue},d.x1=p.x-d.w/2,d.x2=p.x+d.w/2,d.y1=p.y-d.h/2,d.y2=p.y+d.h/2);function m(L,M,N){var k=0,I=0,C=M+N;return L>0&&C>0&&(k=M/C*L,I=N/C*L),{biasDiff:k,biasComplementDiff:I}}o(m,"computeBiasValues");function g(L,M,N,k){if(N.units==="%")switch(k){case"width":return L>0?N.pfValue*L:0;case"height":return M>0?N.pfValue*M:0;case"average":return L>0&&M>0?N.pfValue*(L+M)/2:0;case"min":return L>0&&M>0?L>M?N.pfValue*M:N.pfValue*L:0;case"max":return L>0&&M>0?L>M?N.pfValue*L:N.pfValue*M:0;default:return 0}else return N.units==="px"?N.pfValue:0}o(g,"computePaddingValues");var y=f.width.left.value;f.width.left.units==="px"&&f.width.val>0&&(y=y*100/f.width.val);var v=f.width.right.value;f.width.right.units==="px"&&f.width.val>0&&(v=v*100/f.width.val);var x=f.height.top.value;f.height.top.units==="px"&&f.height.val>0&&(x=x*100/f.height.val);var b=f.height.bottom.value;f.height.bottom.units==="px"&&f.height.val>0&&(b=b*100/f.height.val);var w=m(f.width.val-d.w,y,v),S=w.biasDiff,T=w.biasComplementDiff,E=m(f.height.val-d.h,x,b),_=E.biasDiff,A=E.biasComplementDiff;l.autoPadding=g(d.w,d.h,s.pstyle("padding"),s.pstyle("padding-relative-to").value),l.autoWidth=Math.max(d.w,f.width.val),p.x=(-S+d.x1+d.x2+T)/2,l.autoHeight=Math.max(d.h,f.height.val),p.y=(-_+d.y1+d.y2+A)/2}o(r,"update");for(var n=0;ne.x2?i:e.x2,e.y1=ne.y2?a:e.y2,e.w=e.x2-e.x1,e.h=e.y2-e.y1)},"updateBounds"),F0=o(function(e,r){return r==null?e:Vl(e,r.x1,r.y1,r.x2,r.y2)},"updateBoundsFromBox"),Dx=o(function(e,r,n){return Ul(e,r,n)},"prefixedProperty"),l6=o(function(e,r,n){if(!r.cy().headless()){var i=r._private,a=i.rstyle,s=a.arrowWidth/2,l=r.pstyle(n+"-arrow-shape").value,u,h;if(l!=="none"){n==="source"?(u=a.srcX,h=a.srcY):n==="target"?(u=a.tgtX,h=a.tgtY):(u=a.midX,h=a.midY);var f=i.arrowBounds=i.arrowBounds||{},d=f[n]=f[n]||{};d.x1=u-s,d.y1=h-s,d.x2=u+s,d.y2=h+s,d.w=d.x2-d.x1,d.h=d.y2-d.y1,p6(d,1),Vl(e,d.x1,d.y1,d.x2,d.y2)}}},"updateBoundsFromArrow"),SP=o(function(e,r,n){if(!r.cy().headless()){var i;n?i=n+"-":i="";var a=r._private,s=a.rstyle,l=r.pstyle(i+"label").strValue;if(l){var u=r.pstyle("text-halign"),h=r.pstyle("text-valign"),f=Dx(s,"labelWidth",n),d=Dx(s,"labelHeight",n),p=Dx(s,"labelX",n),m=Dx(s,"labelY",n),g=r.pstyle(i+"text-margin-x").pfValue,y=r.pstyle(i+"text-margin-y").pfValue,v=r.isEdge(),x=r.pstyle(i+"text-rotation"),b=r.pstyle("text-outline-width").pfValue,w=r.pstyle("text-border-width").pfValue,S=w/2,T=r.pstyle("text-background-padding").pfValue,E=2,_=d,A=f,L=A/2,M=_/2,N,k,I,C;if(v)N=p-L,k=p+L,I=m-M,C=m+M;else{switch(u.value){case"left":N=p-A,k=p;break;case"center":N=p-L,k=p+L;break;case"right":N=p,k=p+A;break}switch(h.value){case"top":I=m-_,C=m;break;case"center":I=m-M,C=m+M;break;case"bottom":I=m,C=m+_;break}}N+=g-Math.max(b,S)-T-E,k+=g+Math.max(b,S)+T+E,I+=y-Math.max(b,S)-T-E,C+=y+Math.max(b,S)+T+E;var O=n||"main",D=a.labelBounds,P=D[O]=D[O]||{};P.x1=N,P.y1=I,P.x2=k,P.y2=C,P.w=k-N,P.h=C-I;var F=v&&x.strValue==="autorotate",B=x.pfValue!=null&&x.pfValue!==0;if(F||B){var $=F?Dx(a.rstyle,"labelAngle",n):x.pfValue,z=Math.cos($),Y=Math.sin($),Q=(N+k)/2,X=(I+C)/2;if(!v){switch(u.value){case"left":Q=k;break;case"right":Q=N;break}switch(h.value){case"top":X=C;break;case"bottom":X=I;break}}var ie=o(function(ce,ue){return ce=ce-Q,ue=ue-X,{x:ce*z-ue*Y+Q,y:ce*Y+ue*z+X}},"rotate"),j=ie(N,I),J=ie(N,C),Z=ie(k,I),H=ie(k,C);N=Math.min(j.x,J.x,Z.x,H.x),k=Math.max(j.x,J.x,Z.x,H.x),I=Math.min(j.y,J.y,Z.y,H.y),C=Math.max(j.y,J.y,Z.y,H.y)}var q=O+"Rot",K=D[q]=D[q]||{};K.x1=N,K.y1=I,K.x2=k,K.y2=C,K.w=k-N,K.h=C-I,Vl(e,N,I,k,C),Vl(a.labelBounds.all,N,I,k,C)}return e}},"updateBoundsFromLabel"),Jje=o(function(e,r){if(!r.cy().headless()){var n=r.pstyle("outline-opacity").value,i=r.pstyle("outline-width").value;if(n>0&&i>0){var a=r.pstyle("outline-offset").value,s=r.pstyle("shape").value,l=i+a,u=(e.w+l*2)/e.w,h=(e.h+l*2)/e.h,f=0,d=0;["diamond","pentagon","round-triangle"].includes(s)?(u=(e.w+l*2.4)/e.w,d=-l/3.6):["concave-hexagon","rhomboid","right-rhomboid"].includes(s)?u=(e.w+l*2.4)/e.w:s==="star"?(u=(e.w+l*2.8)/e.w,h=(e.h+l*2.6)/e.h,d=-l/3.8):s==="triangle"?(u=(e.w+l*2.8)/e.w,h=(e.h+l*2.4)/e.h,d=-l/1.4):s==="vee"&&(u=(e.w+l*4.4)/e.w,h=(e.h+l*3.8)/e.h,d=-l*.5);var p=e.h*h-e.h,m=e.w*u-e.w;if(m6(e,[Math.ceil(p/2),Math.ceil(m/2)]),f!=0||d!==0){var g=bWe(e,f,d);Vpe(e,g)}}}},"updateBoundsFromOutline"),eKe=o(function(e,r){var n=e._private.cy,i=n.styleEnabled(),a=n.headless(),s=Gs(),l=e._private,u=e.isNode(),h=e.isEdge(),f,d,p,m,g,y,v=l.rstyle,x=u&&i?e.pstyle("bounds-expansion").pfValue:[0],b=o(function(De){return De.pstyle("display").value!=="none"},"isDisplayed"),w=!i||b(e)&&(!h||b(e.source())&&b(e.target()));if(w){var S=0,T=0;i&&r.includeOverlays&&(S=e.pstyle("overlay-opacity").value,S!==0&&(T=e.pstyle("overlay-padding").value));var E=0,_=0;i&&r.includeUnderlays&&(E=e.pstyle("underlay-opacity").value,E!==0&&(_=e.pstyle("underlay-padding").value));var A=Math.max(T,_),L=0,M=0;if(i&&(L=e.pstyle("width").pfValue,M=L/2),u&&r.includeNodes){var N=e.position();g=N.x,y=N.y;var k=e.outerWidth(),I=k/2,C=e.outerHeight(),O=C/2;f=g-I,d=g+I,p=y-O,m=y+O,Vl(s,f,p,d,m),i&&r.includeOutlines&&Jje(s,e)}else if(h&&r.includeEdges)if(i&&!a){var D=e.pstyle("curve-style").strValue;if(f=Math.min(v.srcX,v.midX,v.tgtX),d=Math.max(v.srcX,v.midX,v.tgtX),p=Math.min(v.srcY,v.midY,v.tgtY),m=Math.max(v.srcY,v.midY,v.tgtY),f-=M,d+=M,p-=M,m+=M,Vl(s,f,p,d,m),D==="haystack"){var P=v.haystackPts;if(P&&P.length===2){if(f=P[0].x,p=P[0].y,d=P[1].x,m=P[1].y,f>d){var F=f;f=d,d=F}if(p>m){var B=p;p=m,m=B}Vl(s,f-M,p-M,d+M,m+M)}}else if(D==="bezier"||D==="unbundled-bezier"||D.endsWith("segments")||D.endsWith("taxi")){var $;switch(D){case"bezier":case"unbundled-bezier":$=v.bezierPts;break;case"segments":case"taxi":case"round-segments":case"round-taxi":$=v.linePts;break}if($!=null)for(var z=0;z<$.length;z++){var Y=$[z];f=Y.x-M,d=Y.x+M,p=Y.y-M,m=Y.y+M,Vl(s,f,p,d,m)}}}else{var Q=e.source(),X=Q.position(),ie=e.target(),j=ie.position();if(f=X.x,d=j.x,p=X.y,m=j.y,f>d){var J=f;f=d,d=J}if(p>m){var Z=p;p=m,m=Z}f-=M,d+=M,p-=M,m+=M,Vl(s,f,p,d,m)}if(i&&r.includeEdges&&h&&(l6(s,e,"mid-source"),l6(s,e,"mid-target"),l6(s,e,"source"),l6(s,e,"target")),i){var H=e.pstyle("ghost").value==="yes";if(H){var q=e.pstyle("ghost-offset-x").pfValue,K=e.pstyle("ghost-offset-y").pfValue;Vl(s,s.x1+q,s.y1+K,s.x2+q,s.y2+K)}}var se=l.bodyBounds=l.bodyBounds||{};g0e(se,s),m6(se,x),p6(se,1),i&&(f=s.x1,d=s.x2,p=s.y1,m=s.y2,Vl(s,f-A,p-A,d+A,m+A));var ce=l.overlayBounds=l.overlayBounds||{};g0e(ce,s),m6(ce,x),p6(ce,1);var ue=l.labelBounds=l.labelBounds||{};ue.all!=null?xWe(ue.all):ue.all=Gs(),i&&r.includeLabels&&(r.includeMainLabels&&SP(s,e,null),h&&(r.includeSourceLabels&&SP(s,e,"source"),r.includeTargetLabels&&SP(s,e,"target")))}return s.x1=el(s.x1),s.y1=el(s.y1),s.x2=el(s.x2),s.y2=el(s.y2),s.w=el(s.x2-s.x1),s.h=el(s.y2-s.y1),s.w>0&&s.h>0&&w&&(m6(s,x),p6(s,1)),s},"boundingBoxImpl"),mme=o(function(e){var r=0,n=o(function(s){return(s?1:0)<=0;l--)s(l);return this};Df.removeAllListeners=function(){return this.removeListener("*")};Df.emit=Df.trigger=function(t,e,r){var n=this.listeners,i=n.length;return this.emitting++,vn(e)||(e=[e]),gKe(this,function(a,s){r!=null&&(n=[{event:s.event,type:s.type,namespace:s.namespace,callback:r}],i=n.length);for(var l=o(function(f){var d=n[f];if(d.type===s.type&&(!d.namespace||d.namespace===s.namespace||d.namespace===pKe)&&a.eventMatches(a.context,d,s)){var p=[s];e!=null&&qYe(p,e),a.beforeEmit(a.context,d,s),d.conf&&d.conf.one&&(a.listeners=a.listeners.filter(function(y){return y!==d}));var m=a.callbackContext(a.context,d,s),g=d.callback.apply(m,p);a.afterEmit(a.context,d,s),g===!1&&(s.stopPropagation(),s.preventDefault())}},"_loop2"),u=0;u1&&!s){var l=this.length-1,u=this[l],h=u._private.data.id;this[l]=void 0,this[e]=u,a.set(h,{ele:u,index:e})}return this.length--,this},"unmergeAt"),unmergeOne:o(function(e){e=e[0];var r=this._private,n=e._private.data.id,i=r.map,a=i.get(n);if(!a)return this;var s=a.index;return this.unmergeAt(s),this},"unmergeOne"),unmerge:o(function(e){var r=this._private.cy;if(!e)return this;if(e&&zt(e)){var n=e;e=r.mutableElements().filter(n)}for(var i=0;i=0;r--){var n=this[r];e(n)&&this.unmergeAt(r)}return this},"unmergeBy"),map:o(function(e,r){for(var n=[],i=this,a=0;an&&(n=u,i=l)}return{value:n,ele:i}},"max"),min:o(function(e,r){for(var n=1/0,i,a=this,s=0;s=0&&a"u"?"undefined":Hi(Symbol))!=e&&Hi(Symbol.iterator)!=e;r&&(L6[Symbol.iterator]=function(){var n=this,i={value:void 0,done:!1},a=0,s=this.length;return bpe({next:o(function(){return a1&&arguments[1]!==void 0?arguments[1]:!0,n=this[0],i=n.cy();if(i.styleEnabled()&&n){this.cleanStyle();var a=n._private.style[e];return a??(r?i.style().getDefaultProperty(e):null)}},"parsedStyle"),numericStyle:o(function(e){var r=this[0];if(r.cy().styleEnabled()&&r){var n=r.pstyle(e);return n.pfValue!==void 0?n.pfValue:n.value}},"numericStyle"),numericStyleUnits:o(function(e){var r=this[0];if(r.cy().styleEnabled()&&r)return r.pstyle(e).units},"numericStyleUnits"),renderedStyle:o(function(e){var r=this.cy();if(!r.styleEnabled())return this;var n=this[0];if(n)return r.style().getRenderedStyle(n,e)},"renderedStyle"),style:o(function(e,r){var n=this.cy();if(!n.styleEnabled())return this;var i=!1,a=n.style();if(Mr(e)){var s=e;a.applyBypass(this,s,i),this.emitAndNotify("style")}else if(zt(e))if(r===void 0){var l=this[0];return l?a.getStylePropertyValue(l,e):void 0}else a.applyBypass(this,e,r,i),this.emitAndNotify("style");else if(e===void 0){var u=this[0];return u?a.getRawStyle(u):void 0}return this},"style"),removeStyle:o(function(e){var r=this.cy();if(!r.styleEnabled())return this;var n=!1,i=r.style(),a=this;if(e===void 0)for(var s=0;s0&&e.push(f[0]),e.push(l[0])}return this.spawn(e,!0).filter(t)},"neighborhood"),closedNeighborhood:o(function(e){return this.neighborhood().add(this).filter(e)},"closedNeighborhood"),openNeighborhood:o(function(e){return this.neighborhood(e)},"openNeighborhood")});Fa.neighbourhood=Fa.neighborhood;Fa.closedNeighbourhood=Fa.closedNeighborhood;Fa.openNeighbourhood=Fa.openNeighborhood;Wt(Fa,{source:tl(o(function(e){var r=this[0],n;return r&&(n=r._private.source||r.cy().collection()),n&&e?n.filter(e):n},"sourceImpl"),"source"),target:tl(o(function(e){var r=this[0],n;return r&&(n=r._private.target||r.cy().collection()),n&&e?n.filter(e):n},"targetImpl"),"target"),sources:X0e({attr:"source"}),targets:X0e({attr:"target"})});o(X0e,"defineSourceFunction");Wt(Fa,{edgesWith:tl(j0e(),"edgesWith"),edgesTo:tl(j0e({thisIsSrc:!0}),"edgesTo")});o(j0e,"defineEdgesWithFunction");Wt(Fa,{connectedEdges:tl(function(t){for(var e=[],r=this,n=0;n0);return s},"components"),component:o(function(){var e=this[0];return e.cy().mutableElements().components(e)[0]},"component")});Fa.componentsOf=Fa.components;Ca=o(function(e,r){var n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!1,i=arguments.length>3&&arguments[3]!==void 0?arguments[3]:!1;if(e===void 0){oi("A collection must have a reference to the core");return}var a=new Vc,s=!1;if(!r)r=[];else if(r.length>0&&Mr(r[0])&&!Zx(r[0])){s=!0;for(var l=[],u=new c1,h=0,f=r.length;h0&&arguments[0]!==void 0?arguments[0]:!0,e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0,r=this,n=r.cy(),i=n._private,a=[],s=[],l,u=0,h=r.length;u0){for(var B=l.length===r.length?r:new Ca(n,l),$=0;$0&&arguments[0]!==void 0?arguments[0]:!0,e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0,r=this,n=[],i={},a=r._private.cy;function s(C){for(var O=C._private.edges,D=0;D0&&(t?N.emitAndNotify("remove"):e&&N.emit("remove"));for(var k=0;kf&&Math.abs(g.v)>f;);return p?function(y){return u[y*(u.length-1)|0]}:h},"springRK4Factory")}(),Ln=o(function(e,r,n,i){var a=SKe(e,r,n,i);return function(s,l,u){return s+(l-s)*a(u)}},"cubicBezier"),v6={linear:o(function(e,r,n){return e+(r-e)*n},"linear"),ease:Ln(.25,.1,.25,1),"ease-in":Ln(.42,0,1,1),"ease-out":Ln(0,0,.58,1),"ease-in-out":Ln(.42,0,.58,1),"ease-in-sine":Ln(.47,0,.745,.715),"ease-out-sine":Ln(.39,.575,.565,1),"ease-in-out-sine":Ln(.445,.05,.55,.95),"ease-in-quad":Ln(.55,.085,.68,.53),"ease-out-quad":Ln(.25,.46,.45,.94),"ease-in-out-quad":Ln(.455,.03,.515,.955),"ease-in-cubic":Ln(.55,.055,.675,.19),"ease-out-cubic":Ln(.215,.61,.355,1),"ease-in-out-cubic":Ln(.645,.045,.355,1),"ease-in-quart":Ln(.895,.03,.685,.22),"ease-out-quart":Ln(.165,.84,.44,1),"ease-in-out-quart":Ln(.77,0,.175,1),"ease-in-quint":Ln(.755,.05,.855,.06),"ease-out-quint":Ln(.23,1,.32,1),"ease-in-out-quint":Ln(.86,0,.07,1),"ease-in-expo":Ln(.95,.05,.795,.035),"ease-out-expo":Ln(.19,1,.22,1),"ease-in-out-expo":Ln(1,0,0,1),"ease-in-circ":Ln(.6,.04,.98,.335),"ease-out-circ":Ln(.075,.82,.165,1),"ease-in-out-circ":Ln(.785,.135,.15,.86),spring:o(function(e,r,n){if(n===0)return v6.linear;var i=AKe(e,r,n);return function(a,s,l){return a+(s-a)*i(l)}},"spring"),"cubic-bezier":Ln};o(Q0e,"getEasedValue");o(Z0e,"getValue");o(jg,"ease");o(_Ke,"step$1");o(Nx,"valid");o(LKe,"startAnimation");o(J0e,"stepAll");DKe={animate:en.animate(),animation:en.animation(),animated:en.animated(),clearQueue:en.clearQueue(),delay:en.delay(),delayAnimation:en.delayAnimation(),stop:en.stop(),addToAnimationPool:o(function(e){var r=this;r.styleEnabled()&&r._private.aniEles.merge(e)},"addToAnimationPool"),stopAnimationLoop:o(function(){this._private.animationsRunning=!1},"stopAnimationLoop"),startAnimationLoop:o(function(){var e=this;if(e._private.animationsRunning=!0,!e.styleEnabled())return;function r(){e._private.animationsRunning&&E6(o(function(a){J0e(a,e),r()},"animationStep"))}o(r,"headlessStep");var n=e.renderer();n&&n.beforeRender?n.beforeRender(o(function(a,s){J0e(s,e)},"rendererAnimationStep"),n.beforeRenderPriorities.animations):r()},"startAnimationLoop")},RKe={qualifierCompare:o(function(e,r){return e==null||r==null?e==null&&r==null:e.sameText(r)},"qualifierCompare"),eventMatches:o(function(e,r,n){var i=r.qualifier;return i!=null?e!==n.target&&Zx(n.target)&&i.matches(n.target):!0},"eventMatches"),addEventFields:o(function(e,r){r.cy=e,r.target=e},"addEventFields"),callbackContext:o(function(e,r,n){return r.qualifier!=null?n.target:e},"callbackContext")},h6=o(function(e){return zt(e)?new _f(e):e},"argSelector"),Sme={createEmitter:o(function(){var e=this._private;return e.emitter||(e.emitter=new H6(RKe,this)),this},"createEmitter"),emitter:o(function(){return this._private.emitter},"emitter"),on:o(function(e,r,n){return this.emitter().on(e,h6(r),n),this},"on"),removeListener:o(function(e,r,n){return this.emitter().removeListener(e,h6(r),n),this},"removeListener"),removeAllListeners:o(function(){return this.emitter().removeAllListeners(),this},"removeAllListeners"),one:o(function(e,r,n){return this.emitter().one(e,h6(r),n),this},"one"),once:o(function(e,r,n){return this.emitter().one(e,h6(r),n),this},"once"),emit:o(function(e,r){return this.emitter().emit(e,r),this},"emit"),emitAndNotify:o(function(e,r){return this.emit(e),this.notify(e,r),this},"emitAndNotify")};en.eventAliasesOn(Sme);FP={png:o(function(e){var r=this._private.renderer;return e=e||{},r.png(e)},"png"),jpg:o(function(e){var r=this._private.renderer;return e=e||{},e.bg=e.bg||"#fff",r.jpg(e)},"jpg")};FP.jpeg=FP.jpg;x6={layout:o(function(e){var r=this;if(e==null){oi("Layout options must be specified to make a layout");return}if(e.name==null){oi("A `name` must be specified to make a layout");return}var n=e.name,i=r.extension("layout",n);if(i==null){oi("No such layout `"+n+"` found. Did you forget to import it and `cytoscape.use()` it?");return}var a;zt(e.eles)?a=r.$(e.eles):a=e.eles!=null?e.eles:r.$();var s=new i(Wt({},e,{cy:r,eles:a}));return s},"layout")};x6.createLayout=x6.makeLayout=x6.layout;NKe={notify:o(function(e,r){var n=this._private;if(this.batching()){n.batchNotifications=n.batchNotifications||{};var i=n.batchNotifications[e]=n.batchNotifications[e]||this.collection();r!=null&&i.merge(r);return}if(n.notificationsEnabled){var a=this.renderer();this.destroyed()||!a||a.notify(e,r)}},"notify"),notifications:o(function(e){var r=this._private;return e===void 0?r.notificationsEnabled:(r.notificationsEnabled=!!e,this)},"notifications"),noNotifications:o(function(e){this.notifications(!1),e(),this.notifications(!0)},"noNotifications"),batching:o(function(){return this._private.batchCount>0},"batching"),startBatch:o(function(){var e=this._private;return e.batchCount==null&&(e.batchCount=0),e.batchCount===0&&(e.batchStyleEles=this.collection(),e.batchNotifications={}),e.batchCount++,this},"startBatch"),endBatch:o(function(){var e=this._private;if(e.batchCount===0)return this;if(e.batchCount--,e.batchCount===0){e.batchStyleEles.updateStyle();var r=this.renderer();Object.keys(e.batchNotifications).forEach(function(n){var i=e.batchNotifications[n];i.empty()?r.notify(n):r.notify(n,i)})}return this},"endBatch"),batch:o(function(e){return this.startBatch(),e(),this.endBatch(),this},"batch"),batchData:o(function(e){var r=this;return this.batch(function(){for(var n=Object.keys(e),i=0;i0;)r.removeChild(r.childNodes[0]);e._private.renderer=null,e.mutableElements().forEach(function(n){var i=n._private;i.rscratch={},i.rstyle={},i.animation.current=[],i.animation.queue=[]})},"destroyRenderer"),onRender:o(function(e){return this.on("render",e)},"onRender"),offRender:o(function(e){return this.off("render",e)},"offRender")};zP.invalidateDimensions=zP.resize;b6={collection:o(function(e,r){return zt(e)?this.$(e):xo(e)?e.collection():vn(e)?(r||(r={}),new Ca(this,e,r.unique,r.removed)):new Ca(this)},"collection"),nodes:o(function(e){var r=this.$(function(n){return n.isNode()});return e?r.filter(e):r},"nodes"),edges:o(function(e){var r=this.$(function(n){return n.isEdge()});return e?r.filter(e):r},"edges"),$:o(function(e){var r=this._private.elements;return e?r.filter(e):r.spawnSelf()},"$"),mutableElements:o(function(){return this._private.elements},"mutableElements")};b6.elements=b6.filter=b6.$;Ga={},Fx="t",IKe="f";Ga.apply=function(t){for(var e=this,r=e._private,n=r.cy,i=n.collection(),a=0;a0;if(p||d&&m){var g=void 0;p&&m||p?g=h.properties:m&&(g=h.mappedProperties);for(var y=0;y1&&(S=1),l.color){var E=n.valueMin[0],_=n.valueMax[0],A=n.valueMin[1],L=n.valueMax[1],M=n.valueMin[2],N=n.valueMax[2],k=n.valueMin[3]==null?1:n.valueMin[3],I=n.valueMax[3]==null?1:n.valueMax[3],C=[Math.round(E+(_-E)*S),Math.round(A+(L-A)*S),Math.round(M+(N-M)*S),Math.round(k+(I-k)*S)];a={bypass:n.bypass,name:n.name,value:C,strValue:"rgb("+C[0]+", "+C[1]+", "+C[2]+")"}}else if(l.number){var O=n.valueMin+(n.valueMax-n.valueMin)*S;a=this.parse(n.name,O,n.bypass,p)}else return!1;if(!a)return y(),!1;a.mapping=n,n=a;break}case s.data:{for(var D=n.field.split("."),P=d.data,F=0;F0&&a>0){for(var l={},u=!1,h=0;h0?t.delayAnimation(s).play().promise().then(w):w()}).then(function(){return t.animation({style:l,duration:a,easing:t.pstyle("transition-timing-function").value,queue:!1}).play().promise()}).then(function(){r.removeBypasses(t,i),t.emitAndNotify("style"),n.transitioning=!1})}else n.transitioning&&(this.removeBypasses(t,i),t.emitAndNotify("style"),n.transitioning=!1)};Ga.checkTrigger=function(t,e,r,n,i,a){var s=this.properties[e],l=i(s);l!=null&&l(r,n)&&a(s)};Ga.checkZOrderTrigger=function(t,e,r,n){var i=this;this.checkTrigger(t,e,r,n,function(a){return a.triggersZOrder},function(){i._private.cy.notify("zorder",t)})};Ga.checkBoundsTrigger=function(t,e,r,n){this.checkTrigger(t,e,r,n,function(i){return i.triggersBounds},function(i){t.dirtyCompoundBoundsCache(),t.dirtyBoundingBoxCache(),i.triggersBoundsOfParallelBeziers&&e==="curve-style"&&(r==="bezier"||n==="bezier")&&t.parallelEdges().forEach(function(a){a.isBundledBezier()&&a.dirtyBoundingBoxCache()}),i.triggersBoundsOfConnectedEdges&&e==="display"&&(r==="none"||n==="none")&&t.connectedEdges().forEach(function(a){a.dirtyBoundingBoxCache()})})};Ga.checkTriggers=function(t,e,r,n){t.dirtyStyleCache(),this.checkZOrderTrigger(t,e,r,n),this.checkBoundsTrigger(t,e,r,n)};rb={};rb.applyBypass=function(t,e,r,n){var i=this,a=[],s=!0;if(e==="*"||e==="**"){if(r!==void 0)for(var l=0;li.length?n=n.substr(i.length):n=""}o(l,"removeSelAndBlockFromRemaining");function u(){a.length>s.length?a=a.substr(s.length):a=""}for(o(u,"removePropAndValFromRem");;){var h=n.match(/^\s*$/);if(h)break;var f=n.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);if(!f){tn("Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: "+n);break}i=f[0];var d=f[1];if(d!=="core"){var p=new _f(d);if(p.invalid){tn("Skipping parsing of block: Invalid selector found in string stylesheet: "+d),l();continue}}var m=f[2],g=!1;a=m;for(var y=[];;){var v=a.match(/^\s*$/);if(v)break;var x=a.match(/^\s*(.+?)\s*:\s*(.+?)(?:\s*;|\s*$)/);if(!x){tn("Skipping parsing of block: Invalid formatting of style property and value definitions found in:"+m),g=!0;break}s=x[0];var b=x[1],w=x[2],S=e.properties[b];if(!S){tn("Skipping property: Invalid property name in: "+s),u();continue}var T=r.parse(b,w);if(!T){tn("Skipping property: Invalid property definition in: "+s),u();continue}y.push({name:b,val:w}),u()}if(g){l();break}r.selector(d);for(var E=0;E=7&&e[0]==="d"&&(f=new RegExp(l.data.regex).exec(e))){if(r)return!1;var p=l.data;return{name:t,value:f,strValue:""+e,mapped:p,field:f[1],bypass:r}}else if(e.length>=10&&e[0]==="m"&&(d=new RegExp(l.mapData.regex).exec(e))){if(r||h.multiple)return!1;var m=l.mapData;if(!(h.color||h.number))return!1;var g=this.parse(t,d[4]);if(!g||g.mapped)return!1;var y=this.parse(t,d[5]);if(!y||y.mapped)return!1;if(g.pfValue===y.pfValue||g.strValue===y.strValue)return tn("`"+t+": "+e+"` is not a valid mapper because the output range is zero; converting to `"+t+": "+g.strValue+"`"),this.parse(t,g.strValue);if(h.color){var v=g.value,x=y.value,b=v[0]===x[0]&&v[1]===x[1]&&v[2]===x[2]&&(v[3]===x[3]||(v[3]==null||v[3]===1)&&(x[3]==null||x[3]===1));if(b)return!1}return{name:t,value:d,strValue:""+e,mapped:m,field:d[1],fieldMin:parseFloat(d[2]),fieldMax:parseFloat(d[3]),valueMin:g.value,valueMax:y.value,bypass:r}}}if(h.multiple&&n!=="multiple"){var w;if(u?w=e.split(/\s+/):vn(e)?w=e:w=[e],h.evenMultiple&&w.length%2!==0)return null;for(var S=[],T=[],E=[],_="",A=!1,L=0;L0?" ":"")+M.strValue}return h.validate&&!h.validate(S,T)?null:h.singleEnum&&A?S.length===1&&zt(S[0])?{name:t,value:S[0],strValue:S[0],bypass:r}:null:{name:t,value:S,pfValue:E,strValue:_,bypass:r,units:T}}var N=o(function(){for(var H=0;Hh.max||h.strictMax&&e===h.max))return null;var D={name:t,value:e,strValue:""+e+(k||""),units:k,bypass:r};return h.unitless||k!=="px"&&k!=="em"?D.pfValue=e:D.pfValue=k==="px"||!k?e:this.getEmSizeInPixels()*e,(k==="ms"||k==="s")&&(D.pfValue=k==="ms"?e:1e3*e),(k==="deg"||k==="rad")&&(D.pfValue=k==="rad"?e:mWe(e)),k==="%"&&(D.pfValue=e/100),D}else if(h.propList){var P=[],F=""+e;if(F!=="none"){for(var B=F.split(/\s*,\s*|\s+/),$=0;$0&&l>0&&!isNaN(n.w)&&!isNaN(n.h)&&n.w>0&&n.h>0){u=Math.min((s-2*r)/n.w,(l-2*r)/n.h),u=u>this._private.maxZoom?this._private.maxZoom:u,u=u=n.minZoom&&(n.maxZoom=r),this},"zoomRange"),minZoom:o(function(e){return e===void 0?this._private.minZoom:this.zoomRange({min:e})},"minZoom"),maxZoom:o(function(e){return e===void 0?this._private.maxZoom:this.zoomRange({max:e})},"maxZoom"),getZoomedViewport:o(function(e){var r=this._private,n=r.pan,i=r.zoom,a,s,l=!1;if(r.zoomingEnabled||(l=!0),ft(e)?s=e:Mr(e)&&(s=e.level,e.position!=null?a=F6(e.position,i,n):e.renderedPosition!=null&&(a=e.renderedPosition),a!=null&&!r.panningEnabled&&(l=!0)),s=s>r.maxZoom?r.maxZoom:s,s=sr.maxZoom||!r.zoomingEnabled?s=!0:(r.zoom=u,a.push("zoom"))}if(i&&(!s||!e.cancelOnFailedZoom)&&r.panningEnabled){var h=e.pan;ft(h.x)&&(r.pan.x=h.x,l=!1),ft(h.y)&&(r.pan.y=h.y,l=!1),l||a.push("pan")}return a.length>0&&(a.push("viewport"),this.emit(a.join(" ")),this.notify("viewport")),this},"viewport"),center:o(function(e){var r=this.getCenterPan(e);return r&&(this._private.pan=r,this.emit("pan viewport"),this.notify("viewport")),this},"center"),getCenterPan:o(function(e,r){if(this._private.panningEnabled){if(zt(e)){var n=e;e=this.mutableElements().filter(n)}else xo(e)||(e=this.mutableElements());if(e.length!==0){var i=e.boundingBox(),a=this.width(),s=this.height();r=r===void 0?this._private.zoom:r;var l={x:(a-r*(i.x1+i.x2))/2,y:(s-r*(i.y1+i.y2))/2};return l}}},"getCenterPan"),reset:o(function(){return!this._private.panningEnabled||!this._private.zoomingEnabled?this:(this.viewport({pan:{x:0,y:0},zoom:1}),this)},"reset"),invalidateSize:o(function(){this._private.sizeCache=null},"invalidateSize"),size:o(function(){var e=this._private,r=e.container,n=this;return e.sizeCache=e.sizeCache||(r?function(){var i=n.window().getComputedStyle(r),a=o(function(l){return parseFloat(i.getPropertyValue(l))},"val");return{width:r.clientWidth-a("padding-left")-a("padding-right"),height:r.clientHeight-a("padding-top")-a("padding-bottom")}}():{width:1,height:1})},"size"),width:o(function(){return this.size().width},"width"),height:o(function(){return this.size().height},"height"),extent:o(function(){var e=this._private.pan,r=this._private.zoom,n=this.renderedExtent(),i={x1:(n.x1-e.x)/r,x2:(n.x2-e.x)/r,y1:(n.y1-e.y)/r,y2:(n.y2-e.y)/r};return i.w=i.x2-i.x1,i.h=i.y2-i.y1,i},"extent"),renderedExtent:o(function(){var e=this.width(),r=this.height();return{x1:0,y1:0,x2:e,y2:r,w:e,h:r}},"renderedExtent"),multiClickDebounceTime:o(function(e){if(e)this._private.multiClickDebounceTime=e;else return this._private.multiClickDebounceTime;return this},"multiClickDebounceTime")};q0.centre=q0.center;q0.autolockNodes=q0.autolock;q0.autoungrabifyNodes=q0.autoungrabify;jx={data:en.data({field:"data",bindingEvent:"data",allowBinding:!0,allowSetting:!0,settingEvent:"data",settingTriggersEvent:!0,triggerFnName:"trigger",allowGetting:!0,updateStyle:!0}),removeData:en.removeData({field:"data",event:"data",triggerFnName:"trigger",triggerEvent:!0,updateStyle:!0}),scratch:en.data({field:"scratch",bindingEvent:"scratch",allowBinding:!0,allowSetting:!0,settingEvent:"scratch",settingTriggersEvent:!0,triggerFnName:"trigger",allowGetting:!0,updateStyle:!0}),removeScratch:en.removeData({field:"scratch",event:"scratch",triggerFnName:"trigger",triggerEvent:!0,updateStyle:!0})};jx.attr=jx.data;jx.removeAttr=jx.removeData;Kx=o(function(e){var r=this;e=Wt({},e);var n=e.container;n&&!k6(n)&&k6(n[0])&&(n=n[0]);var i=n?n._cyreg:null;i=i||{},i&&i.cy&&(i.cy.destroy(),i={});var a=i.readies=i.readies||[];n&&(n._cyreg=i),i.cy=r;var s=Vi!==void 0&&n!==void 0&&!e.headless,l=e;l.layout=Wt({name:s?"grid":"null"},l.layout),l.renderer=Wt({name:s?"canvas":"null"},l.renderer);var u=o(function(g,y,v){return y!==void 0?y:v!==void 0?v:g},"defVal"),h=this._private={container:n,ready:!1,options:l,elements:new Ca(this),listeners:[],aniEles:new Ca(this),data:l.data||{},scratch:{},layout:null,renderer:null,destroyed:!1,notificationsEnabled:!0,minZoom:1e-50,maxZoom:1e50,zoomingEnabled:u(!0,l.zoomingEnabled),userZoomingEnabled:u(!0,l.userZoomingEnabled),panningEnabled:u(!0,l.panningEnabled),userPanningEnabled:u(!0,l.userPanningEnabled),boxSelectionEnabled:u(!0,l.boxSelectionEnabled),autolock:u(!1,l.autolock,l.autolockNodes),autoungrabify:u(!1,l.autoungrabify,l.autoungrabifyNodes),autounselectify:u(!1,l.autounselectify),styleEnabled:l.styleEnabled===void 0?s:l.styleEnabled,zoom:ft(l.zoom)?l.zoom:1,pan:{x:Mr(l.pan)&&ft(l.pan.x)?l.pan.x:0,y:Mr(l.pan)&&ft(l.pan.y)?l.pan.y:0},animation:{current:[],queue:[]},hasCompoundNodes:!1,multiClickDebounceTime:u(250,l.multiClickDebounceTime)};this.createEmitter(),this.selectionType(l.selectionType),this.zoomRange({min:l.minZoom,max:l.maxZoom});var f=o(function(g,y){var v=g.some(zHe);if(v)return u1.all(g).then(y);y(g)},"loadExtData");h.styleEnabled&&r.setStyle([]);var d=Wt({},l,l.renderer);r.initRenderer(d);var p=o(function(g,y,v){r.notifications(!1);var x=r.mutableElements();x.length>0&&x.remove(),g!=null&&(Mr(g)||vn(g))&&r.add(g),r.one("layoutready",function(w){r.notifications(!0),r.emit(w),r.one("load",y),r.emitAndNotify("load")}).one("layoutstop",function(){r.one("done",v),r.emit("done")});var b=Wt({},r._private.options.layout);b.eles=r.elements(),r.layout(b).run()},"setElesAndLayout");f([l.style,l.elements],function(m){var g=m[0],y=m[1];h.styleEnabled&&r.style().append(g),p(y,function(){r.startAnimationLoop(),h.ready=!0,jn(l.ready)&&r.on("ready",l.ready);for(var v=0;v0,u=Gs(e.boundingBox?e.boundingBox:{x1:0,y1:0,w:r.width(),h:r.height()}),h;if(xo(e.roots))h=e.roots;else if(vn(e.roots)){for(var f=[],d=0;d0;){var O=C(),D=M(O,k);if(D)O.outgoers().filter(function(ue){return ue.isNode()&&n.has(ue)}).forEach(I);else if(D===null){tn("Detected double maximal shift for node `"+O.id()+"`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.");break}}}L();var P=0;if(e.avoidOverlap)for(var F=0;F0&&x[0].length<=3?Pe/2:0),W=2*Math.PI/x[oe].length*ke;return oe===0&&x[0].length===1&&(me=1),{x:K.x+me*Math.cos(W),y:K.y+me*Math.sin(W)}}else{var _e={x:K.x+(ke+1-(Ie+1)/2)*Se,y:(oe+1)*Ue};return _e}},"getPosition");return n.nodes().layoutPositions(this,e,ce),this};zKe={fit:!0,padding:30,boundingBox:void 0,avoidOverlap:!0,nodeDimensionsIncludeLabels:!1,spacingFactor:void 0,radius:void 0,startAngle:3/2*Math.PI,sweep:void 0,clockwise:!0,sort:void 0,animate:!1,animationDuration:500,animationEasing:void 0,animateFilter:o(function(e,r){return!0},"animateFilter"),ready:void 0,stop:void 0,transform:o(function(e,r){return r},"transform")};o(_me,"CircleLayout");_me.prototype.run=function(){var t=this.options,e=t,r=t.cy,n=e.eles,i=e.counterclockwise!==void 0?!e.counterclockwise:e.clockwise,a=n.nodes().not(":parent");e.sort&&(a=a.sort(e.sort));for(var s=Gs(e.boundingBox?e.boundingBox:{x1:0,y1:0,w:r.width(),h:r.height()}),l={x:s.x1+s.w/2,y:s.y1+s.h/2},u=e.sweep===void 0?2*Math.PI-2*Math.PI/a.length:e.sweep,h=u/Math.max(1,a.length-1),f,d=0,p=0;p1&&e.avoidOverlap){d*=1.75;var x=Math.cos(h)-Math.cos(0),b=Math.sin(h)-Math.sin(0),w=Math.sqrt(d*d/(x*x+b*b));f=Math.max(w,f)}var S=o(function(E,_){var A=e.startAngle+_*h*(i?1:-1),L=f*Math.cos(A),M=f*Math.sin(A),N={x:l.x+L,y:l.y+M};return N},"getPos");return n.nodes().layoutPositions(this,e,S),this};GKe={fit:!0,padding:30,startAngle:3/2*Math.PI,sweep:void 0,clockwise:!0,equidistant:!1,minNodeSpacing:10,boundingBox:void 0,avoidOverlap:!0,nodeDimensionsIncludeLabels:!1,height:void 0,width:void 0,spacingFactor:void 0,concentric:o(function(e){return e.degree()},"concentric"),levelWidth:o(function(e){return e.maxDegree()/4},"levelWidth"),animate:!1,animationDuration:500,animationEasing:void 0,animateFilter:o(function(e,r){return!0},"animateFilter"),ready:void 0,stop:void 0,transform:o(function(e,r){return r},"transform")};o(Lme,"ConcentricLayout");Lme.prototype.run=function(){for(var t=this.options,e=t,r=e.counterclockwise!==void 0?!e.counterclockwise:e.clockwise,n=t.cy,i=e.eles,a=i.nodes().not(":parent"),s=Gs(e.boundingBox?e.boundingBox:{x1:0,y1:0,w:n.width(),h:n.height()}),l={x:s.x1+s.w/2,y:s.y1+s.h/2},u=[],h=0,f=0;f0){var T=Math.abs(b[0].value-S.value);T>=v&&(b=[],x.push(b))}b.push(S)}var E=h+e.minNodeSpacing;if(!e.avoidOverlap){var _=x.length>0&&x[0].length>1,A=Math.min(s.w,s.h)/2-E,L=A/(x.length+_?1:0);E=Math.min(E,L)}for(var M=0,N=0;N1&&e.avoidOverlap){var O=Math.cos(C)-Math.cos(0),D=Math.sin(C)-Math.sin(0),P=Math.sqrt(E*E/(O*O+D*D));M=Math.max(P,M)}k.r=M,M+=E}if(e.equidistant){for(var F=0,B=0,$=0;$=t.numIter||(XKe(n,t),n.temperature=n.temperature*t.coolingFactor,n.temperature=t.animationThreshold&&a(),E6(d)}},"frame");f()}else{for(;h;)h=s(u),u++;rpe(n,t),l()}return this};j6.prototype.stop=function(){return this.stopped=!0,this.thread&&this.thread.stop(),this.emit("layoutstop"),this};j6.prototype.destroy=function(){return this.thread&&this.thread.stop(),this};VKe=o(function(e,r,n){for(var i=n.eles.edges(),a=n.eles.nodes(),s=Gs(n.boundingBox?n.boundingBox:{x1:0,y1:0,w:e.width(),h:e.height()}),l={isCompound:e.hasCompoundNodes(),layoutNodes:[],idToIndex:{},nodeSize:a.size(),graphSet:[],indexToGraph:[],layoutEdges:[],edgeSize:i.size(),temperature:n.initialTemp,clientWidth:s.w,clientHeight:s.h,boundingBox:s},u=n.eles.components(),h={},f=0;f0){l.graphSet.push(A);for(var f=0;fi.count?0:i.graph},"findLCA"),HKe=o(function t(e,r,n,i){var a=i.graphSet[n];if(-10)var d=i.nodeOverlap*f,p=Math.sqrt(l*l+u*u),m=d*l/p,g=d*u/p;else var y=R6(e,l,u),v=R6(r,-1*l,-1*u),x=v.x-y.x,b=v.y-y.y,w=x*x+b*b,p=Math.sqrt(w),d=(e.nodeRepulsion+r.nodeRepulsion)/w,m=d*x/p,g=d*b/p;e.isLocked||(e.offsetX-=m,e.offsetY-=g),r.isLocked||(r.offsetX+=m,r.offsetY+=g)}},"nodeRepulsion"),QKe=o(function(e,r,n,i){if(n>0)var a=e.maxX-r.minX;else var a=r.maxX-e.minX;if(i>0)var s=e.maxY-r.minY;else var s=r.maxY-e.minY;return a>=0&&s>=0?Math.sqrt(a*a+s*s):0},"nodesOverlap"),R6=o(function(e,r,n){var i=e.positionX,a=e.positionY,s=e.height||1,l=e.width||1,u=n/r,h=s/l,f={};return r===0&&0n?(f.x=i,f.y=a+s/2,f):0r&&-1*h<=u&&u<=h?(f.x=i-l/2,f.y=a-l*n/2/r,f):0=h)?(f.x=i+s*r/2/n,f.y=a+s/2,f):(0>n&&(u<=-1*h||u>=h)&&(f.x=i-s*r/2/n,f.y=a-s/2),f)},"findClippingPoint"),ZKe=o(function(e,r){for(var n=0;nn){var v=r.gravity*m/y,x=r.gravity*g/y;p.offsetX+=v,p.offsetY+=x}}}}},"calculateGravityForces"),eQe=o(function(e,r){var n=[],i=0,a=-1;for(n.push.apply(n,e.graphSet[0]),a+=e.graphSet[0].length;i<=a;){var s=n[i++],l=e.idToIndex[s],u=e.layoutNodes[l],h=u.children;if(0n)var a={x:n*e/i,y:n*r/i};else var a={x:e,y:r};return a},"limitForce"),nQe=o(function t(e,r){var n=e.parentId;if(n!=null){var i=r.layoutNodes[r.idToIndex[n]],a=!1;if((i.maxX==null||e.maxX+i.padRight>i.maxX)&&(i.maxX=e.maxX+i.padRight,a=!0),(i.minX==null||e.minX-i.padLefti.maxY)&&(i.maxY=e.maxY+i.padBottom,a=!0),(i.minY==null||e.minY-i.padTopx&&(g+=v+r.componentSpacing,m=0,y=0,v=0)}}},"separateComponents"),iQe={fit:!0,padding:30,boundingBox:void 0,avoidOverlap:!0,avoidOverlapPadding:10,nodeDimensionsIncludeLabels:!1,spacingFactor:void 0,condense:!1,rows:void 0,cols:void 0,position:o(function(e){},"position"),sort:void 0,animate:!1,animationDuration:500,animationEasing:void 0,animateFilter:o(function(e,r){return!0},"animateFilter"),ready:void 0,stop:void 0,transform:o(function(e,r){return r},"transform")};o(Rme,"GridLayout");Rme.prototype.run=function(){var t=this.options,e=t,r=t.cy,n=e.eles,i=n.nodes().not(":parent");e.sort&&(i=i.sort(e.sort));var a=Gs(e.boundingBox?e.boundingBox:{x1:0,y1:0,w:r.width(),h:r.height()});if(a.h===0||a.w===0)n.nodes().layoutPositions(this,e,function(Q){return{x:a.x1,y:a.y1}});else{var s=i.size(),l=Math.sqrt(s*a.h/a.w),u=Math.round(l),h=Math.round(a.w/a.h*l),f=o(function(X){if(X==null)return Math.min(u,h);var ie=Math.min(u,h);ie==u?u=X:h=X},"small"),d=o(function(X){if(X==null)return Math.max(u,h);var ie=Math.max(u,h);ie==u?u=X:h=X},"large"),p=e.rows,m=e.cols!=null?e.cols:e.columns;if(p!=null&&m!=null)u=p,h=m;else if(p!=null&&m==null)u=p,h=Math.ceil(s/u);else if(p==null&&m!=null)h=m,u=Math.ceil(s/h);else if(h*u>s){var g=f(),y=d();(g-1)*y>=s?f(g-1):(y-1)*g>=s&&d(y-1)}else for(;h*u=s?d(x+1):f(v+1)}var b=a.w/h,w=a.h/u;if(e.condense&&(b=0,w=0),e.avoidOverlap)for(var S=0;S=h&&(O=0,C++)},"moveToNextCell"),P={},F=0;F(O=_We(t,e,D[P],D[P+1],D[P+2],D[P+3])))return v(_,O),!0}else if(L.edgeType==="bezier"||L.edgeType==="multibezier"||L.edgeType==="self"||L.edgeType==="compound"){for(var D=L.allpts,P=0;P+5(O=AWe(t,e,D[P],D[P+1],D[P+2],D[P+3],D[P+4],D[P+5])))return v(_,O),!0}for(var F=F||A.source,B=B||A.target,$=i.getArrowWidth(M,N),z=[{name:"source",x:L.arrowStartX,y:L.arrowStartY,angle:L.srcArrowAngle},{name:"target",x:L.arrowEndX,y:L.arrowEndY,angle:L.tgtArrowAngle},{name:"mid-source",x:L.midX,y:L.midY,angle:L.midsrcArrowAngle},{name:"mid-target",x:L.midX,y:L.midY,angle:L.midtgtArrowAngle}],P=0;P0&&(x(F),x(B))}o(b,"checkEdge");function w(_,A,L){return Ul(_,A,L)}o(w,"preprop");function S(_,A){var L=_._private,M=p,N;A?N=A+"-":N="",_.boundingBox();var k=L.labelBounds[A||"main"],I=_.pstyle(N+"label").value,C=_.pstyle("text-events").strValue==="yes";if(!(!C||!I)){var O=w(L.rscratch,"labelX",A),D=w(L.rscratch,"labelY",A),P=w(L.rscratch,"labelAngle",A),F=_.pstyle(N+"text-margin-x").pfValue,B=_.pstyle(N+"text-margin-y").pfValue,$=k.x1-M-F,z=k.x2+M-F,Y=k.y1-M-B,Q=k.y2+M-B;if(P){var X=Math.cos(P),ie=Math.sin(P),j=o(function(ce,ue){return ce=ce-O,ue=ue-D,{x:ce*X-ue*ie+O,y:ce*ie+ue*X+D}},"rotate"),J=j($,Y),Z=j($,Q),H=j(z,Y),q=j(z,Q),K=[J.x+F,J.y+B,H.x+F,H.y+B,q.x+F,q.y+B,Z.x+F,Z.y+B];if(zs(t,e,K))return v(_),!0}else if(s1(k,t,e))return v(_),!0}}o(S,"checkLabel");for(var T=s.length-1;T>=0;T--){var E=s[T];E.isNode()?x(E)||S(E):b(E)||S(E)||S(E,"source")||S(E,"target")}return l};j0.getAllInBox=function(t,e,r,n){var i=this.getCachedZSortedEles().interactive,a=[],s=Math.min(t,r),l=Math.max(t,r),u=Math.min(e,n),h=Math.max(e,n);t=s,r=l,e=u,n=h;for(var f=Gs({x1:t,y1:e,x2:r,y2:n}),d=0;d0?-(Math.PI-e.ang):Math.PI+e.ang},"invertVec"),uQe=o(function(e,r,n,i,a){if(e!==ope?lpe(r,e,Gc):cQe(Jo,Gc),lpe(r,n,Jo),ape=Gc.nx*Jo.ny-Gc.ny*Jo.nx,spe=Gc.nx*Jo.nx-Gc.ny*-Jo.ny,ju=Math.asin(Math.max(-1,Math.min(1,ape))),Math.abs(ju)<1e-6){GP=r.x,$P=r.y,z0=Qg=0;return}G0=1,w6=!1,spe<0?ju<0?ju=Math.PI+ju:(ju=Math.PI-ju,G0=-1,w6=!0):ju>0&&(G0=-1,w6=!0),r.radius!==void 0?Qg=r.radius:Qg=i,O0=ju/2,f6=Math.min(Gc.len/2,Jo.len/2),a?(zc=Math.abs(Math.cos(O0)*Qg/Math.sin(O0)),zc>f6?(zc=f6,z0=Math.abs(zc*Math.sin(O0)/Math.cos(O0))):z0=Qg):(zc=Math.min(f6,Qg),z0=Math.abs(zc*Math.sin(O0)/Math.cos(O0))),VP=r.x+Jo.nx*zc,UP=r.y+Jo.ny*zc,GP=VP-Jo.ny*z0*G0,$P=UP+Jo.nx*z0*G0,Ome=r.x+Gc.nx*zc,Pme=r.y+Gc.ny*zc,ope=r},"calcCornerArc");o(Bme,"drawPreparedRoundCorner");o(mB,"getRoundCorner");$a={};$a.findMidptPtsEtc=function(t,e){var r=e.posPts,n=e.intersectionPts,i=e.vectorNormInverse,a,s=t.pstyle("source-endpoint"),l=t.pstyle("target-endpoint"),u=s.units!=null&&l.units!=null,h=o(function(T,E,_,A){var L=A-E,M=_-T,N=Math.sqrt(M*M+L*L);return{x:-L/N,y:M/N}},"recalcVectorNormInverse"),f=t.pstyle("edge-distances").value;switch(f){case"node-position":a=r;break;case"intersection":a=n;break;case"endpoints":{if(u){var d=this.manualEndptToPx(t.source()[0],s),p=$l(d,2),m=p[0],g=p[1],y=this.manualEndptToPx(t.target()[0],l),v=$l(y,2),x=v[0],b=v[1],w={x1:m,y1:g,x2:x,y2:b};i=h(m,g,x,b),a=w}else tn("Edge ".concat(t.id()," has edge-distances:endpoints specified without manual endpoints specified via source-endpoint and target-endpoint. Falling back on edge-distances:intersection (default).")),a=n;break}}return{midptPts:a,vectorNormInverse:i}};$a.findHaystackPoints=function(t){for(var e=0;e0?Math.max(Te-Ce,0):Math.min(Te+Ce,0)},"subDWH"),I=k(M,A),C=k(N,L),O=!1;b===h?x=Math.abs(I)>Math.abs(C)?i:n:b===u||b===l?(x=n,O=!0):(b===a||b===s)&&(x=i,O=!0);var D=x===n,P=D?C:I,F=D?N:M,B=$pe(F),$=!1;!(O&&(S||E))&&(b===l&&F<0||b===u&&F>0||b===a&&F>0||b===s&&F<0)&&(B*=-1,P=B*Math.abs(P),$=!0);var z;if(S){var Y=T<0?1+T:T;z=Y*P}else{var Q=T<0?P:0;z=Q+T*B}var X=o(function(Te){return Math.abs(Te)<_||Math.abs(Te)>=Math.abs(P)},"getIsTooClose"),ie=X(z),j=X(Math.abs(P)-Math.abs(z)),J=ie||j;if(J&&!$)if(D){var Z=Math.abs(F)<=p/2,H=Math.abs(M)<=m/2;if(Z){var q=(f.x1+f.x2)/2,K=f.y1,se=f.y2;r.segpts=[q,K,q,se]}else if(H){var ce=(f.y1+f.y2)/2,ue=f.x1,te=f.x2;r.segpts=[ue,ce,te,ce]}else r.segpts=[f.x1,f.y2]}else{var De=Math.abs(F)<=d/2,oe=Math.abs(N)<=g/2;if(De){var ke=(f.y1+f.y2)/2,Ie=f.x1,Se=f.x2;r.segpts=[Ie,ke,Se,ke]}else if(oe){var Ue=(f.x1+f.x2)/2,Pe=f.y1,_e=f.y2;r.segpts=[Ue,Pe,Ue,_e]}else r.segpts=[f.x2,f.y1]}else if(D){var me=f.y1+z+(v?p/2*B:0),W=f.x1,fe=f.x2;r.segpts=[W,me,fe,me]}else{var ge=f.x1+z+(v?d/2*B:0),re=f.y1,he=f.y2;r.segpts=[ge,re,ge,he]}if(r.isRound){var ne=t.pstyle("taxi-radius").value,ae=t.pstyle("radius-type").value[0]==="arc-radius";r.radii=new Array(r.segpts.length/2).fill(ne),r.isArcRadius=new Array(r.segpts.length/2).fill(ae)}};$a.tryToCorrectInvalidPoints=function(t,e){var r=t._private.rscratch;if(r.edgeType==="bezier"){var n=e.srcPos,i=e.tgtPos,a=e.srcW,s=e.srcH,l=e.tgtW,u=e.tgtH,h=e.srcShape,f=e.tgtShape,d=e.srcCornerRadius,p=e.tgtCornerRadius,m=e.srcRs,g=e.tgtRs,y=!ft(r.startX)||!ft(r.startY),v=!ft(r.arrowStartX)||!ft(r.arrowStartY),x=!ft(r.endX)||!ft(r.endY),b=!ft(r.arrowEndX)||!ft(r.arrowEndY),w=3,S=this.getArrowWidth(t.pstyle("width").pfValue,t.pstyle("arrow-scale").value)*this.arrowShapeWidth,T=w*S,E=H0({x:r.ctrlpts[0],y:r.ctrlpts[1]},{x:r.startX,y:r.startY}),_=EC.poolIndex()){var O=I;I=C,C=O}var D=L.srcPos=I.position(),P=L.tgtPos=C.position(),F=L.srcW=I.outerWidth(),B=L.srcH=I.outerHeight(),$=L.tgtW=C.outerWidth(),z=L.tgtH=C.outerHeight(),Y=L.srcShape=r.nodeShapes[e.getNodeShape(I)],Q=L.tgtShape=r.nodeShapes[e.getNodeShape(C)],X=L.srcCornerRadius=I.pstyle("corner-radius").value==="auto"?"auto":I.pstyle("corner-radius").pfValue,ie=L.tgtCornerRadius=C.pstyle("corner-radius").value==="auto"?"auto":C.pstyle("corner-radius").pfValue,j=L.tgtRs=C._private.rscratch,J=L.srcRs=I._private.rscratch;L.dirCounts={north:0,west:0,south:0,east:0,northwest:0,southwest:0,northeast:0,southeast:0};for(var Z=0;Z0){var se=a,ce=B0(se,Jg(r)),ue=B0(se,Jg(K)),te=ce;if(ue2){var De=B0(se,{x:K[2],y:K[3]});De0){var he=s,ne=B0(he,Jg(r)),ae=B0(he,Jg(re)),we=ne;if(ae2){var Te=B0(he,{x:re[2],y:re[3]});Te=g||_){v={cp:S,segment:E};break}}if(v)break}var A=v.cp,L=v.segment,M=(g-x)/L.length,N=L.t1-L.t0,k=m?L.t0+N*M:L.t1-N*M;k=Hx(0,k,1),e=t1(A.p0,A.p1,A.p2,k),p=fQe(A.p0,A.p1,A.p2,k);break}case"straight":case"segments":case"haystack":{for(var I=0,C,O,D,P,F=n.allpts.length,B=0;B+3=g));B+=2);var $=g-O,z=$/C;z=Hx(0,z,1),e=yWe(D,P,z),p=Gme(D,P);break}}s("labelX",d,e.x),s("labelY",d,e.y),s("labelAutoAngle",d,p)}},"calculateEndProjection");h("source"),h("target"),this.applyLabelDimensions(t)}};Hc.applyLabelDimensions=function(t){this.applyPrefixedLabelDimensions(t),t.isEdge()&&(this.applyPrefixedLabelDimensions(t,"source"),this.applyPrefixedLabelDimensions(t,"target"))};Hc.applyPrefixedLabelDimensions=function(t,e){var r=t._private,n=this.getLabelText(t,e),i=this.calculateLabelDimensions(t,n),a=t.pstyle("line-height").pfValue,s=t.pstyle("text-wrap").strValue,l=Ul(r.rscratch,"labelWrapCachedLines",e)||[],u=s!=="wrap"?1:Math.max(l.length,1),h=i.height/u,f=h*a,d=i.width,p=i.height+(u-1)*(a-1)*h;Tf(r.rstyle,"labelWidth",e,d),Tf(r.rscratch,"labelWidth",e,d),Tf(r.rstyle,"labelHeight",e,p),Tf(r.rscratch,"labelHeight",e,p),Tf(r.rscratch,"labelLineHeight",e,f)};Hc.getLabelText=function(t,e){var r=t._private,n=e?e+"-":"",i=t.pstyle(n+"label").strValue,a=t.pstyle("text-transform").value,s=o(function(Q,X){return X?(Tf(r.rscratch,Q,e,X),X):Ul(r.rscratch,Q,e)},"rscratch");if(!i)return"";a=="none"||(a=="uppercase"?i=i.toUpperCase():a=="lowercase"&&(i=i.toLowerCase()));var l=t.pstyle("text-wrap").value;if(l==="wrap"){var u=s("labelKey");if(u!=null&&s("labelWrapKey")===u)return s("labelWrapCachedText");for(var h="\u200B",f=i.split(` +`),d=t.pstyle("text-max-width").pfValue,p=t.pstyle("text-overflow-wrap").value,m=p==="anywhere",g=[],y=/[\s\u200b]+|$/g,v=0;vd){var T=x.matchAll(y),E="",_=0,A=Tpe(T),L;try{for(A.s();!(L=A.n()).done;){var M=L.value,N=M[0],k=x.substring(_,M.index);_=M.index+N.length;var I=E.length===0?k:E+k+N,C=this.calculateLabelDimensions(t,I),O=C.width;O<=d?E+=k+N:(E&&g.push(E),E=k+N)}}catch(Y){A.e(Y)}finally{A.f()}E.match(/^[\s\u200b]+$/)||g.push(E)}else g.push(x)}s("labelWrapCachedLines",g),i=s("labelWrapCachedText",g.join(` +`)),s("labelWrapKey",u)}else if(l==="ellipsis"){var D=t.pstyle("text-max-width").pfValue,P="",F="\u2026",B=!1;if(this.calculateLabelDimensions(t,i).widthD)break;P+=i[$],$===i.length-1&&(B=!0)}return B||(P+=F),P}return i};Hc.getLabelJustification=function(t){var e=t.pstyle("text-justification").strValue,r=t.pstyle("text-halign").strValue;if(e==="auto")if(t.isNode())switch(r){case"left":return"right";case"right":return"left";default:return"center"}else return"center";else return e};Hc.calculateLabelDimensions=function(t,e){var r=this,n=r.cy.window(),i=n.document,a=U0(e,t._private.labelDimsKey),s=r.labelDimCache||(r.labelDimCache=[]),l=s[a];if(l!=null)return l;var u=0,h=t.pstyle("font-style").strValue,f=t.pstyle("font-size").pfValue,d=t.pstyle("font-family").strValue,p=t.pstyle("font-weight").strValue,m=this.labelCalcCanvas,g=this.labelCalcCanvasContext;if(!m){m=this.labelCalcCanvas=i.createElement("canvas"),g=this.labelCalcCanvasContext=m.getContext("2d");var y=m.style;y.position="absolute",y.left="-9999px",y.top="-9999px",y.zIndex="-1",y.visibility="hidden",y.pointerEvents="none"}g.font="".concat(h," ").concat(p," ").concat(f,"px ").concat(d);for(var v=0,x=0,b=e.split(` +`),w=0;w1&&arguments[1]!==void 0?arguments[1]:!0;if(e.merge(s),l)for(var u=0;u=t.desktopTapThreshold2}var Je=i(W);ze&&(t.hoverData.tapholdCancelled=!0);var Ve=o(function(){var St=t.hoverData.dragDelta=t.hoverData.dragDelta||[];St.length===0?(St.push(ye[0]),St.push(ye[1])):(St[0]+=ye[0],St[1]+=ye[1])},"updateDragDelta");ge=!0,n(Ae,["mousemove","vmousemove","tapdrag"],W,{x:ae[0],y:ae[1]});var je=o(function(){t.data.bgActivePosistion=void 0,t.hoverData.selecting||re.emit({originalEvent:W,type:"boxstart",position:{x:ae[0],y:ae[1]}}),Ce[4]=1,t.hoverData.selecting=!0,t.redrawHint("select",!0),t.redraw()},"goIntoBoxMode");if(t.hoverData.which===3){if(ze){var kt={originalEvent:W,type:"cxtdrag",position:{x:ae[0],y:ae[1]}};Me?Me.emit(kt):re.emit(kt),t.hoverData.cxtDragged=!0,(!t.hoverData.cxtOver||Ae!==t.hoverData.cxtOver)&&(t.hoverData.cxtOver&&t.hoverData.cxtOver.emit({originalEvent:W,type:"cxtdragout",position:{x:ae[0],y:ae[1]}}),t.hoverData.cxtOver=Ae,Ae&&Ae.emit({originalEvent:W,type:"cxtdragover",position:{x:ae[0],y:ae[1]}}))}}else if(t.hoverData.dragging){if(ge=!0,re.panningEnabled()&&re.userPanningEnabled()){var at;if(t.hoverData.justStartedPan){var xt=t.hoverData.mdownPos;at={x:(ae[0]-xt[0])*he,y:(ae[1]-xt[1])*he},t.hoverData.justStartedPan=!1}else at={x:ye[0]*he,y:ye[1]*he};re.panBy(at),re.emit("dragpan"),t.hoverData.dragged=!0}ae=t.projectIntoViewport(W.clientX,W.clientY)}else if(Ce[4]==1&&(Me==null||Me.pannable())){if(ze){if(!t.hoverData.dragging&&re.boxSelectionEnabled()&&(Je||!re.panningEnabled()||!re.userPanningEnabled()))je();else if(!t.hoverData.selecting&&re.panningEnabled()&&re.userPanningEnabled()){var it=a(Me,t.hoverData.downs);it&&(t.hoverData.dragging=!0,t.hoverData.justStartedPan=!0,Ce[4]=0,t.data.bgActivePosistion=Jg(we),t.redrawHint("select",!0),t.redraw())}Me&&Me.pannable()&&Me.active()&&Me.unactivate()}}else{if(Me&&Me.pannable()&&Me.active()&&Me.unactivate(),(!Me||!Me.grabbed())&&Ae!=Ge&&(Ge&&n(Ge,["mouseout","tapdragout"],W,{x:ae[0],y:ae[1]}),Ae&&n(Ae,["mouseover","tapdragover"],W,{x:ae[0],y:ae[1]}),t.hoverData.last=Ae),Me)if(ze){if(re.boxSelectionEnabled()&&Je)Me&&Me.grabbed()&&(v(He),Me.emit("freeon"),He.emit("free"),t.dragData.didDrag&&(Me.emit("dragfreeon"),He.emit("dragfree"))),je();else if(Me&&Me.grabbed()&&t.nodeIsDraggable(Me)){var dt=!t.dragData.didDrag;dt&&t.redrawHint("eles",!0),t.dragData.didDrag=!0,t.hoverData.draggingEles||g(He,{inDragLayer:!0});var lt={x:0,y:0};if(ft(ye[0])&&ft(ye[1])&&(lt.x+=ye[0],lt.y+=ye[1],dt)){var It=t.hoverData.dragDelta;It&&ft(It[0])&&ft(It[1])&&(lt.x+=It[0],lt.y+=It[1])}t.hoverData.draggingEles=!0,He.silentShift(lt).emit("position drag"),t.redrawHint("drag",!0),t.redraw()}}else Ve();ge=!0}if(Ce[2]=ae[0],Ce[3]=ae[1],ge)return W.stopPropagation&&W.stopPropagation(),W.preventDefault&&W.preventDefault(),!1}},"mousemoveHandler"),!1);var M,N,k;t.registerBinding(e,"mouseup",o(function(W){if(!(t.hoverData.which===1&&W.which!==1&&t.hoverData.capture)){var fe=t.hoverData.capture;if(fe){t.hoverData.capture=!1;var ge=t.cy,re=t.projectIntoViewport(W.clientX,W.clientY),he=t.selection,ne=t.findNearestElement(re[0],re[1],!0,!1),ae=t.dragData.possibleDragElements,we=t.hoverData.down,Te=i(W);if(t.data.bgActivePosistion&&(t.redrawHint("select",!0),t.redraw()),t.hoverData.tapholdCancelled=!0,t.data.bgActivePosistion=void 0,we&&we.unactivate(),t.hoverData.which===3){var Ce={originalEvent:W,type:"cxttapend",position:{x:re[0],y:re[1]}};if(we?we.emit(Ce):ge.emit(Ce),!t.hoverData.cxtDragged){var Ae={originalEvent:W,type:"cxttap",position:{x:re[0],y:re[1]}};we?we.emit(Ae):ge.emit(Ae)}t.hoverData.cxtDragged=!1,t.hoverData.which=null}else if(t.hoverData.which===1){if(n(ne,["mouseup","tapend","vmouseup"],W,{x:re[0],y:re[1]}),!t.dragData.didDrag&&!t.hoverData.dragged&&!t.hoverData.selecting&&!t.hoverData.isOverThresholdDrag&&(n(we,["click","tap","vclick"],W,{x:re[0],y:re[1]}),N=!1,W.timeStamp-k<=ge.multiClickDebounceTime()?(M&&clearTimeout(M),N=!0,k=null,n(we,["dblclick","dbltap","vdblclick"],W,{x:re[0],y:re[1]})):(M=setTimeout(function(){N||n(we,["oneclick","onetap","voneclick"],W,{x:re[0],y:re[1]})},ge.multiClickDebounceTime()),k=W.timeStamp)),we==null&&!t.dragData.didDrag&&!t.hoverData.selecting&&!t.hoverData.dragged&&!i(W)&&(ge.$(r).unselect(["tapunselect"]),ae.length>0&&t.redrawHint("eles",!0),t.dragData.possibleDragElements=ae=ge.collection()),ne==we&&!t.dragData.didDrag&&!t.hoverData.selecting&&ne!=null&&ne._private.selectable&&(t.hoverData.dragging||(ge.selectionType()==="additive"||Te?ne.selected()?ne.unselect(["tapunselect"]):ne.select(["tapselect"]):Te||(ge.$(r).unmerge(ne).unselect(["tapunselect"]),ne.select(["tapselect"]))),t.redrawHint("eles",!0)),t.hoverData.selecting){var Ge=ge.collection(t.getAllInBox(he[0],he[1],he[2],he[3]));t.redrawHint("select",!0),Ge.length>0&&t.redrawHint("eles",!0),ge.emit({type:"boxend",originalEvent:W,position:{x:re[0],y:re[1]}});var Me=o(function(ze){return ze.selectable()&&!ze.selected()},"eleWouldBeSelected");ge.selectionType()==="additive"||Te||ge.$(r).unmerge(Ge).unselect(),Ge.emit("box").stdFilter(Me).select().emit("boxselect"),t.redraw()}if(t.hoverData.dragging&&(t.hoverData.dragging=!1,t.redrawHint("select",!0),t.redrawHint("eles",!0),t.redraw()),!he[4]){t.redrawHint("drag",!0),t.redrawHint("eles",!0);var ye=we&&we.grabbed();v(ae),ye&&(we.emit("freeon"),ae.emit("free"),t.dragData.didDrag&&(we.emit("dragfreeon"),ae.emit("dragfree")))}}he[4]=0,t.hoverData.down=null,t.hoverData.cxtStarted=!1,t.hoverData.draggingEles=!1,t.hoverData.selecting=!1,t.hoverData.isOverThresholdDrag=!1,t.dragData.didDrag=!1,t.hoverData.dragged=!1,t.hoverData.dragDelta=[],t.hoverData.mdownPos=null,t.hoverData.mdownGPos=null}}},"mouseupHandler"),!1);var I=o(function(W){if(!t.scrollingPage){var fe=t.cy,ge=fe.zoom(),re=fe.pan(),he=t.projectIntoViewport(W.clientX,W.clientY),ne=[he[0]*ge+re.x,he[1]*ge+re.y];if(t.hoverData.draggingEles||t.hoverData.dragging||t.hoverData.cxtStarted||A()){W.preventDefault();return}if(fe.panningEnabled()&&fe.userPanningEnabled()&&fe.zoomingEnabled()&&fe.userZoomingEnabled()){W.preventDefault(),t.data.wheelZooming=!0,clearTimeout(t.data.wheelTimeout),t.data.wheelTimeout=setTimeout(function(){t.data.wheelZooming=!1,t.redrawHint("eles",!0),t.redraw()},150);var ae;W.deltaY!=null?ae=W.deltaY/-250:W.wheelDeltaY!=null?ae=W.wheelDeltaY/1e3:ae=W.wheelDelta/1e3,ae=ae*t.wheelSensitivity;var we=W.deltaMode===1;we&&(ae*=33);var Te=fe.zoom()*Math.pow(10,ae);W.type==="gesturechange"&&(Te=t.gestureStartZoom*W.scale),fe.zoom({level:Te,renderedPosition:{x:ne[0],y:ne[1]}}),fe.emit(W.type==="gesturechange"?"pinchzoom":"scrollzoom")}}},"wheelHandler");t.registerBinding(t.container,"wheel",I,!0),t.registerBinding(e,"scroll",o(function(W){t.scrollingPage=!0,clearTimeout(t.scrollingPageTimeout),t.scrollingPageTimeout=setTimeout(function(){t.scrollingPage=!1},250)},"scrollHandler"),!0),t.registerBinding(t.container,"gesturestart",o(function(W){t.gestureStartZoom=t.cy.zoom(),t.hasTouchStarted||W.preventDefault()},"gestureStartHandler"),!0),t.registerBinding(t.container,"gesturechange",function(me){t.hasTouchStarted||I(me)},!0),t.registerBinding(t.container,"mouseout",o(function(W){var fe=t.projectIntoViewport(W.clientX,W.clientY);t.cy.emit({originalEvent:W,type:"mouseout",position:{x:fe[0],y:fe[1]}})},"mouseOutHandler"),!1),t.registerBinding(t.container,"mouseover",o(function(W){var fe=t.projectIntoViewport(W.clientX,W.clientY);t.cy.emit({originalEvent:W,type:"mouseover",position:{x:fe[0],y:fe[1]}})},"mouseOverHandler"),!1);var C,O,D,P,F,B,$,z,Y,Q,X,ie,j,J=o(function(W,fe,ge,re){return Math.sqrt((ge-W)*(ge-W)+(re-fe)*(re-fe))},"distance"),Z=o(function(W,fe,ge,re){return(ge-W)*(ge-W)+(re-fe)*(re-fe)},"distanceSq"),H;t.registerBinding(t.container,"touchstart",H=o(function(W){if(t.hasTouchStarted=!0,!!L(W)){b(),t.touchData.capture=!0,t.data.bgActivePosistion=void 0;var fe=t.cy,ge=t.touchData.now,re=t.touchData.earlier;if(W.touches[0]){var he=t.projectIntoViewport(W.touches[0].clientX,W.touches[0].clientY);ge[0]=he[0],ge[1]=he[1]}if(W.touches[1]){var he=t.projectIntoViewport(W.touches[1].clientX,W.touches[1].clientY);ge[2]=he[0],ge[3]=he[1]}if(W.touches[2]){var he=t.projectIntoViewport(W.touches[2].clientX,W.touches[2].clientY);ge[4]=he[0],ge[5]=he[1]}if(W.touches[1]){t.touchData.singleTouchMoved=!0,v(t.dragData.touchDragEles);var ne=t.findContainerClientCoords();Y=ne[0],Q=ne[1],X=ne[2],ie=ne[3],C=W.touches[0].clientX-Y,O=W.touches[0].clientY-Q,D=W.touches[1].clientX-Y,P=W.touches[1].clientY-Q,j=0<=C&&C<=X&&0<=D&&D<=X&&0<=O&&O<=ie&&0<=P&&P<=ie;var ae=fe.pan(),we=fe.zoom();F=J(C,O,D,P),B=Z(C,O,D,P),$=[(C+D)/2,(O+P)/2],z=[($[0]-ae.x)/we,($[1]-ae.y)/we];var Te=200,Ce=Te*Te;if(B=1){for(var gt=t.touchData.startPosition=[null,null,null,null,null,null],yt=0;yt=t.touchTapThreshold2}if(fe&&t.touchData.cxt){W.preventDefault();var gt=W.touches[0].clientX-Y,yt=W.touches[0].clientY-Q,tt=W.touches[1].clientX-Y,Ye=W.touches[1].clientY-Q,Je=Z(gt,yt,tt,Ye),Ve=Je/B,je=150,kt=je*je,at=1.5,xt=at*at;if(Ve>=xt||Je>=kt){t.touchData.cxt=!1,t.data.bgActivePosistion=void 0,t.redrawHint("select",!0);var it={originalEvent:W,type:"cxttapend",position:{x:he[0],y:he[1]}};t.touchData.start?(t.touchData.start.unactivate().emit(it),t.touchData.start=null):re.emit(it)}}if(fe&&t.touchData.cxt){var it={originalEvent:W,type:"cxtdrag",position:{x:he[0],y:he[1]}};t.data.bgActivePosistion=void 0,t.redrawHint("select",!0),t.touchData.start?t.touchData.start.emit(it):re.emit(it),t.touchData.start&&(t.touchData.start._private.grabbed=!1),t.touchData.cxtDragged=!0;var dt=t.findNearestElement(he[0],he[1],!0,!0);(!t.touchData.cxtOver||dt!==t.touchData.cxtOver)&&(t.touchData.cxtOver&&t.touchData.cxtOver.emit({originalEvent:W,type:"cxtdragout",position:{x:he[0],y:he[1]}}),t.touchData.cxtOver=dt,dt&&dt.emit({originalEvent:W,type:"cxtdragover",position:{x:he[0],y:he[1]}}))}else if(fe&&W.touches[2]&&re.boxSelectionEnabled())W.preventDefault(),t.data.bgActivePosistion=void 0,this.lastThreeTouch=+new Date,t.touchData.selecting||re.emit({originalEvent:W,type:"boxstart",position:{x:he[0],y:he[1]}}),t.touchData.selecting=!0,t.touchData.didSelect=!0,ge[4]=1,!ge||ge.length===0||ge[0]===void 0?(ge[0]=(he[0]+he[2]+he[4])/3,ge[1]=(he[1]+he[3]+he[5])/3,ge[2]=(he[0]+he[2]+he[4])/3+1,ge[3]=(he[1]+he[3]+he[5])/3+1):(ge[2]=(he[0]+he[2]+he[4])/3,ge[3]=(he[1]+he[3]+he[5])/3),t.redrawHint("select",!0),t.redraw();else if(fe&&W.touches[1]&&!t.touchData.didSelect&&re.zoomingEnabled()&&re.panningEnabled()&&re.userZoomingEnabled()&&re.userPanningEnabled()){W.preventDefault(),t.data.bgActivePosistion=void 0,t.redrawHint("select",!0);var lt=t.dragData.touchDragEles;if(lt){t.redrawHint("drag",!0);for(var It=0;It0&&!t.hoverData.draggingEles&&!t.swipePanning&&t.data.bgActivePosistion!=null&&(t.data.bgActivePosistion=void 0,t.redrawHint("select",!0),t.redraw())}},"touchmoveHandler"),!1);var K;t.registerBinding(e,"touchcancel",K=o(function(W){var fe=t.touchData.start;t.touchData.capture=!1,fe&&fe.unactivate()},"touchcancelHandler"));var se,ce,ue,te;if(t.registerBinding(e,"touchend",se=o(function(W){var fe=t.touchData.start,ge=t.touchData.capture;if(ge)W.touches.length===0&&(t.touchData.capture=!1),W.preventDefault();else return;var re=t.selection;t.swipePanning=!1,t.hoverData.draggingEles=!1;var he=t.cy,ne=he.zoom(),ae=t.touchData.now,we=t.touchData.earlier;if(W.touches[0]){var Te=t.projectIntoViewport(W.touches[0].clientX,W.touches[0].clientY);ae[0]=Te[0],ae[1]=Te[1]}if(W.touches[1]){var Te=t.projectIntoViewport(W.touches[1].clientX,W.touches[1].clientY);ae[2]=Te[0],ae[3]=Te[1]}if(W.touches[2]){var Te=t.projectIntoViewport(W.touches[2].clientX,W.touches[2].clientY);ae[4]=Te[0],ae[5]=Te[1]}fe&&fe.unactivate();var Ce;if(t.touchData.cxt){if(Ce={originalEvent:W,type:"cxttapend",position:{x:ae[0],y:ae[1]}},fe?fe.emit(Ce):he.emit(Ce),!t.touchData.cxtDragged){var Ae={originalEvent:W,type:"cxttap",position:{x:ae[0],y:ae[1]}};fe?fe.emit(Ae):he.emit(Ae)}t.touchData.start&&(t.touchData.start._private.grabbed=!1),t.touchData.cxt=!1,t.touchData.start=null,t.redraw();return}if(!W.touches[2]&&he.boxSelectionEnabled()&&t.touchData.selecting){t.touchData.selecting=!1;var Ge=he.collection(t.getAllInBox(re[0],re[1],re[2],re[3]));re[0]=void 0,re[1]=void 0,re[2]=void 0,re[3]=void 0,re[4]=0,t.redrawHint("select",!0),he.emit({type:"boxend",originalEvent:W,position:{x:ae[0],y:ae[1]}});var Me=o(function(kt){return kt.selectable()&&!kt.selected()},"eleWouldBeSelected");Ge.emit("box").stdFilter(Me).select().emit("boxselect"),Ge.nonempty()&&t.redrawHint("eles",!0),t.redraw()}if(fe?.unactivate(),W.touches[2])t.data.bgActivePosistion=void 0,t.redrawHint("select",!0);else if(!W.touches[1]){if(!W.touches[0]){if(!W.touches[0]){t.data.bgActivePosistion=void 0,t.redrawHint("select",!0);var ye=t.dragData.touchDragEles;if(fe!=null){var He=fe._private.grabbed;v(ye),t.redrawHint("drag",!0),t.redrawHint("eles",!0),He&&(fe.emit("freeon"),ye.emit("free"),t.dragData.didDrag&&(fe.emit("dragfreeon"),ye.emit("dragfree"))),n(fe,["touchend","tapend","vmouseup","tapdragout"],W,{x:ae[0],y:ae[1]}),fe.unactivate(),t.touchData.start=null}else{var ze=t.findNearestElement(ae[0],ae[1],!0,!0);n(ze,["touchend","tapend","vmouseup","tapdragout"],W,{x:ae[0],y:ae[1]})}var Ze=t.touchData.startPosition[0]-ae[0],gt=Ze*Ze,yt=t.touchData.startPosition[1]-ae[1],tt=yt*yt,Ye=gt+tt,Je=Ye*ne*ne;t.touchData.singleTouchMoved||(fe||he.$(":selected").unselect(["tapunselect"]),n(fe,["tap","vclick"],W,{x:ae[0],y:ae[1]}),ce=!1,W.timeStamp-te<=he.multiClickDebounceTime()?(ue&&clearTimeout(ue),ce=!0,te=null,n(fe,["dbltap","vdblclick"],W,{x:ae[0],y:ae[1]})):(ue=setTimeout(function(){ce||n(fe,["onetap","voneclick"],W,{x:ae[0],y:ae[1]})},he.multiClickDebounceTime()),te=W.timeStamp)),fe!=null&&!t.dragData.didDrag&&fe._private.selectable&&Je"u"){var De=[],oe=o(function(W){return{clientX:W.clientX,clientY:W.clientY,force:1,identifier:W.pointerId,pageX:W.pageX,pageY:W.pageY,radiusX:W.width/2,radiusY:W.height/2,screenX:W.screenX,screenY:W.screenY,target:W.target}},"makeTouch"),ke=o(function(W){return{event:W,touch:oe(W)}},"makePointer"),Ie=o(function(W){De.push(ke(W))},"addPointer"),Se=o(function(W){for(var fe=0;fe0)return Y[0]}return null},"getCurveT"),g=Object.keys(p),y=0;y0?m:Hpe(a,s,e,r,n,i,l,u)},"intersectLine"),checkPoint:o(function(e,r,n,i,a,s,l,u){u=u==="auto"?Y0(i,a):u;var h=2*u;if(Qu(e,r,this.points,s,l,i,a-h,[0,-1],n)||Qu(e,r,this.points,s,l,i-h,a,[0,-1],n))return!0;var f=i/2+2*n,d=a/2+2*n,p=[s-f,l-d,s-f,l,s+f,l,s+f,l-d];return!!(zs(e,r,p)||$0(e,r,h,h,s+i/2-u,l+a/2-u,n)||$0(e,r,h,h,s-i/2+u,l+a/2-u,n))},"checkPoint")}};Ju.registerNodeShapes=function(){var t=this.nodeShapes={},e=this;this.generateEllipse(),this.generatePolygon("triangle",ls(3,0)),this.generateRoundPolygon("round-triangle",ls(3,0)),this.generatePolygon("rectangle",ls(4,0)),t.square=t.rectangle,this.generateRoundRectangle(),this.generateCutRectangle(),this.generateBarrel(),this.generateBottomRoundrectangle();{var r=[0,1,1,0,0,-1,-1,0];this.generatePolygon("diamond",r),this.generateRoundPolygon("round-diamond",r)}this.generatePolygon("pentagon",ls(5,0)),this.generateRoundPolygon("round-pentagon",ls(5,0)),this.generatePolygon("hexagon",ls(6,0)),this.generateRoundPolygon("round-hexagon",ls(6,0)),this.generatePolygon("heptagon",ls(7,0)),this.generateRoundPolygon("round-heptagon",ls(7,0)),this.generatePolygon("octagon",ls(8,0)),this.generateRoundPolygon("round-octagon",ls(8,0));var n=new Array(20);{var i=NP(5,0),a=NP(5,Math.PI/5),s=.5*(3-Math.sqrt(5));s*=1.57;for(var l=0;l=e.deqFastCost*S)break}else if(h){if(b>=e.deqCost*m||b>=e.deqAvgCost*p)break}else if(w>=e.deqNoDrawCost*LP)break;var T=e.deq(n,v,y);if(T.length>0)for(var E=0;E0&&(e.onDeqd(n,g),!h&&e.shouldRedraw(n,g,v,y)&&a())},"dequeue"),l=e.priority||JP;i.beforeRender(s,l(n))}},"setupDequeueingImpl")},"setupDequeueing")},pQe=function(){function t(e){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:C6;XP(this,t),this.idsByKey=new Vc,this.keyForId=new Vc,this.cachesByLvl=new Vc,this.lvls=[],this.getKey=e,this.doesEleInvalidateKey=r}return o(t,"ElementTextureCacheLookup"),jP(t,[{key:"getIdsFor",value:o(function(r){r==null&&oi("Can not get id list for null key");var n=this.idsByKey,i=this.idsByKey.get(r);return i||(i=new c1,n.set(r,i)),i},"getIdsFor")},{key:"addIdForKey",value:o(function(r,n){r!=null&&this.getIdsFor(r).add(n)},"addIdForKey")},{key:"deleteIdForKey",value:o(function(r,n){r!=null&&this.getIdsFor(r).delete(n)},"deleteIdForKey")},{key:"getNumberOfIdsForKey",value:o(function(r){return r==null?0:this.getIdsFor(r).size},"getNumberOfIdsForKey")},{key:"updateKeyMappingFor",value:o(function(r){var n=r.id(),i=this.keyForId.get(n),a=this.getKey(r);this.deleteIdForKey(i,n),this.addIdForKey(a,n),this.keyForId.set(n,a)},"updateKeyMappingFor")},{key:"deleteKeyMappingFor",value:o(function(r){var n=r.id(),i=this.keyForId.get(n);this.deleteIdForKey(i,n),this.keyForId.delete(n)},"deleteKeyMappingFor")},{key:"keyHasChangedFor",value:o(function(r){var n=r.id(),i=this.keyForId.get(n),a=this.getKey(r);return i!==a},"keyHasChangedFor")},{key:"isInvalid",value:o(function(r){return this.keyHasChangedFor(r)||this.doesEleInvalidateKey(r)},"isInvalid")},{key:"getCachesAt",value:o(function(r){var n=this.cachesByLvl,i=this.lvls,a=n.get(r);return a||(a=new Vc,n.set(r,a),i.push(r)),a},"getCachesAt")},{key:"getCache",value:o(function(r,n){return this.getCachesAt(n).get(r)},"getCache")},{key:"get",value:o(function(r,n){var i=this.getKey(r),a=this.getCache(i,n);return a!=null&&this.updateKeyMappingFor(r),a},"get")},{key:"getForCachedKey",value:o(function(r,n){var i=this.keyForId.get(r.id()),a=this.getCache(i,n);return a},"getForCachedKey")},{key:"hasCache",value:o(function(r,n){return this.getCachesAt(n).has(r)},"hasCache")},{key:"has",value:o(function(r,n){var i=this.getKey(r);return this.hasCache(i,n)},"has")},{key:"setCache",value:o(function(r,n,i){i.key=r,this.getCachesAt(n).set(r,i)},"setCache")},{key:"set",value:o(function(r,n,i){var a=this.getKey(r);this.setCache(a,n,i),this.updateKeyMappingFor(r)},"set")},{key:"deleteCache",value:o(function(r,n){this.getCachesAt(n).delete(r)},"deleteCache")},{key:"delete",value:o(function(r,n){var i=this.getKey(r);this.deleteCache(i,n)},"_delete")},{key:"invalidateKey",value:o(function(r){var n=this;this.lvls.forEach(function(i){return n.deleteCache(r,i)})},"invalidateKey")},{key:"invalidate",value:o(function(r){var n=r.id(),i=this.keyForId.get(n);this.deleteKeyMappingFor(r);var a=this.doesEleInvalidateKey(r);return a&&this.invalidateKey(i),a||this.getNumberOfIdsForKey(i)===0},"invalidate")}]),t}(),fpe=25,d6=50,T6=-4,HP=3,mQe=7.99,gQe=8,yQe=1024,vQe=1024,xQe=1024,bQe=.2,wQe=.8,TQe=10,kQe=.15,EQe=.1,CQe=.9,SQe=.9,AQe=100,_Qe=1,e1={dequeue:"dequeue",downscale:"downscale",highQuality:"highQuality"},LQe=Sa({getKey:null,doesEleInvalidateKey:C6,drawElement:null,getBoundingBox:null,getRotationPoint:null,getRotationOffset:null,isVisible:Ppe,allowEdgeTxrCaching:!0,allowParentTxrCaching:!0}),Bx=o(function(e,r){var n=this;n.renderer=e,n.onDequeues=[];var i=LQe(r);Wt(n,i),n.lookup=new pQe(i.getKey,i.doesEleInvalidateKey),n.setupDequeueing()},"ElementTextureCache"),Yi=Bx.prototype;Yi.reasons=e1;Yi.getTextureQueue=function(t){var e=this;return e.eleImgCaches=e.eleImgCaches||{},e.eleImgCaches[t]=e.eleImgCaches[t]||[]};Yi.getRetiredTextureQueue=function(t){var e=this,r=e.eleImgCaches.retired=e.eleImgCaches.retired||{},n=r[t]=r[t]||[];return n};Yi.getElementQueue=function(){var t=this,e=t.eleCacheQueue=t.eleCacheQueue||new eb(function(r,n){return n.reqs-r.reqs});return e};Yi.getElementKeyToQueue=function(){var t=this,e=t.eleKeyToCacheQueue=t.eleKeyToCacheQueue||{};return e};Yi.getElement=function(t,e,r,n,i){var a=this,s=this.renderer,l=s.cy.zoom(),u=this.lookup;if(!e||e.w===0||e.h===0||isNaN(e.w)||isNaN(e.h)||!t.visible()||t.removed()||!a.allowEdgeTxrCaching&&t.isEdge()||!a.allowParentTxrCaching&&t.isParent())return null;if(n==null&&(n=Math.ceil(tB(l*r))),n=mQe||n>HP)return null;var h=Math.pow(2,n),f=e.h*h,d=e.w*h,p=s.eleTextBiggerThanMin(t,h);if(!this.isVisible(t,p))return null;var m=u.get(t,n);if(m&&m.invalidated&&(m.invalidated=!1,m.texture.invalidatedWidth-=m.width),m)return m;var g;if(f<=fpe?g=fpe:f<=d6?g=d6:g=Math.ceil(f/d6)*d6,f>xQe||d>vQe)return null;var y=a.getTextureQueue(g),v=y[y.length-2],x=o(function(){return a.recycleTexture(g,d)||a.addTexture(g,d)},"addNewTxr");v||(v=y[y.length-1]),v||(v=x()),v.width-v.usedWidthn;N--)L=a.getElement(t,e,r,N,e1.downscale);M()}else return a.queueElement(t,E.level-1),E;else{var k;if(!w&&!S&&!T)for(var I=n-1;I>=T6;I--){var C=u.get(t,I);if(C){k=C;break}}if(b(k))return a.queueElement(t,n),k;v.context.translate(v.usedWidth,0),v.context.scale(h,h),this.drawElement(v.context,t,e,p,!1),v.context.scale(1/h,1/h),v.context.translate(-v.usedWidth,0)}return m={x:v.usedWidth,texture:v,level:n,scale:h,width:d,height:f,scaledLabelShown:p},v.usedWidth+=Math.ceil(d+gQe),v.eleCaches.push(m),u.set(t,n,m),a.checkTextureFullness(v),m};Yi.invalidateElements=function(t){for(var e=0;e=bQe*t.width&&this.retireTexture(t)};Yi.checkTextureFullness=function(t){var e=this,r=e.getTextureQueue(t.height);t.usedWidth/t.width>wQe&&t.fullnessChecks>=TQe?Af(r,t):t.fullnessChecks++};Yi.retireTexture=function(t){var e=this,r=t.height,n=e.getTextureQueue(r),i=this.lookup;Af(n,t),t.retired=!0;for(var a=t.eleCaches,s=0;s=e)return s.retired=!1,s.usedWidth=0,s.invalidatedWidth=0,s.fullnessChecks=0,eB(s.eleCaches),s.context.setTransform(1,0,0,1,0,0),s.context.clearRect(0,0,s.width,s.height),Af(i,s),n.push(s),s}};Yi.queueElement=function(t,e){var r=this,n=r.getElementQueue(),i=r.getElementKeyToQueue(),a=this.getKey(t),s=i[a];if(s)s.level=Math.max(s.level,e),s.eles.merge(t),s.reqs++,n.updateItem(s);else{var l={eles:t.spawn().merge(t),level:e,reqs:1,key:a};n.push(l),i[a]=l}};Yi.dequeue=function(t){for(var e=this,r=e.getElementQueue(),n=e.getElementKeyToQueue(),i=[],a=e.lookup,s=0;s<_Qe&&r.size()>0;s++){var l=r.pop(),u=l.key,h=l.eles[0],f=a.hasCache(h,l.level);if(n[u]=null,f)continue;i.push(l);var d=e.getBoundingBox(h);e.getElement(h,d,t,l.level,e1.dequeue)}return i};Yi.removeFromQueue=function(t){var e=this,r=e.getElementQueue(),n=e.getElementKeyToQueue(),i=this.getKey(t),a=n[i];a!=null&&(a.eles.length===1?(a.reqs=ZP,r.updateItem(a),r.pop(),n[i]=null):a.eles.unmerge(t))};Yi.onDequeue=function(t){this.onDequeues.push(t)};Yi.offDequeue=function(t){Af(this.onDequeues,t)};Yi.setupDequeueing=Yme.setupDequeueing({deqRedrawThreshold:AQe,deqCost:kQe,deqAvgCost:EQe,deqNoDrawCost:CQe,deqFastCost:SQe,deq:o(function(e,r,n){return e.dequeue(r,n)},"deq"),onDeqd:o(function(e,r){for(var n=0;n=RQe||r>M6)return null}n.validateLayersElesOrdering(r,t);var u=n.layersByLevel,h=Math.pow(2,r),f=u[r]=u[r]||[],d,p=n.levelIsComplete(r,t),m,g=o(function(){var M=o(function(O){if(n.validateLayersElesOrdering(O,t),n.levelIsComplete(O,t))return m=u[O],!0},"canUseAsTmpLvl"),N=o(function(O){if(!m)for(var D=r+O;zx<=D&&D<=M6&&!M(D);D+=O);},"checkLvls");N(1),N(-1);for(var k=f.length-1;k>=0;k--){var I=f[k];I.invalid&&Af(f,I)}},"checkTempLevels");if(!p)g();else return f;var y=o(function(){if(!d){d=Gs();for(var M=0;MzQe)return null;var I=n.makeLayer(d,r);if(N!=null){var C=f.indexOf(N)+1;f.splice(C,0,I)}else(M.insert===void 0||M.insert)&&f.unshift(I);return I},"makeLayer");if(n.skipping&&!l)return null;for(var x=null,b=t.length/DQe,w=!l,S=0;S=b||!Upe(x.bb,T.boundingBox()))&&(x=v({insert:!0,after:x}),!x))return null;m||w?n.queueLayer(x,T):n.drawEleInLayer(x,T,r,e),x.eles.push(T),_[r]=x}return m||(w?null:f)};Aa.getEleLevelForLayerLevel=function(t,e){return t};Aa.drawEleInLayer=function(t,e,r,n){var i=this,a=this.renderer,s=t.context,l=e.boundingBox();l.w===0||l.h===0||!e.visible()||(r=i.getEleLevelForLayerLevel(r,n),a.setImgSmoothing(s,!1),a.drawCachedElement(s,e,null,null,r,GQe),a.setImgSmoothing(s,!0))};Aa.levelIsComplete=function(t,e){var r=this,n=r.layersByLevel[t];if(!n||n.length===0)return!1;for(var i=0,a=0;a0||s.invalid)return!1;i+=s.eles.length}return i===e.length};Aa.validateLayersElesOrdering=function(t,e){var r=this.layersByLevel[t];if(r)for(var n=0;n0){e=!0;break}}return e};Aa.invalidateElements=function(t){var e=this;t.length!==0&&(e.lastInvalidationTime=Ku(),!(t.length===0||!e.haveLayers())&&e.updateElementsInLayers(t,o(function(n,i,a){e.invalidateLayer(n)},"invalAssocLayers")))};Aa.invalidateLayer=function(t){if(this.lastInvalidationTime=Ku(),!t.invalid){var e=t.level,r=t.eles,n=this.layersByLevel[e];Af(n,t),t.elesQueue=[],t.invalid=!0,t.replacement&&(t.replacement.invalid=!0);for(var i=0;i3&&arguments[3]!==void 0?arguments[3]:!0,i=arguments.length>4&&arguments[4]!==void 0?arguments[4]:!0,a=arguments.length>5&&arguments[5]!==void 0?arguments[5]:!0,s=this,l=e._private.rscratch;if(!(a&&!e.visible())&&!(l.badLine||l.allpts==null||isNaN(l.allpts[0]))){var u;r&&(u=r,t.translate(-u.x1,-u.y1));var h=a?e.pstyle("opacity").value:1,f=a?e.pstyle("line-opacity").value:1,d=e.pstyle("curve-style").value,p=e.pstyle("line-style").value,m=e.pstyle("width").pfValue,g=e.pstyle("line-cap").value,y=e.pstyle("line-outline-width").value,v=e.pstyle("line-outline-color").value,x=h*f,b=h*f,w=o(function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:x;d==="straight-triangle"?(s.eleStrokeStyle(t,e,O),s.drawEdgeTrianglePath(e,t,l.allpts)):(t.lineWidth=m,t.lineCap=g,s.eleStrokeStyle(t,e,O),s.drawEdgePath(e,t,l.allpts,p),t.lineCap="butt")},"drawLine"),S=o(function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:x;if(t.lineWidth=m+y,t.lineCap=g,y>0)s.colorStrokeStyle(t,v[0],v[1],v[2],O);else{t.lineCap="butt";return}d==="straight-triangle"?s.drawEdgeTrianglePath(e,t,l.allpts):(s.drawEdgePath(e,t,l.allpts,p),t.lineCap="butt")},"drawLineOutline"),T=o(function(){i&&s.drawEdgeOverlay(t,e)},"drawOverlay"),E=o(function(){i&&s.drawEdgeUnderlay(t,e)},"drawUnderlay"),_=o(function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:b;s.drawArrowheads(t,e,O)},"drawArrows"),A=o(function(){s.drawElementText(t,e,null,n)},"drawText");t.lineJoin="round";var L=e.pstyle("ghost").value==="yes";if(L){var M=e.pstyle("ghost-offset-x").pfValue,N=e.pstyle("ghost-offset-y").pfValue,k=e.pstyle("ghost-opacity").value,I=x*k;t.translate(M,N),w(I),_(I),t.translate(-M,-N)}else S();E(),w(),_(),T(),A(),r&&t.translate(u.x1,u.y1)}};Xme=o(function(e){if(!["overlay","underlay"].includes(e))throw new Error("Invalid state");return function(r,n){if(n.visible()){var i=n.pstyle("".concat(e,"-opacity")).value;if(i!==0){var a=this,s=a.usePaths(),l=n._private.rscratch,u=n.pstyle("".concat(e,"-padding")).pfValue,h=2*u,f=n.pstyle("".concat(e,"-color")).value;r.lineWidth=h,l.edgeType==="self"&&!s?r.lineCap="butt":r.lineCap="round",a.colorStrokeStyle(r,f[0],f[1],f[2],i),a.drawEdgePath(n,r,l.allpts,"solid")}}}},"drawEdgeOverlayUnderlay");eh.drawEdgeOverlay=Xme("overlay");eh.drawEdgeUnderlay=Xme("underlay");eh.drawEdgePath=function(t,e,r,n){var i=t._private.rscratch,a=e,s,l=!1,u=this.usePaths(),h=t.pstyle("line-dash-pattern").pfValue,f=t.pstyle("line-dash-offset").pfValue;if(u){var d=r.join("$"),p=i.pathCacheKey&&i.pathCacheKey===d;p?(s=e=i.pathCache,l=!0):(s=e=new Path2D,i.pathCacheKey=d,i.pathCache=s)}if(a.setLineDash)switch(n){case"dotted":a.setLineDash([1,1]);break;case"dashed":a.setLineDash(h),a.lineDashOffset=f;break;case"solid":a.setLineDash([]);break}if(!l&&!i.badLine)switch(e.beginPath&&e.beginPath(),e.moveTo(r[0],r[1]),i.edgeType){case"bezier":case"self":case"compound":case"multibezier":for(var m=2;m+35&&arguments[5]!==void 0?arguments[5]:!0,s=this;if(n==null){if(a&&!s.eleTextBiggerThanMin(e))return}else if(n===!1)return;if(e.isNode()){var l=e.pstyle("label");if(!l||!l.value)return;var u=s.getLabelJustification(e);t.textAlign=u,t.textBaseline="bottom"}else{var h=e.element()._private.rscratch.badLine,f=e.pstyle("label"),d=e.pstyle("source-label"),p=e.pstyle("target-label");if(h||(!f||!f.value)&&(!d||!d.value)&&(!p||!p.value))return;t.textAlign="center",t.textBaseline="bottom"}var m=!r,g;r&&(g=r,t.translate(-g.x1,-g.y1)),i==null?(s.drawText(t,e,null,m,a),e.isEdge()&&(s.drawText(t,e,"source",m,a),s.drawText(t,e,"target",m,a))):s.drawText(t,e,i,m,a),r&&t.translate(g.x1,g.y1)};K0.getFontCache=function(t){var e;this.fontCaches=this.fontCaches||[];for(var r=0;r2&&arguments[2]!==void 0?arguments[2]:!0,n=e.pstyle("font-style").strValue,i=e.pstyle("font-size").pfValue+"px",a=e.pstyle("font-family").strValue,s=e.pstyle("font-weight").strValue,l=r?e.effectiveOpacity()*e.pstyle("text-opacity").value:1,u=e.pstyle("text-outline-opacity").value*l,h=e.pstyle("color").value,f=e.pstyle("text-outline-color").value;t.font=n+" "+s+" "+i+" "+a,t.lineJoin="round",this.colorFillStyle(t,h[0],h[1],h[2],l),this.colorStrokeStyle(t,f[0],f[1],f[2],u)};o(RP,"roundRect");K0.getTextAngle=function(t,e){var r,n=t._private,i=n.rscratch,a=e?e+"-":"",s=t.pstyle(a+"text-rotation"),l=Ul(i,"labelAngle",e);return s.strValue==="autorotate"?r=t.isEdge()?l:0:s.strValue==="none"?r=0:r=s.pfValue,r};K0.drawText=function(t,e,r){var n=arguments.length>3&&arguments[3]!==void 0?arguments[3]:!0,i=arguments.length>4&&arguments[4]!==void 0?arguments[4]:!0,a=e._private,s=a.rscratch,l=i?e.effectiveOpacity():1;if(!(i&&(l===0||e.pstyle("text-opacity").value===0))){r==="main"&&(r=null);var u=Ul(s,"labelX",r),h=Ul(s,"labelY",r),f,d,p=this.getLabelText(e,r);if(p!=null&&p!==""&&!isNaN(u)&&!isNaN(h)){this.setupTextStyle(t,e,i);var m=r?r+"-":"",g=Ul(s,"labelWidth",r),y=Ul(s,"labelHeight",r),v=e.pstyle(m+"text-margin-x").pfValue,x=e.pstyle(m+"text-margin-y").pfValue,b=e.isEdge(),w=e.pstyle("text-halign").value,S=e.pstyle("text-valign").value;b&&(w="center",S="center"),u+=v,h+=x;var T;switch(n?T=this.getTextAngle(e,r):T=0,T!==0&&(f=u,d=h,t.translate(f,d),t.rotate(T),u=0,h=0),S){case"top":break;case"center":h+=y/2;break;case"bottom":h+=y;break}var E=e.pstyle("text-background-opacity").value,_=e.pstyle("text-border-opacity").value,A=e.pstyle("text-border-width").pfValue,L=e.pstyle("text-background-padding").pfValue,M=e.pstyle("text-background-shape").strValue,N=M.indexOf("round")===0,k=2;if(E>0||A>0&&_>0){var I=u-L;switch(w){case"left":I-=g;break;case"center":I-=g/2;break}var C=h-y-L,O=g+2*L,D=y+2*L;if(E>0){var P=t.fillStyle,F=e.pstyle("text-background-color").value;t.fillStyle="rgba("+F[0]+","+F[1]+","+F[2]+","+E*l+")",N?RP(t,I,C,O,D,k):t.fillRect(I,C,O,D),t.fillStyle=P}if(A>0&&_>0){var B=t.strokeStyle,$=t.lineWidth,z=e.pstyle("text-border-color").value,Y=e.pstyle("text-border-style").value;if(t.strokeStyle="rgba("+z[0]+","+z[1]+","+z[2]+","+_*l+")",t.lineWidth=A,t.setLineDash)switch(Y){case"dotted":t.setLineDash([1,1]);break;case"dashed":t.setLineDash([4,2]);break;case"double":t.lineWidth=A/4,t.setLineDash([]);break;case"solid":t.setLineDash([]);break}if(N?RP(t,I,C,O,D,k,"stroke"):t.strokeRect(I,C,O,D),Y==="double"){var Q=A/2;N?RP(t,I+Q,C+Q,O-Q*2,D-Q*2,k,"stroke"):t.strokeRect(I+Q,C+Q,O-Q*2,D-Q*2)}t.setLineDash&&t.setLineDash([]),t.lineWidth=$,t.strokeStyle=B}}var X=2*e.pstyle("text-outline-width").pfValue;if(X>0&&(t.lineWidth=X),e.pstyle("text-wrap").value==="wrap"){var ie=Ul(s,"labelWrapCachedLines",r),j=Ul(s,"labelLineHeight",r),J=g/2,Z=this.getLabelJustification(e);switch(Z==="auto"||(w==="left"?Z==="left"?u+=-g:Z==="center"&&(u+=-J):w==="center"?Z==="left"?u+=-J:Z==="right"&&(u+=J):w==="right"&&(Z==="center"?u+=J:Z==="right"&&(u+=g))),S){case"top":h-=(ie.length-1)*j;break;case"center":case"bottom":h-=(ie.length-1)*j;break}for(var H=0;H0&&t.strokeText(ie[H],u,h),t.fillText(ie[H],u,h),h+=j}else X>0&&t.strokeText(p,u,h),t.fillText(p,u,h);T!==0&&(t.rotate(-T),t.translate(-f,-d))}}};v1={};v1.drawNode=function(t,e,r){var n=arguments.length>3&&arguments[3]!==void 0?arguments[3]:!0,i=arguments.length>4&&arguments[4]!==void 0?arguments[4]:!0,a=arguments.length>5&&arguments[5]!==void 0?arguments[5]:!0,s=this,l,u,h=e._private,f=h.rscratch,d=e.position();if(!(!ft(d.x)||!ft(d.y))&&!(a&&!e.visible())){var p=a?e.effectiveOpacity():1,m=s.usePaths(),g,y=!1,v=e.padding();l=e.width()+2*v,u=e.height()+2*v;var x;r&&(x=r,t.translate(-x.x1,-x.y1));for(var b=e.pstyle("background-image"),w=b.value,S=new Array(w.length),T=new Array(w.length),E=0,_=0;_0&&arguments[0]!==void 0?arguments[0]:I;s.eleFillStyle(t,e,ne)},"setupShapeColor"),H=o(function(){var ne=arguments.length>0&&arguments[0]!==void 0?arguments[0]:z;s.colorStrokeStyle(t,C[0],C[1],C[2],ne)},"setupBorderColor"),q=o(function(){var ne=arguments.length>0&&arguments[0]!==void 0?arguments[0]:ie;s.colorStrokeStyle(t,Q[0],Q[1],Q[2],ne)},"setupOutlineColor"),K=o(function(ne,ae,we,Te){var Ce=s.nodePathCache=s.nodePathCache||[],Ae=Ope(we==="polygon"?we+","+Te.join(","):we,""+ae,""+ne,""+J),Ge=Ce[Ae],Me,ye=!1;return Ge!=null?(Me=Ge,ye=!0,f.pathCache=Me):(Me=new Path2D,Ce[Ae]=f.pathCache=Me),{path:Me,cacheHit:ye}},"getPath"),se=e.pstyle("shape").strValue,ce=e.pstyle("shape-polygon-points").pfValue;if(m){t.translate(d.x,d.y);var ue=K(l,u,se,ce);g=ue.path,y=ue.cacheHit}var te=o(function(){if(!y){var ne=d;m&&(ne={x:0,y:0}),s.nodeShapes[s.getNodeShape(e)].draw(g||t,ne.x,ne.y,l,u,J,f)}m?t.fill(g):t.fill()},"drawShape"),De=o(function(){for(var ne=arguments.length>0&&arguments[0]!==void 0?arguments[0]:p,ae=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0,we=h.backgrounding,Te=0,Ce=0;Ce0&&arguments[0]!==void 0?arguments[0]:!1,ae=arguments.length>1&&arguments[1]!==void 0?arguments[1]:p;s.hasPie(e)&&(s.drawPie(t,e,ae),ne&&(m||s.nodeShapes[s.getNodeShape(e)].draw(t,d.x,d.y,l,u,J,f)))},"drawPie"),ke=o(function(){var ne=arguments.length>0&&arguments[0]!==void 0?arguments[0]:p,ae=(N>0?N:-N)*ne,we=N>0?0:255;N!==0&&(s.colorFillStyle(t,we,we,we,ae),m?t.fill(g):t.fill())},"darken"),Ie=o(function(){if(k>0){if(t.lineWidth=k,t.lineCap=P,t.lineJoin=D,t.setLineDash)switch(O){case"dotted":t.setLineDash([1,1]);break;case"dashed":t.setLineDash(B),t.lineDashOffset=$;break;case"solid":case"double":t.setLineDash([]);break}if(F!=="center"){if(t.save(),t.lineWidth*=2,F==="inside")m?t.clip(g):t.clip();else{var ne=new Path2D;ne.rect(-l/2-k,-u/2-k,l+2*k,u+2*k),ne.addPath(g),t.clip(ne,"evenodd")}m?t.stroke(g):t.stroke(),t.restore()}else m?t.stroke(g):t.stroke();if(O==="double"){t.lineWidth=k/3;var ae=t.globalCompositeOperation;t.globalCompositeOperation="destination-out",m?t.stroke(g):t.stroke(),t.globalCompositeOperation=ae}t.setLineDash&&t.setLineDash([])}},"drawBorder"),Se=o(function(){if(Y>0){if(t.lineWidth=Y,t.lineCap="butt",t.setLineDash)switch(X){case"dotted":t.setLineDash([1,1]);break;case"dashed":t.setLineDash([4,2]);break;case"solid":case"double":t.setLineDash([]);break}var ne=d;m&&(ne={x:0,y:0});var ae=s.getNodeShape(e),we=k;F==="inside"&&(we=0),F==="outside"&&(we*=2);var Te=(l+we+(Y+j))/l,Ce=(u+we+(Y+j))/u,Ae=l*Te,Ge=u*Ce,Me=s.nodeShapes[ae].points,ye;if(m){var He=K(Ae,Ge,ae,Me);ye=He.path}if(ae==="ellipse")s.drawEllipsePath(ye||t,ne.x,ne.y,Ae,Ge);else if(["round-diamond","round-heptagon","round-hexagon","round-octagon","round-pentagon","round-polygon","round-triangle","round-tag"].includes(ae)){var ze=0,Ze=0,gt=0;ae==="round-diamond"?ze=(we+j+Y)*1.4:ae==="round-heptagon"?(ze=(we+j+Y)*1.075,gt=-(we/2+j+Y)/35):ae==="round-hexagon"?ze=(we+j+Y)*1.12:ae==="round-pentagon"?(ze=(we+j+Y)*1.13,gt=-(we/2+j+Y)/15):ae==="round-tag"?(ze=(we+j+Y)*1.12,Ze=(we/2+Y+j)*.07):ae==="round-triangle"&&(ze=(we+j+Y)*(Math.PI/2),gt=-(we+j/2+Y)/Math.PI),ze!==0&&(Te=(l+ze)/l,Ae=l*Te,["round-hexagon","round-tag"].includes(ae)||(Ce=(u+ze)/u,Ge=u*Ce)),J=J==="auto"?Wpe(Ae,Ge):J;for(var yt=Ae/2,tt=Ge/2,Ye=J+(we+Y+j)/2,Je=new Array(Me.length/2),Ve=new Array(Me.length/2),je=0;je0){if(i=i||n.position(),a==null||s==null){var m=n.padding();a=n.width()+2*m,s=n.height()+2*m}l.colorFillStyle(r,f[0],f[1],f[2],h),l.nodeShapes[d].draw(r,i.x,i.y,a+u*2,s+u*2,p),r.fill()}}}},"drawNodeOverlayUnderlay");v1.drawNodeOverlay=jme("overlay");v1.drawNodeUnderlay=jme("underlay");v1.hasPie=function(t){return t=t[0],t._private.hasPie};v1.drawPie=function(t,e,r,n){e=e[0],n=n||e.position();var i=e.cy().style(),a=e.pstyle("pie-size"),s=n.x,l=n.y,u=e.width(),h=e.height(),f=Math.min(u,h)/2,d=0,p=this.usePaths();p&&(s=0,l=0),a.units==="%"?f=f*a.pfValue:a.pfValue!==void 0&&(f=a.pfValue/2);for(var m=1;m<=i.pieBackgroundN;m++){var g=e.pstyle("pie-"+m+"-background-size").value,y=e.pstyle("pie-"+m+"-background-color").value,v=e.pstyle("pie-"+m+"-background-opacity").value*r,x=g/100;x+d>1&&(x=1-d);var b=1.5*Math.PI+2*Math.PI*d,w=2*Math.PI*x,S=b+w;g===0||d>=1||d+x>1||(t.beginPath(),t.moveTo(s,l),t.arc(s,l,f,b,S),t.closePath(),this.colorFillStyle(t,y[0],y[1],y[2],v),t.fill(),d+=x)}};bo={},QQe=100;bo.getPixelRatio=function(){var t=this.data.contexts[0];if(this.forcedPixelRatio!=null)return this.forcedPixelRatio;var e=this.cy.window(),r=t.backingStorePixelRatio||t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1;return(e.devicePixelRatio||1)/r};bo.paintCache=function(t){for(var e=this.paintCaches=this.paintCaches||[],r=!0,n,i=0;is.minMbLowQualFrames&&(s.motionBlurPxRatio=s.mbPxRBlurry)),s.clearingMotionBlur&&(s.motionBlurPxRatio=1),s.textureDrawLastFrame&&!d&&(f[s.NODE]=!0,f[s.SELECT_BOX]=!0);var b=u.style(),w=u.zoom(),S=i!==void 0?i:w,T=u.pan(),E={x:T.x,y:T.y},_={zoom:w,pan:{x:T.x,y:T.y}},A=s.prevViewport,L=A===void 0||_.zoom!==A.zoom||_.pan.x!==A.pan.x||_.pan.y!==A.pan.y;!L&&!(y&&!g)&&(s.motionBlurPxRatio=1),a&&(E=a),S*=l,E.x*=l,E.y*=l;var M=s.getCachedZSortedEles();function N(ue,te,De,oe,ke){var Ie=ue.globalCompositeOperation;ue.globalCompositeOperation="destination-out",s.colorFillStyle(ue,255,255,255,s.motionBlurTransparency),ue.fillRect(te,De,oe,ke),ue.globalCompositeOperation=Ie}o(N,"mbclear");function k(ue,te){var De,oe,ke,Ie;!s.clearingMotionBlur&&(ue===h.bufferContexts[s.MOTIONBLUR_BUFFER_NODE]||ue===h.bufferContexts[s.MOTIONBLUR_BUFFER_DRAG])?(De={x:T.x*m,y:T.y*m},oe=w*m,ke=s.canvasWidth*m,Ie=s.canvasHeight*m):(De=E,oe=S,ke=s.canvasWidth,Ie=s.canvasHeight),ue.setTransform(1,0,0,1,0,0),te==="motionBlur"?N(ue,0,0,ke,Ie):!e&&(te===void 0||te)&&ue.clearRect(0,0,ke,Ie),r||(ue.translate(De.x,De.y),ue.scale(oe,oe)),a&&ue.translate(a.x,a.y),i&&ue.scale(i,i)}if(o(k,"setContextTransform"),d||(s.textureDrawLastFrame=!1),d){if(s.textureDrawLastFrame=!0,!s.textureCache){s.textureCache={},s.textureCache.bb=u.mutableElements().boundingBox(),s.textureCache.texture=s.data.bufferCanvases[s.TEXTURE_BUFFER];var I=s.data.bufferContexts[s.TEXTURE_BUFFER];I.setTransform(1,0,0,1,0,0),I.clearRect(0,0,s.canvasWidth*s.textureMult,s.canvasHeight*s.textureMult),s.render({forcedContext:I,drawOnlyNodeLayer:!0,forcedPxRatio:l*s.textureMult});var _=s.textureCache.viewport={zoom:u.zoom(),pan:u.pan(),width:s.canvasWidth,height:s.canvasHeight};_.mpan={x:(0-_.pan.x)/_.zoom,y:(0-_.pan.y)/_.zoom}}f[s.DRAG]=!1,f[s.NODE]=!1;var C=h.contexts[s.NODE],O=s.textureCache.texture,_=s.textureCache.viewport;C.setTransform(1,0,0,1,0,0),p?N(C,0,0,_.width,_.height):C.clearRect(0,0,_.width,_.height);var D=b.core("outside-texture-bg-color").value,P=b.core("outside-texture-bg-opacity").value;s.colorFillStyle(C,D[0],D[1],D[2],P),C.fillRect(0,0,_.width,_.height);var w=u.zoom();k(C,!1),C.clearRect(_.mpan.x,_.mpan.y,_.width/_.zoom/l,_.height/_.zoom/l),C.drawImage(O,_.mpan.x,_.mpan.y,_.width/_.zoom/l,_.height/_.zoom/l)}else s.textureOnViewport&&!e&&(s.textureCache=null);var F=u.extent(),B=s.pinching||s.hoverData.dragging||s.swipePanning||s.data.wheelZooming||s.hoverData.draggingEles||s.cy.animated(),$=s.hideEdgesOnViewport&&B,z=[];if(z[s.NODE]=!f[s.NODE]&&p&&!s.clearedForMotionBlur[s.NODE]||s.clearingMotionBlur,z[s.NODE]&&(s.clearedForMotionBlur[s.NODE]=!0),z[s.DRAG]=!f[s.DRAG]&&p&&!s.clearedForMotionBlur[s.DRAG]||s.clearingMotionBlur,z[s.DRAG]&&(s.clearedForMotionBlur[s.DRAG]=!0),f[s.NODE]||r||n||z[s.NODE]){var Y=p&&!z[s.NODE]&&m!==1,C=e||(Y?s.data.bufferContexts[s.MOTIONBLUR_BUFFER_NODE]:h.contexts[s.NODE]),Q=p&&!Y?"motionBlur":void 0;k(C,Q),$?s.drawCachedNodes(C,M.nondrag,l,F):s.drawLayeredElements(C,M.nondrag,l,F),s.debug&&s.drawDebugPoints(C,M.nondrag),!r&&!p&&(f[s.NODE]=!1)}if(!n&&(f[s.DRAG]||r||z[s.DRAG])){var Y=p&&!z[s.DRAG]&&m!==1,C=e||(Y?s.data.bufferContexts[s.MOTIONBLUR_BUFFER_DRAG]:h.contexts[s.DRAG]);k(C,p&&!Y?"motionBlur":void 0),$?s.drawCachedNodes(C,M.drag,l,F):s.drawCachedElements(C,M.drag,l,F),s.debug&&s.drawDebugPoints(C,M.drag),!r&&!p&&(f[s.DRAG]=!1)}if(s.showFps||!n&&f[s.SELECT_BOX]&&!r){var C=e||h.contexts[s.SELECT_BOX];if(k(C),s.selection[4]==1&&(s.hoverData.selecting||s.touchData.selecting)){var w=s.cy.zoom(),X=b.core("selection-box-border-width").value/w;C.lineWidth=X,C.fillStyle="rgba("+b.core("selection-box-color").value[0]+","+b.core("selection-box-color").value[1]+","+b.core("selection-box-color").value[2]+","+b.core("selection-box-opacity").value+")",C.fillRect(s.selection[0],s.selection[1],s.selection[2]-s.selection[0],s.selection[3]-s.selection[1]),X>0&&(C.strokeStyle="rgba("+b.core("selection-box-border-color").value[0]+","+b.core("selection-box-border-color").value[1]+","+b.core("selection-box-border-color").value[2]+","+b.core("selection-box-opacity").value+")",C.strokeRect(s.selection[0],s.selection[1],s.selection[2]-s.selection[0],s.selection[3]-s.selection[1]))}if(h.bgActivePosistion&&!s.hoverData.selecting){var w=s.cy.zoom(),ie=h.bgActivePosistion;C.fillStyle="rgba("+b.core("active-bg-color").value[0]+","+b.core("active-bg-color").value[1]+","+b.core("active-bg-color").value[2]+","+b.core("active-bg-opacity").value+")",C.beginPath(),C.arc(ie.x,ie.y,b.core("active-bg-size").pfValue/w,0,2*Math.PI),C.fill()}var j=s.lastRedrawTime;if(s.showFps&&j){j=Math.round(j);var J=Math.round(1e3/j);C.setTransform(1,0,0,1,0,0),C.fillStyle="rgba(255, 0, 0, 0.75)",C.strokeStyle="rgba(255, 0, 0, 0.75)",C.lineWidth=1,C.fillText("1 frame = "+j+" ms = "+J+" fps",0,20);var Z=60;C.strokeRect(0,30,250,20),C.fillRect(0,30,250*Math.min(J/Z,1),20)}r||(f[s.SELECT_BOX]=!1)}if(p&&m!==1){var H=h.contexts[s.NODE],q=s.data.bufferCanvases[s.MOTIONBLUR_BUFFER_NODE],K=h.contexts[s.DRAG],se=s.data.bufferCanvases[s.MOTIONBLUR_BUFFER_DRAG],ce=o(function(te,De,oe){te.setTransform(1,0,0,1,0,0),oe||!x?te.clearRect(0,0,s.canvasWidth,s.canvasHeight):N(te,0,0,s.canvasWidth,s.canvasHeight);var ke=m;te.drawImage(De,0,0,s.canvasWidth*ke,s.canvasHeight*ke,0,0,s.canvasWidth,s.canvasHeight)},"drawMotionBlur");(f[s.NODE]||z[s.NODE])&&(ce(H,q,z[s.NODE]),f[s.NODE]=!1),(f[s.DRAG]||z[s.DRAG])&&(ce(K,se,z[s.DRAG]),f[s.DRAG]=!1)}s.prevViewport=_,s.clearingMotionBlur&&(s.clearingMotionBlur=!1,s.motionBlurCleared=!0,s.motionBlur=!0),p&&(s.motionBlurTimeout=setTimeout(function(){s.motionBlurTimeout=null,s.clearedForMotionBlur[s.NODE]=!1,s.clearedForMotionBlur[s.DRAG]=!1,s.motionBlur=!1,s.clearingMotionBlur=!d,s.mbFrames=0,f[s.NODE]=!0,f[s.DRAG]=!0,s.redraw()},QQe)),e||u.emit("render")};Nf={};Nf.drawPolygonPath=function(t,e,r,n,i,a){var s=n/2,l=i/2;t.beginPath&&t.beginPath(),t.moveTo(e+s*a[0],r+l*a[1]);for(var u=1;u0&&s>0){m.clearRect(0,0,a,s),m.globalCompositeOperation="source-over";var g=this.getCachedZSortedEles();if(t.full)m.translate(-n.x1*h,-n.y1*h),m.scale(h,h),this.drawElements(m,g),m.scale(1/h,1/h),m.translate(n.x1*h,n.y1*h);else{var y=e.pan(),v={x:y.x*h,y:y.y*h};h*=e.zoom(),m.translate(v.x,v.y),m.scale(h,h),this.drawElements(m,g),m.scale(1/h,1/h),m.translate(-v.x,-v.y)}t.bg&&(m.globalCompositeOperation="destination-over",m.fillStyle=t.bg,m.rect(0,0,a,s),m.fill())}return p};o(ZQe,"b64ToBlob");o(xpe,"b64UriToB64");o(Qme,"output");ab.png=function(t){return Qme(t,this.bufferCanvasImage(t),"image/png")};ab.jpg=function(t){return Qme(t,this.bufferCanvasImage(t),"image/jpeg")};Zme={};Zme.nodeShapeImpl=function(t,e,r,n,i,a,s,l){switch(t){case"ellipse":return this.drawEllipsePath(e,r,n,i,a);case"polygon":return this.drawPolygonPath(e,r,n,i,a,s);case"round-polygon":return this.drawRoundPolygonPath(e,r,n,i,a,s,l);case"roundrectangle":case"round-rectangle":return this.drawRoundRectanglePath(e,r,n,i,a,l);case"cutrectangle":case"cut-rectangle":return this.drawCutRectanglePath(e,r,n,i,a,s,l);case"bottomroundrectangle":case"bottom-round-rectangle":return this.drawBottomRoundRectanglePath(e,r,n,i,a,l);case"barrel":return this.drawBarrelPath(e,r,n,i,a)}};JQe=Jme,Yr=Jme.prototype;Yr.CANVAS_LAYERS=3;Yr.SELECT_BOX=0;Yr.DRAG=1;Yr.NODE=2;Yr.BUFFER_COUNT=3;Yr.TEXTURE_BUFFER=0;Yr.MOTIONBLUR_BUFFER_NODE=1;Yr.MOTIONBLUR_BUFFER_DRAG=2;o(Jme,"CanvasRenderer");Yr.redrawHint=function(t,e){var r=this;switch(t){case"eles":r.data.canvasNeedsRedraw[Yr.NODE]=e;break;case"drag":r.data.canvasNeedsRedraw[Yr.DRAG]=e;break;case"select":r.data.canvasNeedsRedraw[Yr.SELECT_BOX]=e;break}};eZe=typeof Path2D<"u";Yr.path2dEnabled=function(t){if(t===void 0)return this.pathsEnabled;this.pathsEnabled=!!t};Yr.usePaths=function(){return eZe&&this.pathsEnabled};Yr.setImgSmoothing=function(t,e){t.imageSmoothingEnabled!=null?t.imageSmoothingEnabled=e:(t.webkitImageSmoothingEnabled=e,t.mozImageSmoothingEnabled=e,t.msImageSmoothingEnabled=e)};Yr.getImgSmoothing=function(t){return t.imageSmoothingEnabled!=null?t.imageSmoothingEnabled:t.webkitImageSmoothingEnabled||t.mozImageSmoothingEnabled||t.msImageSmoothingEnabled};Yr.makeOffscreenCanvas=function(t,e){var r;if((typeof OffscreenCanvas>"u"?"undefined":Hi(OffscreenCanvas))!=="undefined")r=new OffscreenCanvas(t,e);else{var n=this.cy.window(),i=n.document;r=i.createElement("canvas"),r.width=t,r.height=e}return r};[qme,Yc,eh,yB,K0,v1,bo,Nf,ab,Zme].forEach(function(t){Wt(Yr,t)});tZe=[{name:"null",impl:Ime},{name:"base",impl:Hme},{name:"canvas",impl:JQe}],rZe=[{type:"layout",extensions:lQe},{type:"renderer",extensions:tZe}],ege={},tge={};o(rge,"setExtension");o(nge,"getExtension");o(nZe,"setModule");o(iZe,"getModule");qP=o(function(){if(arguments.length===2)return nge.apply(null,arguments);if(arguments.length===3)return rge.apply(null,arguments);if(arguments.length===4)return iZe.apply(null,arguments);if(arguments.length===5)return nZe.apply(null,arguments);oi("Invalid extension access syntax")},"extension");Kx.prototype.extension=qP;rZe.forEach(function(t){t.extensions.forEach(function(e){rge(t.type,e.name,e.impl)})});ige=o(function t(){if(!(this instanceof t))return new t;this.length=0},"Stylesheet"),X0=ige.prototype;X0.instanceString=function(){return"stylesheet"};X0.selector=function(t){var e=this.length++;return this[e]={selector:t,properties:[]},this};X0.css=function(t,e){var r=this.length-1;if(zt(t))this[r].properties.push({name:t,value:e});else if(Mr(t))for(var n=t,i=Object.keys(n),a=0;a{"use strict";o(function(e,r){typeof sb=="object"&&typeof xB=="object"?xB.exports=r():typeof define=="function"&&define.amd?define([],r):typeof sb=="object"?sb.layoutBase=r():e.layoutBase=r()},"webpackUniversalModuleDefinition")(sb,function(){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var i=e[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}return o(r,"__webpack_require__"),r.m=t,r.c=e,r.i=function(n){return n},r.d=function(n,i,a){r.o(n,i)||Object.defineProperty(n,i,{configurable:!1,enumerable:!0,get:a})},r.n=function(n){var i=n&&n.__esModule?o(function(){return n.default},"getDefault"):o(function(){return n},"getModuleExports");return r.d(i,"a",i),i},r.o=function(n,i){return Object.prototype.hasOwnProperty.call(n,i)},r.p="",r(r.s=26)}([function(t,e,r){"use strict";function n(){}o(n,"LayoutConstants"),n.QUALITY=1,n.DEFAULT_CREATE_BENDS_AS_NEEDED=!1,n.DEFAULT_INCREMENTAL=!1,n.DEFAULT_ANIMATION_ON_LAYOUT=!0,n.DEFAULT_ANIMATION_DURING_LAYOUT=!1,n.DEFAULT_ANIMATION_PERIOD=50,n.DEFAULT_UNIFORM_LEAF_NODE_SIZES=!1,n.DEFAULT_GRAPH_MARGIN=15,n.NODE_DIMENSIONS_INCLUDE_LABELS=!1,n.SIMPLE_NODE_SIZE=40,n.SIMPLE_NODE_HALF_SIZE=n.SIMPLE_NODE_SIZE/2,n.EMPTY_COMPOUND_NODE_SIZE=40,n.MIN_EDGE_LENGTH=1,n.WORLD_BOUNDARY=1e6,n.INITIAL_WORLD_BOUNDARY=n.WORLD_BOUNDARY/1e3,n.WORLD_CENTER_X=1200,n.WORLD_CENTER_Y=900,t.exports=n},function(t,e,r){"use strict";var n=r(2),i=r(8),a=r(9);function s(u,h,f){n.call(this,f),this.isOverlapingSourceAndTarget=!1,this.vGraphObject=f,this.bendpoints=[],this.source=u,this.target=h}o(s,"LEdge"),s.prototype=Object.create(n.prototype);for(var l in n)s[l]=n[l];s.prototype.getSource=function(){return this.source},s.prototype.getTarget=function(){return this.target},s.prototype.isInterGraph=function(){return this.isInterGraph},s.prototype.getLength=function(){return this.length},s.prototype.isOverlapingSourceAndTarget=function(){return this.isOverlapingSourceAndTarget},s.prototype.getBendpoints=function(){return this.bendpoints},s.prototype.getLca=function(){return this.lca},s.prototype.getSourceInLca=function(){return this.sourceInLca},s.prototype.getTargetInLca=function(){return this.targetInLca},s.prototype.getOtherEnd=function(u){if(this.source===u)return this.target;if(this.target===u)return this.source;throw"Node is not incident with this edge"},s.prototype.getOtherEndInGraph=function(u,h){for(var f=this.getOtherEnd(u),d=h.getGraphManager().getRoot();;){if(f.getOwner()==h)return f;if(f.getOwner()==d)break;f=f.getOwner().getParent()}return null},s.prototype.updateLength=function(){var u=new Array(4);this.isOverlapingSourceAndTarget=i.getIntersection(this.target.getRect(),this.source.getRect(),u),this.isOverlapingSourceAndTarget||(this.lengthX=u[0]-u[2],this.lengthY=u[1]-u[3],Math.abs(this.lengthX)<1&&(this.lengthX=a.sign(this.lengthX)),Math.abs(this.lengthY)<1&&(this.lengthY=a.sign(this.lengthY)),this.length=Math.sqrt(this.lengthX*this.lengthX+this.lengthY*this.lengthY))},s.prototype.updateLengthSimple=function(){this.lengthX=this.target.getCenterX()-this.source.getCenterX(),this.lengthY=this.target.getCenterY()-this.source.getCenterY(),Math.abs(this.lengthX)<1&&(this.lengthX=a.sign(this.lengthX)),Math.abs(this.lengthY)<1&&(this.lengthY=a.sign(this.lengthY)),this.length=Math.sqrt(this.lengthX*this.lengthX+this.lengthY*this.lengthY)},t.exports=s},function(t,e,r){"use strict";function n(i){this.vGraphObject=i}o(n,"LGraphObject"),t.exports=n},function(t,e,r){"use strict";var n=r(2),i=r(10),a=r(13),s=r(0),l=r(16),u=r(4);function h(d,p,m,g){m==null&&g==null&&(g=p),n.call(this,g),d.graphManager!=null&&(d=d.graphManager),this.estimatedSize=i.MIN_VALUE,this.inclusionTreeDepth=i.MAX_VALUE,this.vGraphObject=g,this.edges=[],this.graphManager=d,m!=null&&p!=null?this.rect=new a(p.x,p.y,m.width,m.height):this.rect=new a}o(h,"LNode"),h.prototype=Object.create(n.prototype);for(var f in n)h[f]=n[f];h.prototype.getEdges=function(){return this.edges},h.prototype.getChild=function(){return this.child},h.prototype.getOwner=function(){return this.owner},h.prototype.getWidth=function(){return this.rect.width},h.prototype.setWidth=function(d){this.rect.width=d},h.prototype.getHeight=function(){return this.rect.height},h.prototype.setHeight=function(d){this.rect.height=d},h.prototype.getCenterX=function(){return this.rect.x+this.rect.width/2},h.prototype.getCenterY=function(){return this.rect.y+this.rect.height/2},h.prototype.getCenter=function(){return new u(this.rect.x+this.rect.width/2,this.rect.y+this.rect.height/2)},h.prototype.getLocation=function(){return new u(this.rect.x,this.rect.y)},h.prototype.getRect=function(){return this.rect},h.prototype.getDiagonal=function(){return Math.sqrt(this.rect.width*this.rect.width+this.rect.height*this.rect.height)},h.prototype.getHalfTheDiagonal=function(){return Math.sqrt(this.rect.height*this.rect.height+this.rect.width*this.rect.width)/2},h.prototype.setRect=function(d,p){this.rect.x=d.x,this.rect.y=d.y,this.rect.width=p.width,this.rect.height=p.height},h.prototype.setCenter=function(d,p){this.rect.x=d-this.rect.width/2,this.rect.y=p-this.rect.height/2},h.prototype.setLocation=function(d,p){this.rect.x=d,this.rect.y=p},h.prototype.moveBy=function(d,p){this.rect.x+=d,this.rect.y+=p},h.prototype.getEdgeListToNode=function(d){var p=[],m,g=this;return g.edges.forEach(function(y){if(y.target==d){if(y.source!=g)throw"Incorrect edge source!";p.push(y)}}),p},h.prototype.getEdgesBetween=function(d){var p=[],m,g=this;return g.edges.forEach(function(y){if(!(y.source==g||y.target==g))throw"Incorrect edge source and/or target";(y.target==d||y.source==d)&&p.push(y)}),p},h.prototype.getNeighborsList=function(){var d=new Set,p=this;return p.edges.forEach(function(m){if(m.source==p)d.add(m.target);else{if(m.target!=p)throw"Incorrect incidency!";d.add(m.source)}}),d},h.prototype.withChildren=function(){var d=new Set,p,m;if(d.add(this),this.child!=null)for(var g=this.child.getNodes(),y=0;yp&&(this.rect.x-=(this.labelWidth-p)/2,this.setWidth(this.labelWidth)),this.labelHeight>m&&(this.labelPos=="center"?this.rect.y-=(this.labelHeight-m)/2:this.labelPos=="top"&&(this.rect.y-=this.labelHeight-m),this.setHeight(this.labelHeight))}}},h.prototype.getInclusionTreeDepth=function(){if(this.inclusionTreeDepth==i.MAX_VALUE)throw"assert failed";return this.inclusionTreeDepth},h.prototype.transform=function(d){var p=this.rect.x;p>s.WORLD_BOUNDARY?p=s.WORLD_BOUNDARY:p<-s.WORLD_BOUNDARY&&(p=-s.WORLD_BOUNDARY);var m=this.rect.y;m>s.WORLD_BOUNDARY?m=s.WORLD_BOUNDARY:m<-s.WORLD_BOUNDARY&&(m=-s.WORLD_BOUNDARY);var g=new u(p,m),y=d.inverseTransformPoint(g);this.setLocation(y.x,y.y)},h.prototype.getLeft=function(){return this.rect.x},h.prototype.getRight=function(){return this.rect.x+this.rect.width},h.prototype.getTop=function(){return this.rect.y},h.prototype.getBottom=function(){return this.rect.y+this.rect.height},h.prototype.getParent=function(){return this.owner==null?null:this.owner.getParent()},t.exports=h},function(t,e,r){"use strict";function n(i,a){i==null&&a==null?(this.x=0,this.y=0):(this.x=i,this.y=a)}o(n,"PointD"),n.prototype.getX=function(){return this.x},n.prototype.getY=function(){return this.y},n.prototype.setX=function(i){this.x=i},n.prototype.setY=function(i){this.y=i},n.prototype.getDifference=function(i){return new DimensionD(this.x-i.x,this.y-i.y)},n.prototype.getCopy=function(){return new n(this.x,this.y)},n.prototype.translate=function(i){return this.x+=i.width,this.y+=i.height,this},t.exports=n},function(t,e,r){"use strict";var n=r(2),i=r(10),a=r(0),s=r(6),l=r(3),u=r(1),h=r(13),f=r(12),d=r(11);function p(g,y,v){n.call(this,v),this.estimatedSize=i.MIN_VALUE,this.margin=a.DEFAULT_GRAPH_MARGIN,this.edges=[],this.nodes=[],this.isConnected=!1,this.parent=g,y!=null&&y instanceof s?this.graphManager=y:y!=null&&y instanceof Layout&&(this.graphManager=y.graphManager)}o(p,"LGraph"),p.prototype=Object.create(n.prototype);for(var m in n)p[m]=n[m];p.prototype.getNodes=function(){return this.nodes},p.prototype.getEdges=function(){return this.edges},p.prototype.getGraphManager=function(){return this.graphManager},p.prototype.getParent=function(){return this.parent},p.prototype.getLeft=function(){return this.left},p.prototype.getRight=function(){return this.right},p.prototype.getTop=function(){return this.top},p.prototype.getBottom=function(){return this.bottom},p.prototype.isConnected=function(){return this.isConnected},p.prototype.add=function(g,y,v){if(y==null&&v==null){var x=g;if(this.graphManager==null)throw"Graph has no graph mgr!";if(this.getNodes().indexOf(x)>-1)throw"Node already in graph!";return x.owner=this,this.getNodes().push(x),x}else{var b=g;if(!(this.getNodes().indexOf(y)>-1&&this.getNodes().indexOf(v)>-1))throw"Source or target not in graph!";if(!(y.owner==v.owner&&y.owner==this))throw"Both owners must be this graph!";return y.owner!=v.owner?null:(b.source=y,b.target=v,b.isInterGraph=!1,this.getEdges().push(b),y.edges.push(b),v!=y&&v.edges.push(b),b)}},p.prototype.remove=function(g){var y=g;if(g instanceof l){if(y==null)throw"Node is null!";if(!(y.owner!=null&&y.owner==this))throw"Owner graph is invalid!";if(this.graphManager==null)throw"Owner graph manager is invalid!";for(var v=y.edges.slice(),x,b=v.length,w=0;w-1&&E>-1))throw"Source and/or target doesn't know this edge!";x.source.edges.splice(T,1),x.target!=x.source&&x.target.edges.splice(E,1);var S=x.source.owner.getEdges().indexOf(x);if(S==-1)throw"Not in owner's edge list!";x.source.owner.getEdges().splice(S,1)}},p.prototype.updateLeftTop=function(){for(var g=i.MAX_VALUE,y=i.MAX_VALUE,v,x,b,w=this.getNodes(),S=w.length,T=0;Tv&&(g=v),y>x&&(y=x)}return g==i.MAX_VALUE?null:(w[0].getParent().paddingLeft!=null?b=w[0].getParent().paddingLeft:b=this.margin,this.left=y-b,this.top=g-b,new f(this.left,this.top))},p.prototype.updateBounds=function(g){for(var y=i.MAX_VALUE,v=-i.MAX_VALUE,x=i.MAX_VALUE,b=-i.MAX_VALUE,w,S,T,E,_,A=this.nodes,L=A.length,M=0;Mw&&(y=w),vT&&(x=T),bw&&(y=w),vT&&(x=T),b=this.nodes.length){var L=0;v.forEach(function(M){M.owner==g&&L++}),L==this.nodes.length&&(this.isConnected=!0)}},t.exports=p},function(t,e,r){"use strict";var n,i=r(1);function a(s){n=r(5),this.layout=s,this.graphs=[],this.edges=[]}o(a,"LGraphManager"),a.prototype.addRoot=function(){var s=this.layout.newGraph(),l=this.layout.newNode(null),u=this.add(s,l);return this.setRootGraph(u),this.rootGraph},a.prototype.add=function(s,l,u,h,f){if(u==null&&h==null&&f==null){if(s==null)throw"Graph is null!";if(l==null)throw"Parent node is null!";if(this.graphs.indexOf(s)>-1)throw"Graph already in this graph mgr!";if(this.graphs.push(s),s.parent!=null)throw"Already has a parent!";if(l.child!=null)throw"Already has a child!";return s.parent=l,l.child=s,s}else{f=u,h=l,u=s;var d=h.getOwner(),p=f.getOwner();if(!(d!=null&&d.getGraphManager()==this))throw"Source not in this graph mgr!";if(!(p!=null&&p.getGraphManager()==this))throw"Target not in this graph mgr!";if(d==p)return u.isInterGraph=!1,d.add(u,h,f);if(u.isInterGraph=!0,u.source=h,u.target=f,this.edges.indexOf(u)>-1)throw"Edge already in inter-graph edge list!";if(this.edges.push(u),!(u.source!=null&&u.target!=null))throw"Edge source and/or target is null!";if(!(u.source.edges.indexOf(u)==-1&&u.target.edges.indexOf(u)==-1))throw"Edge already in source and/or target incidency list!";return u.source.edges.push(u),u.target.edges.push(u),u}},a.prototype.remove=function(s){if(s instanceof n){var l=s;if(l.getGraphManager()!=this)throw"Graph not in this graph mgr";if(!(l==this.rootGraph||l.parent!=null&&l.parent.graphManager==this))throw"Invalid parent node!";var u=[];u=u.concat(l.getEdges());for(var h,f=u.length,d=0;d=s.getRight()?l[0]+=Math.min(s.getX()-a.getX(),a.getRight()-s.getRight()):s.getX()<=a.getX()&&s.getRight()>=a.getRight()&&(l[0]+=Math.min(a.getX()-s.getX(),s.getRight()-a.getRight())),a.getY()<=s.getY()&&a.getBottom()>=s.getBottom()?l[1]+=Math.min(s.getY()-a.getY(),a.getBottom()-s.getBottom()):s.getY()<=a.getY()&&s.getBottom()>=a.getBottom()&&(l[1]+=Math.min(a.getY()-s.getY(),s.getBottom()-a.getBottom()));var f=Math.abs((s.getCenterY()-a.getCenterY())/(s.getCenterX()-a.getCenterX()));s.getCenterY()===a.getCenterY()&&s.getCenterX()===a.getCenterX()&&(f=1);var d=f*l[0],p=l[1]/f;l[0]d)return l[0]=u,l[1]=m,l[2]=f,l[3]=A,!1;if(hf)return l[0]=p,l[1]=h,l[2]=E,l[3]=d,!1;if(uf?(l[0]=y,l[1]=v,k=!0):(l[0]=g,l[1]=m,k=!0):C===D&&(u>f?(l[0]=p,l[1]=m,k=!0):(l[0]=x,l[1]=v,k=!0)),-O===D?f>u?(l[2]=_,l[3]=A,I=!0):(l[2]=E,l[3]=T,I=!0):O===D&&(f>u?(l[2]=S,l[3]=T,I=!0):(l[2]=L,l[3]=A,I=!0)),k&&I)return!1;if(u>f?h>d?(P=this.getCardinalDirection(C,D,4),F=this.getCardinalDirection(O,D,2)):(P=this.getCardinalDirection(-C,D,3),F=this.getCardinalDirection(-O,D,1)):h>d?(P=this.getCardinalDirection(-C,D,1),F=this.getCardinalDirection(-O,D,3)):(P=this.getCardinalDirection(C,D,2),F=this.getCardinalDirection(O,D,4)),!k)switch(P){case 1:$=m,B=u+-w/D,l[0]=B,l[1]=$;break;case 2:B=x,$=h+b*D,l[0]=B,l[1]=$;break;case 3:$=v,B=u+w/D,l[0]=B,l[1]=$;break;case 4:B=y,$=h+-b*D,l[0]=B,l[1]=$;break}if(!I)switch(F){case 1:Y=T,z=f+-N/D,l[2]=z,l[3]=Y;break;case 2:z=L,Y=d+M*D,l[2]=z,l[3]=Y;break;case 3:Y=A,z=f+N/D,l[2]=z,l[3]=Y;break;case 4:z=_,Y=d+-M*D,l[2]=z,l[3]=Y;break}}return!1},i.getCardinalDirection=function(a,s,l){return a>s?l:1+l%4},i.getIntersection=function(a,s,l,u){if(u==null)return this.getIntersection2(a,s,l);var h=a.x,f=a.y,d=s.x,p=s.y,m=l.x,g=l.y,y=u.x,v=u.y,x=void 0,b=void 0,w=void 0,S=void 0,T=void 0,E=void 0,_=void 0,A=void 0,L=void 0;return w=p-f,T=h-d,_=d*f-h*p,S=v-g,E=m-y,A=y*g-m*v,L=w*E-S*T,L===0?null:(x=(T*A-E*_)/L,b=(S*_-w*A)/L,new n(x,b))},i.angleOfVector=function(a,s,l,u){var h=void 0;return a!==l?(h=Math.atan((u-s)/(l-a)),l0?1:i<0?-1:0},n.floor=function(i){return i<0?Math.ceil(i):Math.floor(i)},n.ceil=function(i){return i<0?Math.floor(i):Math.ceil(i)},t.exports=n},function(t,e,r){"use strict";function n(){}o(n,"Integer"),n.MAX_VALUE=2147483647,n.MIN_VALUE=-2147483648,t.exports=n},function(t,e,r){"use strict";var n=function(){function h(f,d){for(var p=0;p"u"?"undefined":n(a);return a==null||s!="object"&&s!="function"},t.exports=i},function(t,e,r){"use strict";function n(m){if(Array.isArray(m)){for(var g=0,y=Array(m.length);g0&&g;){for(w.push(T[0]);w.length>0&&g;){var E=w[0];w.splice(0,1),b.add(E);for(var _=E.getEdges(),x=0;x<_.length;x++){var A=_[x].getOtherEnd(E);if(S.get(E)!=A)if(!b.has(A))w.push(A),S.set(A,E);else{g=!1;break}}}if(!g)m=[];else{var L=[].concat(n(b));m.push(L);for(var x=0;x-1&&T.splice(N,1)}b=new Set,S=new Map}}return m},p.prototype.createDummyNodesForBendpoints=function(m){for(var g=[],y=m.source,v=this.graphManager.calcLowestCommonAncestor(m.source,m.target),x=0;x0){for(var v=this.edgeToDummyNodes.get(y),x=0;x=0&&g.splice(A,1);var L=S.getNeighborsList();L.forEach(function(k){if(y.indexOf(k)<0){var I=v.get(k),C=I-1;C==1&&E.push(k),v.set(k,C)}})}y=y.concat(E),(g.length==1||g.length==2)&&(x=!0,b=g[0])}return b},p.prototype.setGraphManager=function(m){this.graphManager=m},t.exports=p},function(t,e,r){"use strict";function n(){}o(n,"RandomSeed"),n.seed=1,n.x=0,n.nextDouble=function(){return n.x=Math.sin(n.seed++)*1e4,n.x-Math.floor(n.x)},t.exports=n},function(t,e,r){"use strict";var n=r(4);function i(a,s){this.lworldOrgX=0,this.lworldOrgY=0,this.ldeviceOrgX=0,this.ldeviceOrgY=0,this.lworldExtX=1,this.lworldExtY=1,this.ldeviceExtX=1,this.ldeviceExtY=1}o(i,"Transform"),i.prototype.getWorldOrgX=function(){return this.lworldOrgX},i.prototype.setWorldOrgX=function(a){this.lworldOrgX=a},i.prototype.getWorldOrgY=function(){return this.lworldOrgY},i.prototype.setWorldOrgY=function(a){this.lworldOrgY=a},i.prototype.getWorldExtX=function(){return this.lworldExtX},i.prototype.setWorldExtX=function(a){this.lworldExtX=a},i.prototype.getWorldExtY=function(){return this.lworldExtY},i.prototype.setWorldExtY=function(a){this.lworldExtY=a},i.prototype.getDeviceOrgX=function(){return this.ldeviceOrgX},i.prototype.setDeviceOrgX=function(a){this.ldeviceOrgX=a},i.prototype.getDeviceOrgY=function(){return this.ldeviceOrgY},i.prototype.setDeviceOrgY=function(a){this.ldeviceOrgY=a},i.prototype.getDeviceExtX=function(){return this.ldeviceExtX},i.prototype.setDeviceExtX=function(a){this.ldeviceExtX=a},i.prototype.getDeviceExtY=function(){return this.ldeviceExtY},i.prototype.setDeviceExtY=function(a){this.ldeviceExtY=a},i.prototype.transformX=function(a){var s=0,l=this.lworldExtX;return l!=0&&(s=this.ldeviceOrgX+(a-this.lworldOrgX)*this.ldeviceExtX/l),s},i.prototype.transformY=function(a){var s=0,l=this.lworldExtY;return l!=0&&(s=this.ldeviceOrgY+(a-this.lworldOrgY)*this.ldeviceExtY/l),s},i.prototype.inverseTransformX=function(a){var s=0,l=this.ldeviceExtX;return l!=0&&(s=this.lworldOrgX+(a-this.ldeviceOrgX)*this.lworldExtX/l),s},i.prototype.inverseTransformY=function(a){var s=0,l=this.ldeviceExtY;return l!=0&&(s=this.lworldOrgY+(a-this.ldeviceOrgY)*this.lworldExtY/l),s},i.prototype.inverseTransformPoint=function(a){var s=new n(this.inverseTransformX(a.x),this.inverseTransformY(a.y));return s},t.exports=i},function(t,e,r){"use strict";function n(d){if(Array.isArray(d)){for(var p=0,m=Array(d.length);pa.ADAPTATION_LOWER_NODE_LIMIT&&(this.coolingFactor=Math.max(this.coolingFactor*a.COOLING_ADAPTATION_FACTOR,this.coolingFactor-(d-a.ADAPTATION_LOWER_NODE_LIMIT)/(a.ADAPTATION_UPPER_NODE_LIMIT-a.ADAPTATION_LOWER_NODE_LIMIT)*this.coolingFactor*(1-a.COOLING_ADAPTATION_FACTOR))),this.maxNodeDisplacement=a.MAX_NODE_DISPLACEMENT_INCREMENTAL):(d>a.ADAPTATION_LOWER_NODE_LIMIT?this.coolingFactor=Math.max(a.COOLING_ADAPTATION_FACTOR,1-(d-a.ADAPTATION_LOWER_NODE_LIMIT)/(a.ADAPTATION_UPPER_NODE_LIMIT-a.ADAPTATION_LOWER_NODE_LIMIT)*(1-a.COOLING_ADAPTATION_FACTOR)):this.coolingFactor=1,this.initialCoolingFactor=this.coolingFactor,this.maxNodeDisplacement=a.MAX_NODE_DISPLACEMENT),this.maxIterations=Math.max(this.getAllNodes().length*5,this.maxIterations),this.totalDisplacementThreshold=this.displacementThresholdPerNode*this.getAllNodes().length,this.repulsionRange=this.calcRepulsionRange()},h.prototype.calcSpringForces=function(){for(var d=this.getAllEdges(),p,m=0;m0&&arguments[0]!==void 0?arguments[0]:!0,p=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!1,m,g,y,v,x=this.getAllNodes(),b;if(this.useFRGridVariant)for(this.totalIterations%a.GRID_CALCULATION_CHECK_PERIOD==1&&d&&this.updateGrid(),b=new Set,m=0;mw||b>w)&&(d.gravitationForceX=-this.gravityConstant*y,d.gravitationForceY=-this.gravityConstant*v)):(w=p.getEstimatedSize()*this.compoundGravityRangeFactor,(x>w||b>w)&&(d.gravitationForceX=-this.gravityConstant*y*this.compoundGravityConstant,d.gravitationForceY=-this.gravityConstant*v*this.compoundGravityConstant))},h.prototype.isConverged=function(){var d,p=!1;return this.totalIterations>this.maxIterations/3&&(p=Math.abs(this.totalDisplacement-this.oldTotalDisplacement)<2),d=this.totalDisplacement=x.length||w>=x[0].length)){for(var S=0;Sh},"_defaultCompareFunction")}]),l}();t.exports=s},function(t,e,r){"use strict";var n=function(){function s(l,u){for(var h=0;h2&&arguments[2]!==void 0?arguments[2]:1,f=arguments.length>3&&arguments[3]!==void 0?arguments[3]:-1,d=arguments.length>4&&arguments[4]!==void 0?arguments[4]:-1;i(this,s),this.sequence1=l,this.sequence2=u,this.match_score=h,this.mismatch_penalty=f,this.gap_penalty=d,this.iMax=l.length+1,this.jMax=u.length+1,this.grid=new Array(this.iMax);for(var p=0;p=0;l--){var u=this.listeners[l];u.event===a&&u.callback===s&&this.listeners.splice(l,1)}},i.emit=function(a,s){for(var l=0;l{"use strict";o(function(e,r){typeof ob=="object"&&typeof wB=="object"?wB.exports=r(bB()):typeof define=="function"&&define.amd?define(["layout-base"],r):typeof ob=="object"?ob.coseBase=r(bB()):e.coseBase=r(e.layoutBase)},"webpackUniversalModuleDefinition")(ob,function(t){return function(e){var r={};function n(i){if(r[i])return r[i].exports;var a=r[i]={i,l:!1,exports:{}};return e[i].call(a.exports,a,a.exports,n),a.l=!0,a.exports}return o(n,"__webpack_require__"),n.m=e,n.c=r,n.i=function(i){return i},n.d=function(i,a,s){n.o(i,a)||Object.defineProperty(i,a,{configurable:!1,enumerable:!0,get:s})},n.n=function(i){var a=i&&i.__esModule?o(function(){return i.default},"getDefault"):o(function(){return i},"getModuleExports");return n.d(a,"a",a),a},n.o=function(i,a){return Object.prototype.hasOwnProperty.call(i,a)},n.p="",n(n.s=7)}([function(e,r){e.exports=t},function(e,r,n){"use strict";var i=n(0).FDLayoutConstants;function a(){}o(a,"CoSEConstants");for(var s in i)a[s]=i[s];a.DEFAULT_USE_MULTI_LEVEL_SCALING=!1,a.DEFAULT_RADIAL_SEPARATION=i.DEFAULT_EDGE_LENGTH,a.DEFAULT_COMPONENT_SEPERATION=60,a.TILE=!0,a.TILING_PADDING_VERTICAL=10,a.TILING_PADDING_HORIZONTAL=10,a.TREE_REDUCTION_ON_INCREMENTAL=!1,e.exports=a},function(e,r,n){"use strict";var i=n(0).FDLayoutEdge;function a(l,u,h){i.call(this,l,u,h)}o(a,"CoSEEdge"),a.prototype=Object.create(i.prototype);for(var s in i)a[s]=i[s];e.exports=a},function(e,r,n){"use strict";var i=n(0).LGraph;function a(l,u,h){i.call(this,l,u,h)}o(a,"CoSEGraph"),a.prototype=Object.create(i.prototype);for(var s in i)a[s]=i[s];e.exports=a},function(e,r,n){"use strict";var i=n(0).LGraphManager;function a(l){i.call(this,l)}o(a,"CoSEGraphManager"),a.prototype=Object.create(i.prototype);for(var s in i)a[s]=i[s];e.exports=a},function(e,r,n){"use strict";var i=n(0).FDLayoutNode,a=n(0).IMath;function s(u,h,f,d){i.call(this,u,h,f,d)}o(s,"CoSENode"),s.prototype=Object.create(i.prototype);for(var l in i)s[l]=i[l];s.prototype.move=function(){var u=this.graphManager.getLayout();this.displacementX=u.coolingFactor*(this.springForceX+this.repulsionForceX+this.gravitationForceX)/this.noOfChildren,this.displacementY=u.coolingFactor*(this.springForceY+this.repulsionForceY+this.gravitationForceY)/this.noOfChildren,Math.abs(this.displacementX)>u.coolingFactor*u.maxNodeDisplacement&&(this.displacementX=u.coolingFactor*u.maxNodeDisplacement*a.sign(this.displacementX)),Math.abs(this.displacementY)>u.coolingFactor*u.maxNodeDisplacement&&(this.displacementY=u.coolingFactor*u.maxNodeDisplacement*a.sign(this.displacementY)),this.child==null?this.moveBy(this.displacementX,this.displacementY):this.child.getNodes().length==0?this.moveBy(this.displacementX,this.displacementY):this.propogateDisplacementToChildren(this.displacementX,this.displacementY),u.totalDisplacement+=Math.abs(this.displacementX)+Math.abs(this.displacementY),this.springForceX=0,this.springForceY=0,this.repulsionForceX=0,this.repulsionForceY=0,this.gravitationForceX=0,this.gravitationForceY=0,this.displacementX=0,this.displacementY=0},s.prototype.propogateDisplacementToChildren=function(u,h){for(var f=this.getChild().getNodes(),d,p=0;p0)this.positionNodesRadially(T);else{this.reduceTrees(),this.graphManager.resetAllNodesToApplyGravitation();var E=new Set(this.getAllNodes()),_=this.nodesWithGravity.filter(function(A){return E.has(A)});this.graphManager.setAllNodesToApplyGravitation(_),this.positionNodesRandomly()}}return this.initSpringEmbedder(),this.runSpringEmbedder(),!0},w.prototype.tick=function(){if(this.totalIterations++,this.totalIterations===this.maxIterations&&!this.isTreeGrowing&&!this.isGrowthFinished)if(this.prunedNodesAll.length>0)this.isTreeGrowing=!0;else return!0;if(this.totalIterations%f.CONVERGENCE_CHECK_PERIOD==0&&!this.isTreeGrowing&&!this.isGrowthFinished){if(this.isConverged())if(this.prunedNodesAll.length>0)this.isTreeGrowing=!0;else return!0;this.coolingCycle++,this.layoutQuality==0?this.coolingAdjuster=this.coolingCycle:this.layoutQuality==1&&(this.coolingAdjuster=this.coolingCycle/3),this.coolingFactor=Math.max(this.initialCoolingFactor-Math.pow(this.coolingCycle,Math.log(100*(this.initialCoolingFactor-this.finalTemperature))/Math.log(this.maxCoolingCycle))/100*this.coolingAdjuster,this.finalTemperature),this.animationPeriod=Math.ceil(this.initialAnimationPeriod*Math.sqrt(this.coolingFactor))}if(this.isTreeGrowing){if(this.growTreeIterations%10==0)if(this.prunedNodesAll.length>0){this.graphManager.updateBounds(),this.updateGrid(),this.growTree(this.prunedNodesAll),this.graphManager.resetAllNodesToApplyGravitation();var T=new Set(this.getAllNodes()),E=this.nodesWithGravity.filter(function(L){return T.has(L)});this.graphManager.setAllNodesToApplyGravitation(E),this.graphManager.updateBounds(),this.updateGrid(),this.coolingFactor=f.DEFAULT_COOLING_FACTOR_INCREMENTAL}else this.isTreeGrowing=!1,this.isGrowthFinished=!0;this.growTreeIterations++}if(this.isGrowthFinished){if(this.isConverged())return!0;this.afterGrowthIterations%10==0&&(this.graphManager.updateBounds(),this.updateGrid()),this.coolingFactor=f.DEFAULT_COOLING_FACTOR_INCREMENTAL*((100-this.afterGrowthIterations)/100),this.afterGrowthIterations++}var _=!this.isTreeGrowing&&!this.isGrowthFinished,A=this.growTreeIterations%10==1&&this.isTreeGrowing||this.afterGrowthIterations%10==1&&this.isGrowthFinished;return this.totalDisplacement=0,this.graphManager.updateBounds(),this.calcSpringForces(),this.calcRepulsionForces(_,A),this.calcGravitationalForces(),this.moveNodes(),this.animate(),!1},w.prototype.getPositionsData=function(){for(var T=this.graphManager.getAllNodes(),E={},_=0;_1){var k;for(k=0;kA&&(A=Math.floor(N.y)),M=Math.floor(N.x+h.DEFAULT_COMPONENT_SEPERATION)}this.transform(new m(d.WORLD_CENTER_X-N.x/2,d.WORLD_CENTER_Y-N.y/2))},w.radialLayout=function(T,E,_){var A=Math.max(this.maxDiagonalInTree(T),h.DEFAULT_RADIAL_SEPARATION);w.branchRadialLayout(E,null,0,359,0,A);var L=x.calculateBounds(T),M=new b;M.setDeviceOrgX(L.getMinX()),M.setDeviceOrgY(L.getMinY()),M.setWorldOrgX(_.x),M.setWorldOrgY(_.y);for(var N=0;N1;){var Q=Y[0];Y.splice(0,1);var X=P.indexOf(Q);X>=0&&P.splice(X,1),$--,F--}E!=null?z=(P.indexOf(Y[0])+1)%$:z=0;for(var ie=Math.abs(A-_)/F,j=z;B!=F;j=++j%$){var J=P[j].getOtherEnd(T);if(J!=E){var Z=(_+B*ie)%360,H=(Z+ie)%360;w.branchRadialLayout(J,T,Z,H,L+M,M),B++}}},w.maxDiagonalInTree=function(T){for(var E=y.MIN_VALUE,_=0;_E&&(E=L)}return E},w.prototype.calcRepulsionRange=function(){return 2*(this.level+1)*this.idealEdgeLength},w.prototype.groupZeroDegreeMembers=function(){var T=this,E={};this.memberGroups={},this.idToDummyNode={};for(var _=[],A=this.graphManager.getAllNodes(),L=0;L"u"&&(E[k]=[]),E[k]=E[k].concat(M)}Object.keys(E).forEach(function(I){if(E[I].length>1){var C="DummyCompound_"+I;T.memberGroups[C]=E[I];var O=E[I][0].getParent(),D=new l(T.graphManager);D.id=C,D.paddingLeft=O.paddingLeft||0,D.paddingRight=O.paddingRight||0,D.paddingBottom=O.paddingBottom||0,D.paddingTop=O.paddingTop||0,T.idToDummyNode[C]=D;var P=T.getGraphManager().add(T.newGraph(),D),F=O.getChild();F.add(D);for(var B=0;B=0;T--){var E=this.compoundOrder[T],_=E.id,A=E.paddingLeft,L=E.paddingTop;this.adjustLocations(this.tiledMemberPack[_],E.rect.x,E.rect.y,A,L)}},w.prototype.repopulateZeroDegreeMembers=function(){var T=this,E=this.tiledZeroDegreePack;Object.keys(E).forEach(function(_){var A=T.idToDummyNode[_],L=A.paddingLeft,M=A.paddingTop;T.adjustLocations(E[_],A.rect.x,A.rect.y,L,M)})},w.prototype.getToBeTiled=function(T){var E=T.id;if(this.toBeTiled[E]!=null)return this.toBeTiled[E];var _=T.getChild();if(_==null)return this.toBeTiled[E]=!1,!1;for(var A=_.getNodes(),L=0;L0)return this.toBeTiled[E]=!1,!1;if(M.getChild()==null){this.toBeTiled[M.id]=!1;continue}if(!this.getToBeTiled(M))return this.toBeTiled[E]=!1,!1}return this.toBeTiled[E]=!0,!0},w.prototype.getNodeDegree=function(T){for(var E=T.id,_=T.getEdges(),A=0,L=0;L<_.length;L++){var M=_[L];M.getSource().id!==M.getTarget().id&&(A=A+1)}return A},w.prototype.getNodeDegreeWithChildren=function(T){var E=this.getNodeDegree(T);if(T.getChild()==null)return E;for(var _=T.getChild().getNodes(),A=0;A<_.length;A++){var L=_[A];E+=this.getNodeDegreeWithChildren(L)}return E},w.prototype.performDFSOnCompounds=function(){this.compoundOrder=[],this.fillCompexOrderByDFS(this.graphManager.getRoot().getNodes())},w.prototype.fillCompexOrderByDFS=function(T){for(var E=0;EI&&(I=O.rect.height)}_+=I+T.verticalPadding}},w.prototype.tileCompoundMembers=function(T,E){var _=this;this.tiledMemberPack=[],Object.keys(T).forEach(function(A){var L=E[A];_.tiledMemberPack[A]=_.tileNodes(T[A],L.paddingLeft+L.paddingRight),L.rect.width=_.tiledMemberPack[A].width,L.rect.height=_.tiledMemberPack[A].height})},w.prototype.tileNodes=function(T,E){var _=h.TILING_PADDING_VERTICAL,A=h.TILING_PADDING_HORIZONTAL,L={rows:[],rowWidth:[],rowHeight:[],width:0,height:E,verticalPadding:_,horizontalPadding:A};T.sort(function(k,I){return k.rect.width*k.rect.height>I.rect.width*I.rect.height?-1:k.rect.width*k.rect.height0&&(N+=T.horizontalPadding),T.rowWidth[_]=N,T.width0&&(k+=T.verticalPadding);var I=0;k>T.rowHeight[_]&&(I=T.rowHeight[_],T.rowHeight[_]=k,I=T.rowHeight[_]-I),T.height+=I,T.rows[_].push(E)},w.prototype.getShortestRowIndex=function(T){for(var E=-1,_=Number.MAX_VALUE,A=0;A_&&(E=A,_=T.rowWidth[A]);return E},w.prototype.canAddHorizontal=function(T,E,_){var A=this.getShortestRowIndex(T);if(A<0)return!0;var L=T.rowWidth[A];if(L+T.horizontalPadding+E<=T.width)return!0;var M=0;T.rowHeight[A]<_&&A>0&&(M=_+T.verticalPadding-T.rowHeight[A]);var N;T.width-L>=E+T.horizontalPadding?N=(T.height+M)/(L+E+T.horizontalPadding):N=(T.height+M)/T.width,M=_+T.verticalPadding;var k;return T.widthM&&E!=_){A.splice(-1,1),T.rows[_].push(L),T.rowWidth[E]=T.rowWidth[E]-M,T.rowWidth[_]=T.rowWidth[_]+M,T.width=T.rowWidth[instance.getLongestRowIndex(T)];for(var N=Number.MIN_VALUE,k=0;kN&&(N=A[k].height);E>0&&(N+=T.verticalPadding);var I=T.rowHeight[E]+T.rowHeight[_];T.rowHeight[E]=N,T.rowHeight[_]0)for(var F=L;F<=M;F++)P[0]+=this.grid[F][N-1].length+this.grid[F][N].length-1;if(M0)for(var F=N;F<=k;F++)P[3]+=this.grid[L-1][F].length+this.grid[L][F].length-1;for(var B=y.MAX_VALUE,$,z,Y=0;Y{"use strict";o(function(e,r){typeof lb=="object"&&typeof kB=="object"?kB.exports=r(TB()):typeof define=="function"&&define.amd?define(["cose-base"],r):typeof lb=="object"?lb.cytoscapeCoseBilkent=r(TB()):e.cytoscapeCoseBilkent=r(e.coseBase)},"webpackUniversalModuleDefinition")(lb,function(t){return function(e){var r={};function n(i){if(r[i])return r[i].exports;var a=r[i]={i,l:!1,exports:{}};return e[i].call(a.exports,a,a.exports,n),a.l=!0,a.exports}return o(n,"__webpack_require__"),n.m=e,n.c=r,n.i=function(i){return i},n.d=function(i,a,s){n.o(i,a)||Object.defineProperty(i,a,{configurable:!1,enumerable:!0,get:s})},n.n=function(i){var a=i&&i.__esModule?o(function(){return i.default},"getDefault"):o(function(){return i},"getModuleExports");return n.d(a,"a",a),a},n.o=function(i,a){return Object.prototype.hasOwnProperty.call(i,a)},n.p="",n(n.s=1)}([function(e,r){e.exports=t},function(e,r,n){"use strict";var i=n(0).layoutBase.LayoutConstants,a=n(0).layoutBase.FDLayoutConstants,s=n(0).CoSEConstants,l=n(0).CoSELayout,u=n(0).CoSENode,h=n(0).layoutBase.PointD,f=n(0).layoutBase.DimensionD,d={ready:o(function(){},"ready"),stop:o(function(){},"stop"),quality:"default",nodeDimensionsIncludeLabels:!1,refresh:30,fit:!0,padding:10,randomize:!0,nodeRepulsion:4500,idealEdgeLength:50,edgeElasticity:.45,nestingFactor:.1,gravity:.25,numIter:2500,tile:!0,animate:"end",animationDuration:500,tilingPaddingVertical:10,tilingPaddingHorizontal:10,gravityRangeCompound:1.5,gravityCompound:1,gravityRange:3.8,initialEnergyOnIncremental:.5};function p(v,x){var b={};for(var w in v)b[w]=v[w];for(var w in x)b[w]=x[w];return b}o(p,"extend");function m(v){this.options=p(d,v),g(this.options)}o(m,"_CoSELayout");var g=o(function(x){x.nodeRepulsion!=null&&(s.DEFAULT_REPULSION_STRENGTH=a.DEFAULT_REPULSION_STRENGTH=x.nodeRepulsion),x.idealEdgeLength!=null&&(s.DEFAULT_EDGE_LENGTH=a.DEFAULT_EDGE_LENGTH=x.idealEdgeLength),x.edgeElasticity!=null&&(s.DEFAULT_SPRING_STRENGTH=a.DEFAULT_SPRING_STRENGTH=x.edgeElasticity),x.nestingFactor!=null&&(s.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR=a.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR=x.nestingFactor),x.gravity!=null&&(s.DEFAULT_GRAVITY_STRENGTH=a.DEFAULT_GRAVITY_STRENGTH=x.gravity),x.numIter!=null&&(s.MAX_ITERATIONS=a.MAX_ITERATIONS=x.numIter),x.gravityRange!=null&&(s.DEFAULT_GRAVITY_RANGE_FACTOR=a.DEFAULT_GRAVITY_RANGE_FACTOR=x.gravityRange),x.gravityCompound!=null&&(s.DEFAULT_COMPOUND_GRAVITY_STRENGTH=a.DEFAULT_COMPOUND_GRAVITY_STRENGTH=x.gravityCompound),x.gravityRangeCompound!=null&&(s.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR=a.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR=x.gravityRangeCompound),x.initialEnergyOnIncremental!=null&&(s.DEFAULT_COOLING_FACTOR_INCREMENTAL=a.DEFAULT_COOLING_FACTOR_INCREMENTAL=x.initialEnergyOnIncremental),x.quality=="draft"?i.QUALITY=0:x.quality=="proof"?i.QUALITY=2:i.QUALITY=1,s.NODE_DIMENSIONS_INCLUDE_LABELS=a.NODE_DIMENSIONS_INCLUDE_LABELS=i.NODE_DIMENSIONS_INCLUDE_LABELS=x.nodeDimensionsIncludeLabels,s.DEFAULT_INCREMENTAL=a.DEFAULT_INCREMENTAL=i.DEFAULT_INCREMENTAL=!x.randomize,s.ANIMATE=a.ANIMATE=i.ANIMATE=x.animate,s.TILE=x.tile,s.TILING_PADDING_VERTICAL=typeof x.tilingPaddingVertical=="function"?x.tilingPaddingVertical.call():x.tilingPaddingVertical,s.TILING_PADDING_HORIZONTAL=typeof x.tilingPaddingHorizontal=="function"?x.tilingPaddingHorizontal.call():x.tilingPaddingHorizontal},"getUserOptions");m.prototype.run=function(){var v,x,b=this.options,w=this.idToLNode={},S=this.layout=new l,T=this;T.stopped=!1,this.cy=this.options.cy,this.cy.trigger({type:"layoutstart",layout:this});var E=S.newGraphManager();this.gm=E;var _=this.options.eles.nodes(),A=this.options.eles.edges();this.root=E.addRoot(),this.processChildrenList(this.root,this.getTopMostNodes(_),S);for(var L=0;L0){var k;k=b.getGraphManager().add(b.newGraph(),_),this.processChildrenList(k,E,b)}}},m.prototype.stop=function(){return this.stopped=!0,this};var y=o(function(x){x("layout","cose-bilkent",m)},"register");typeof cytoscape<"u"&&y(cytoscape),e.exports=y}])})});function fZe(t,e,r,n,i){return t.insert("polygon",":first-child").attr("points",n.map(function(a){return a.x+","+a.y}).join(" ")).attr("transform","translate("+(i.width-e)/2+", "+r+")")}var sZe,oZe,lZe,cZe,uZe,hZe,dZe,pZe,sge,oge,lge=R(()=>{"use strict";Al();xr();sZe=12,oZe=o(function(t,e,r,n){e.append("path").attr("id","node-"+r.id).attr("class","node-bkg node-"+t.type2Str(r.type)).attr("d",`M0 ${r.height-5} v${-r.height+2*5} q0,-5 5,-5 h${r.width-2*5} q5,0 5,5 v${r.height-5} H0 Z`),e.append("line").attr("class","node-line-"+n).attr("x1",0).attr("y1",r.height).attr("x2",r.width).attr("y2",r.height)},"defaultBkg"),lZe=o(function(t,e,r){e.append("rect").attr("id","node-"+r.id).attr("class","node-bkg node-"+t.type2Str(r.type)).attr("height",r.height).attr("width",r.width)},"rectBkg"),cZe=o(function(t,e,r){let n=r.width,i=r.height,a=.15*n,s=.25*n,l=.35*n,u=.2*n;e.append("path").attr("id","node-"+r.id).attr("class","node-bkg node-"+t.type2Str(r.type)).attr("d",`M0 0 a${a},${a} 0 0,1 ${n*.25},${-1*n*.1} + a${l},${l} 1 0,1 ${n*.4},${-1*n*.1} + a${s},${s} 1 0,1 ${n*.35},${1*n*.2} + + a${a},${a} 1 0,1 ${n*.15},${1*i*.35} + a${u},${u} 1 0,1 ${-1*n*.15},${1*i*.65} + + a${s},${a} 1 0,1 ${-1*n*.25},${n*.15} + a${l},${l} 1 0,1 ${-1*n*.5},0 + a${a},${a} 1 0,1 ${-1*n*.25},${-1*n*.15} + + a${a},${a} 1 0,1 ${-1*n*.1},${-1*i*.35} + a${u},${u} 1 0,1 ${n*.1},${-1*i*.65} + + H0 V0 Z`)},"cloudBkg"),uZe=o(function(t,e,r){let n=r.width,i=r.height,a=.15*n;e.append("path").attr("id","node-"+r.id).attr("class","node-bkg node-"+t.type2Str(r.type)).attr("d",`M0 0 a${a},${a} 1 0,0 ${n*.25},${-1*i*.1} + a${a},${a} 1 0,0 ${n*.25},0 + a${a},${a} 1 0,0 ${n*.25},0 + a${a},${a} 1 0,0 ${n*.25},${1*i*.1} + + a${a},${a} 1 0,0 ${n*.15},${1*i*.33} + a${a*.8},${a*.8} 1 0,0 0,${1*i*.34} + a${a},${a} 1 0,0 ${-1*n*.15},${1*i*.33} + + a${a},${a} 1 0,0 ${-1*n*.25},${i*.15} + a${a},${a} 1 0,0 ${-1*n*.25},0 + a${a},${a} 1 0,0 ${-1*n*.25},0 + a${a},${a} 1 0,0 ${-1*n*.25},${-1*i*.15} + + a${a},${a} 1 0,0 ${-1*n*.1},${-1*i*.33} + a${a*.8},${a*.8} 1 0,0 0,${-1*i*.34} + a${a},${a} 1 0,0 ${n*.1},${-1*i*.33} + + H0 V0 Z`)},"bangBkg"),hZe=o(function(t,e,r){e.append("circle").attr("id","node-"+r.id).attr("class","node-bkg node-"+t.type2Str(r.type)).attr("r",r.width/2)},"circleBkg");o(fZe,"insertPolygonShape");dZe=o(function(t,e,r){let n=r.height,a=n/4,s=r.width-r.padding+2*a,l=[{x:a,y:0},{x:s-a,y:0},{x:s,y:-n/2},{x:s-a,y:-n},{x:a,y:-n},{x:0,y:-n/2}];fZe(e,s,n,l,r)},"hexagonBkg"),pZe=o(function(t,e,r){e.append("rect").attr("id","node-"+r.id).attr("class","node-bkg node-"+t.type2Str(r.type)).attr("height",r.height).attr("rx",r.padding).attr("ry",r.padding).attr("width",r.width)},"roundedRectBkg"),sge=o(async function(t,e,r,n,i){let a=i.htmlLabels,s=n%(sZe-1),l=e.append("g");r.section=s;let u="section-"+s;s<0&&(u+=" section-root"),l.attr("class",(r.class?r.class+" ":"")+"mindmap-node "+u);let h=l.append("g"),f=l.append("g"),d=r.descr.replace(/()/g,` +`);await ta(f,d,{useHtmlLabels:a,width:r.width,classes:"mindmap-node-label"},i),a||f.attr("dy","1em").attr("alignment-baseline","middle").attr("dominant-baseline","middle").attr("text-anchor","middle");let p=f.node().getBBox(),[m]=mc(i.fontSize);if(r.height=p.height+m*1.1*.5+r.padding,r.width=p.width+2*r.padding,r.icon)if(r.type===t.nodeType.CIRCLE)r.height+=50,r.width+=50,l.append("foreignObject").attr("height","50px").attr("width",r.width).attr("style","text-align: center;").append("div").attr("class","icon-container").append("i").attr("class","node-icon-"+s+" "+r.icon),f.attr("transform","translate("+r.width/2+", "+(r.height/2-1.5*r.padding)+")");else{r.width+=50;let g=r.height;r.height=Math.max(g,60);let y=Math.abs(r.height-g);l.append("foreignObject").attr("width","60px").attr("height",r.height).attr("style","text-align: center;margin-top:"+y/2+"px;").append("div").attr("class","icon-container").append("i").attr("class","node-icon-"+s+" "+r.icon),f.attr("transform","translate("+(25+r.width/2)+", "+(y/2+r.padding/2)+")")}else if(a){let g=(r.width-p.width)/2,y=(r.height-p.height)/2;f.attr("transform","translate("+g+", "+y+")")}else{let g=r.width/2,y=r.padding/2;f.attr("transform","translate("+g+", "+y+")")}switch(r.type){case t.nodeType.DEFAULT:oZe(t,h,r,s);break;case t.nodeType.ROUNDED_RECT:pZe(t,h,r,s);break;case t.nodeType.RECT:lZe(t,h,r,s);break;case t.nodeType.CIRCLE:h.attr("transform","translate("+r.width/2+", "+ +r.height/2+")"),hZe(t,h,r,s);break;case t.nodeType.CLOUD:cZe(t,h,r,s);break;case t.nodeType.BANG:uZe(t,h,r,s);break;case t.nodeType.HEXAGON:dZe(t,h,r,s);break}return t.setElementForId(r.id,l),r.height},"drawNode"),oge=o(function(t,e){let r=t.getElementById(e.id),n=e.x||0,i=e.y||0;r.attr("transform","translate("+n+","+i+")")},"positionNode")});async function uge(t,e,r,n,i){await sge(t,e,r,n,i),r.children&&await Promise.all(r.children.map((a,s)=>uge(t,e,a,n<0?s:n,i)))}function mZe(t,e){e.edges().map((r,n)=>{let i=r.data();if(r[0]._private.bodyBounds){let a=r[0]._private.rscratch;V.trace("Edge: ",n,i),t.insert("path").attr("d",`M ${a.startX},${a.startY} L ${a.midX},${a.midY} L${a.endX},${a.endY} `).attr("class","edge section-edge-"+i.section+" edge-depth-"+i.depth)}})}function hge(t,e,r,n){e.add({group:"nodes",data:{id:t.id.toString(),labelText:t.descr,height:t.height,width:t.width,level:n,nodeId:t.id,padding:t.padding,type:t.type},position:{x:t.x,y:t.y}}),t.children&&t.children.forEach(i=>{hge(i,e,r,n+1),e.add({group:"edges",data:{id:`${t.id}_${i.id}`,source:t.id,target:i.id,depth:n,section:i.section}})})}function gZe(t,e){return new Promise(r=>{let n=$e("body").append("div").attr("id","cy").attr("style","display:none"),i=rl({container:document.getElementById("cy"),style:[{selector:"edge",style:{"curve-style":"bezier"}}]});n.remove(),hge(t,i,e,0),i.nodes().forEach(function(a){a.layoutDimensions=()=>{let s=a.data();return{w:s.width,h:s.height}}}),i.layout({name:"cose-bilkent",quality:"proof",styleEnabled:!1,animate:!1}).run(),i.ready(a=>{V.info("Ready",a),r(i)})})}function yZe(t,e){e.nodes().map((r,n)=>{let i=r.data();i.x=r.position().x,i.y=r.position().y,oge(t,i);let a=t.getElementById(i.nodeId);V.info("Id:",n,"Position: (",r.position().x,", ",r.position().y,")",i),a.attr("transform",`translate(${r.position().x-i.width/2}, ${r.position().y-i.height/2})`),a.attr("attr",`apa-${n})`)})}var cge,vZe,fge,dge=R(()=>{"use strict";vB();cge=Xi(age(),1);Zt();_t();ut();pf();Yn();lge();sl();rl.use(cge.default);o(uge,"drawNodes");o(mZe,"drawEdges");o(hge,"addNodes");o(gZe,"layoutMindmap");o(yZe,"positionNodes");vZe=o(async(t,e,r,n)=>{V.debug(`Rendering mindmap diagram +`+t);let i=n.db,a=i.getMindmap();if(!a)return;let s=de();s.htmlLabels=!1;let l=Ps(e),u=l.append("g");u.attr("class","mindmap-edges");let h=l.append("g");h.attr("class","mindmap-nodes"),await uge(i,h,a,-1,s);let f=await gZe(a,s);mZe(u,f),yZe(i,f),Lo(void 0,l,s.mindmap?.padding??mr.mindmap.padding,s.mindmap?.useMaxWidth??mr.mindmap.useMaxWidth)},"draw"),fge={draw:vZe}});var xZe,bZe,pge,mge=R(()=>{"use strict";al();xZe=o(t=>{let e="";for(let r=0;r` + .edge { + stroke-width: 3; + } + ${xZe(t)} + .section-root rect, .section-root path, .section-root circle, .section-root polygon { + fill: ${t.git0}; + } + .section-root text { + fill: ${t.gitBranchLabel0}; + } + .icon-container { + height:100%; + display: flex; + justify-content: center; + align-items: center; + } + .edge { + fill: none; + } + .mindmap-node-label { + dy: 1em; + alignment-baseline: middle; + text-anchor: middle; + dominant-baseline: middle; + text-align: center; + } +`,"getStyles"),pge=bZe});var gge={};hr(gge,{diagram:()=>wZe});var wZe,yge=R(()=>{"use strict";r0e();a0e();dge();mge();wZe={db:i0e,renderer:fge,parser:t0e,styles:pge}});var EB,cb,bge=R(()=>{"use strict";EB=function(){var t=o(function(l,u,h,f){for(h=h||{},f=l.length;f--;h[l[f]]=u);return h},"o"),e=[1,9],r=[1,10],n=[1,5,10,12],i={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,start:3,SANKEY:4,NEWLINE:5,csv:6,opt_eof:7,record:8,csv_tail:9,EOF:10,"field[source]":11,COMMA:12,"field[target]":13,"field[value]":14,field:15,escaped:16,non_escaped:17,DQUOTE:18,ESCAPED_TEXT:19,NON_ESCAPED_TEXT:20,$accept:0,$end:1},terminals_:{2:"error",4:"SANKEY",5:"NEWLINE",10:"EOF",11:"field[source]",12:"COMMA",13:"field[target]",14:"field[value]",18:"DQUOTE",19:"ESCAPED_TEXT",20:"NON_ESCAPED_TEXT"},productions_:[0,[3,4],[6,2],[9,2],[9,0],[7,1],[7,0],[8,5],[15,1],[15,1],[16,3],[17,1]],performAction:o(function(u,h,f,d,p,m,g){var y=m.length-1;switch(p){case 7:let v=d.findOrCreateNode(m[y-4].trim().replaceAll('""','"')),x=d.findOrCreateNode(m[y-2].trim().replaceAll('""','"')),b=parseFloat(m[y].trim());d.addLink(v,x,b);break;case 8:case 9:case 11:this.$=m[y];break;case 10:this.$=m[y-1];break}},"anonymous"),table:[{3:1,4:[1,2]},{1:[3]},{5:[1,3]},{6:4,8:5,15:6,16:7,17:8,18:e,20:r},{1:[2,6],7:11,10:[1,12]},t(r,[2,4],{9:13,5:[1,14]}),{12:[1,15]},t(n,[2,8]),t(n,[2,9]),{19:[1,16]},t(n,[2,11]),{1:[2,1]},{1:[2,5]},t(r,[2,2]),{6:17,8:5,15:6,16:7,17:8,18:e,20:r},{15:18,16:7,17:8,18:e,20:r},{18:[1,19]},t(r,[2,3]),{12:[1,20]},t(n,[2,10]),{15:21,16:7,17:8,18:e,20:r},t([1,5,10],[2,7])],defaultActions:{11:[2,1],12:[2,5]},parseError:o(function(u,h){if(h.recoverable)this.trace(u);else{var f=new Error(u);throw f.hash=h,f}},"parseError"),parse:o(function(u){var h=this,f=[0],d=[],p=[null],m=[],g=this.table,y="",v=0,x=0,b=0,w=2,S=1,T=m.slice.call(arguments,1),E=Object.create(this.lexer),_={yy:{}};for(var A in this.yy)Object.prototype.hasOwnProperty.call(this.yy,A)&&(_.yy[A]=this.yy[A]);E.setInput(u,_.yy),_.yy.lexer=E,_.yy.parser=this,typeof E.yylloc>"u"&&(E.yylloc={});var L=E.yylloc;m.push(L);var M=E.options&&E.options.ranges;typeof _.yy.parseError=="function"?this.parseError=_.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function N(ie){f.length=f.length-2*ie,p.length=p.length-ie,m.length=m.length-ie}o(N,"popStack");function k(){var ie;return ie=d.pop()||E.lex()||S,typeof ie!="number"&&(ie instanceof Array&&(d=ie,ie=d.pop()),ie=h.symbols_[ie]||ie),ie}o(k,"lex");for(var I,C,O,D,P,F,B={},$,z,Y,Q;;){if(O=f[f.length-1],this.defaultActions[O]?D=this.defaultActions[O]:((I===null||typeof I>"u")&&(I=k()),D=g[O]&&g[O][I]),typeof D>"u"||!D.length||!D[0]){var X="";Q=[];for($ in g[O])this.terminals_[$]&&$>w&&Q.push("'"+this.terminals_[$]+"'");E.showPosition?X="Parse error on line "+(v+1)+`: +`+E.showPosition()+` +Expecting `+Q.join(", ")+", got '"+(this.terminals_[I]||I)+"'":X="Parse error on line "+(v+1)+": Unexpected "+(I==S?"end of input":"'"+(this.terminals_[I]||I)+"'"),this.parseError(X,{text:E.match,token:this.terminals_[I]||I,line:E.yylineno,loc:L,expected:Q})}if(D[0]instanceof Array&&D.length>1)throw new Error("Parse Error: multiple actions possible at state: "+O+", token: "+I);switch(D[0]){case 1:f.push(I),p.push(E.yytext),m.push(E.yylloc),f.push(D[1]),I=null,C?(I=C,C=null):(x=E.yyleng,y=E.yytext,v=E.yylineno,L=E.yylloc,b>0&&b--);break;case 2:if(z=this.productions_[D[1]][1],B.$=p[p.length-z],B._$={first_line:m[m.length-(z||1)].first_line,last_line:m[m.length-1].last_line,first_column:m[m.length-(z||1)].first_column,last_column:m[m.length-1].last_column},M&&(B._$.range=[m[m.length-(z||1)].range[0],m[m.length-1].range[1]]),F=this.performAction.apply(B,[y,x,v,_.yy,D[1],p,m].concat(T)),typeof F<"u")return F;z&&(f=f.slice(0,-1*z*2),p=p.slice(0,-1*z),m=m.slice(0,-1*z)),f.push(this.productions_[D[1]][0]),p.push(B.$),m.push(B._$),Y=g[f[f.length-2]][f[f.length-1]],f.push(Y);break;case 3:return!0}}return!0},"parse")},a=function(){var l={EOF:1,parseError:o(function(h,f){if(this.yy.parser)this.yy.parser.parseError(h,f);else throw new Error(h)},"parseError"),setInput:o(function(u,h){return this.yy=h||this.yy||{},this._input=u,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var u=this._input[0];this.yytext+=u,this.yyleng++,this.offset++,this.match+=u,this.matched+=u;var h=u.match(/(?:\r\n?|\n).*/g);return h?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),u},"input"),unput:o(function(u){var h=u.length,f=u.split(/(?:\r\n?|\n)/g);this._input=u+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-h),this.offset-=h;var d=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),f.length-1&&(this.yylineno-=f.length-1);var p=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:f?(f.length===d.length?this.yylloc.first_column:0)+d[d.length-f.length].length-f[0].length:this.yylloc.first_column-h},this.options.ranges&&(this.yylloc.range=[p[0],p[0]+this.yyleng-h]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(u){this.unput(this.match.slice(u))},"less"),pastInput:o(function(){var u=this.matched.substr(0,this.matched.length-this.match.length);return(u.length>20?"...":"")+u.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var u=this.match;return u.length<20&&(u+=this._input.substr(0,20-u.length)),(u.substr(0,20)+(u.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var u=this.pastInput(),h=new Array(u.length+1).join("-");return u+this.upcomingInput()+` +`+h+"^"},"showPosition"),test_match:o(function(u,h){var f,d,p;if(this.options.backtrack_lexer&&(p={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(p.yylloc.range=this.yylloc.range.slice(0))),d=u[0].match(/(?:\r\n?|\n).*/g),d&&(this.yylineno+=d.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:d?d[d.length-1].length-d[d.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+u[0].length},this.yytext+=u[0],this.match+=u[0],this.matches=u,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(u[0].length),this.matched+=u[0],f=this.performAction.call(this,this.yy,this,h,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),f)return f;if(this._backtrack){for(var m in p)this[m]=p[m];return!1}return!1},"test_match"),next:o(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var u,h,f,d;this._more||(this.yytext="",this.match="");for(var p=this._currentRules(),m=0;mh[0].length)){if(h=f,d=m,this.options.backtrack_lexer){if(u=this.test_match(f,p[m]),u!==!1)return u;if(this._backtrack){h=!1;continue}else return!1}else if(!this.options.flex)break}return h?(u=this.test_match(h,p[d]),u!==!1?u:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:o(function(){var h=this.next();return h||this.lex()},"lex"),begin:o(function(h){this.conditionStack.push(h)},"begin"),popState:o(function(){var h=this.conditionStack.length-1;return h>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:o(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:o(function(h){return h=this.conditionStack.length-1-Math.abs(h||0),h>=0?this.conditionStack[h]:"INITIAL"},"topState"),pushState:o(function(h){this.begin(h)},"pushState"),stateStackSize:o(function(){return this.conditionStack.length},"stateStackSize"),options:{"case-insensitive":!0},performAction:o(function(h,f,d,p){var m=p;switch(d){case 0:return this.pushState("csv"),4;break;case 1:return 10;case 2:return 5;case 3:return 12;case 4:return this.pushState("escaped_text"),18;break;case 5:return 20;case 6:return this.popState("escaped_text"),18;break;case 7:return 19}},"anonymous"),rules:[/^(?:sankey-beta\b)/i,/^(?:$)/i,/^(?:((\u000D\u000A)|(\u000A)))/i,/^(?:(\u002C))/i,/^(?:(\u0022))/i,/^(?:([\u0020-\u0021\u0023-\u002B\u002D-\u007E])*)/i,/^(?:(\u0022)(?!(\u0022)))/i,/^(?:(([\u0020-\u0021\u0023-\u002B\u002D-\u007E])|(\u002C)|(\u000D)|(\u000A)|(\u0022)(\u0022))*)/i],conditions:{csv:{rules:[1,2,3,4,5,6,7],inclusive:!1},escaped_text:{rules:[6,7],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,7],inclusive:!0}}};return l}();i.lexer=a;function s(){this.yy={}}return o(s,"Parser"),s.prototype=i,i.Parser=s,new s}();EB.parser=EB;cb=EB});var J6,eC,Z6,CZe,CB,SZe,SB,AZe,_Ze,LZe,DZe,wge,Tge=R(()=>{"use strict";_t();rr();bi();J6=[],eC=[],Z6=new Map,CZe=o(()=>{J6=[],eC=[],Z6=new Map,vr()},"clear"),CB=class{constructor(e,r,n=0){this.source=e;this.target=r;this.value=n}static{o(this,"SankeyLink")}},SZe=o((t,e,r)=>{J6.push(new CB(t,e,r))},"addLink"),SB=class{constructor(e){this.ID=e}static{o(this,"SankeyNode")}},AZe=o(t=>{t=We.sanitizeText(t,de());let e=Z6.get(t);return e===void 0&&(e=new SB(t),Z6.set(t,e),eC.push(e)),e},"findOrCreateNode"),_Ze=o(()=>eC,"getNodes"),LZe=o(()=>J6,"getLinks"),DZe=o(()=>({nodes:eC.map(t=>({id:t.ID})),links:J6.map(t=>({source:t.source.ID,target:t.target.ID,value:t.value}))}),"getGraph"),wge={nodesMap:Z6,getConfig:o(()=>de().sankey,"getConfig"),getNodes:_Ze,getLinks:LZe,getGraph:DZe,addLink:SZe,findOrCreateNode:AZe,getAccTitle:Ar,setAccTitle:kr,getAccDescription:Lr,setAccDescription:_r,getDiagramTitle:Xr,setDiagramTitle:nn,clear:CZe}});function ub(t,e){let r;if(e===void 0)for(let n of t)n!=null&&(r=n)&&(r=n);else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(r=i)&&(r=i)}return r}var kge=R(()=>{"use strict";o(ub,"max")});function x1(t,e){let r;if(e===void 0)for(let n of t)n!=null&&(r>n||r===void 0&&n>=n)&&(r=n);else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(r>i||r===void 0&&i>=i)&&(r=i)}return r}var Ege=R(()=>{"use strict";o(x1,"min")});function b1(t,e){let r=0;if(e===void 0)for(let n of t)(n=+n)&&(r+=n);else{let n=-1;for(let i of t)(i=+e(i,++n,t))&&(r+=i)}return r}var Cge=R(()=>{"use strict";o(b1,"sum")});var AB=R(()=>{"use strict";kge();Ege();Cge()});function RZe(t){return t.target.depth}function _B(t){return t.depth}function LB(t,e){return e-1-t.height}function hb(t,e){return t.sourceLinks.length?t.depth:e-1}function DB(t){return t.targetLinks.length?t.depth:t.sourceLinks.length?x1(t.sourceLinks,RZe)-1:0}var RB=R(()=>{"use strict";AB();o(RZe,"targetDepth");o(_B,"left");o(LB,"right");o(hb,"justify");o(DB,"center")});function w1(t){return function(){return t}}var Sge=R(()=>{"use strict";o(w1,"constant")});function Age(t,e){return tC(t.source,e.source)||t.index-e.index}function _ge(t,e){return tC(t.target,e.target)||t.index-e.index}function tC(t,e){return t.y0-e.y0}function NB(t){return t.value}function NZe(t){return t.index}function MZe(t){return t.nodes}function IZe(t){return t.links}function Lge(t,e){let r=t.get(e);if(!r)throw new Error("missing: "+e);return r}function Dge({nodes:t}){for(let e of t){let r=e.y0,n=r;for(let i of e.sourceLinks)i.y0=r+i.width/2,r+=i.width;for(let i of e.targetLinks)i.y1=n+i.width/2,n+=i.width}}function rC(){let t=0,e=0,r=1,n=1,i=24,a=8,s,l=NZe,u=hb,h,f,d=MZe,p=IZe,m=6;function g(){let O={nodes:d.apply(null,arguments),links:p.apply(null,arguments)};return y(O),v(O),x(O),b(O),T(O),Dge(O),O}o(g,"sankey"),g.update=function(O){return Dge(O),O},g.nodeId=function(O){return arguments.length?(l=typeof O=="function"?O:w1(O),g):l},g.nodeAlign=function(O){return arguments.length?(u=typeof O=="function"?O:w1(O),g):u},g.nodeSort=function(O){return arguments.length?(h=O,g):h},g.nodeWidth=function(O){return arguments.length?(i=+O,g):i},g.nodePadding=function(O){return arguments.length?(a=s=+O,g):a},g.nodes=function(O){return arguments.length?(d=typeof O=="function"?O:w1(O),g):d},g.links=function(O){return arguments.length?(p=typeof O=="function"?O:w1(O),g):p},g.linkSort=function(O){return arguments.length?(f=O,g):f},g.size=function(O){return arguments.length?(t=e=0,r=+O[0],n=+O[1],g):[r-t,n-e]},g.extent=function(O){return arguments.length?(t=+O[0][0],r=+O[1][0],e=+O[0][1],n=+O[1][1],g):[[t,e],[r,n]]},g.iterations=function(O){return arguments.length?(m=+O,g):m};function y({nodes:O,links:D}){for(let[F,B]of O.entries())B.index=F,B.sourceLinks=[],B.targetLinks=[];let P=new Map(O.map((F,B)=>[l(F,B,O),F]));for(let[F,B]of D.entries()){B.index=F;let{source:$,target:z}=B;typeof $!="object"&&($=B.source=Lge(P,$)),typeof z!="object"&&(z=B.target=Lge(P,z)),$.sourceLinks.push(B),z.targetLinks.push(B)}if(f!=null)for(let{sourceLinks:F,targetLinks:B}of O)F.sort(f),B.sort(f)}o(y,"computeNodeLinks");function v({nodes:O}){for(let D of O)D.value=D.fixedValue===void 0?Math.max(b1(D.sourceLinks,NB),b1(D.targetLinks,NB)):D.fixedValue}o(v,"computeNodeValues");function x({nodes:O}){let D=O.length,P=new Set(O),F=new Set,B=0;for(;P.size;){for(let $ of P){$.depth=B;for(let{target:z}of $.sourceLinks)F.add(z)}if(++B>D)throw new Error("circular link");P=F,F=new Set}}o(x,"computeNodeDepths");function b({nodes:O}){let D=O.length,P=new Set(O),F=new Set,B=0;for(;P.size;){for(let $ of P){$.height=B;for(let{source:z}of $.targetLinks)F.add(z)}if(++B>D)throw new Error("circular link");P=F,F=new Set}}o(b,"computeNodeHeights");function w({nodes:O}){let D=ub(O,B=>B.depth)+1,P=(r-t-i)/(D-1),F=new Array(D);for(let B of O){let $=Math.max(0,Math.min(D-1,Math.floor(u.call(null,B,D))));B.layer=$,B.x0=t+$*P,B.x1=B.x0+i,F[$]?F[$].push(B):F[$]=[B]}if(h)for(let B of F)B.sort(h);return F}o(w,"computeNodeLayers");function S(O){let D=x1(O,P=>(n-e-(P.length-1)*s)/b1(P,NB));for(let P of O){let F=e;for(let B of P){B.y0=F,B.y1=F+B.value*D,F=B.y1+s;for(let $ of B.sourceLinks)$.width=$.value*D}F=(n-F+s)/(P.length+1);for(let B=0;BP.length)-1)),S(D);for(let P=0;P0))continue;let X=(Y/Q-z.y0)*D;z.y0+=X,z.y1+=X,N(z)}h===void 0&&$.sort(tC),A($,P)}}o(E,"relaxLeftToRight");function _(O,D,P){for(let F=O.length,B=F-2;B>=0;--B){let $=O[B];for(let z of $){let Y=0,Q=0;for(let{target:ie,value:j}of z.sourceLinks){let J=j*(ie.layer-z.layer);Y+=C(z,ie)*J,Q+=J}if(!(Q>0))continue;let X=(Y/Q-z.y0)*D;z.y0+=X,z.y1+=X,N(z)}h===void 0&&$.sort(tC),A($,P)}}o(_,"relaxRightToLeft");function A(O,D){let P=O.length>>1,F=O[P];M(O,F.y0-s,P-1,D),L(O,F.y1+s,P+1,D),M(O,n,O.length-1,D),L(O,e,0,D)}o(A,"resolveCollisions");function L(O,D,P,F){for(;P1e-6&&(B.y0+=$,B.y1+=$),D=B.y1+s}}o(L,"resolveCollisionsTopToBottom");function M(O,D,P,F){for(;P>=0;--P){let B=O[P],$=(B.y1-D)*F;$>1e-6&&(B.y0-=$,B.y1-=$),D=B.y0-s}}o(M,"resolveCollisionsBottomToTop");function N({sourceLinks:O,targetLinks:D}){if(f===void 0){for(let{source:{sourceLinks:P}}of D)P.sort(_ge);for(let{target:{targetLinks:P}}of O)P.sort(Age)}}o(N,"reorderNodeLinks");function k(O){if(f===void 0)for(let{sourceLinks:D,targetLinks:P}of O)D.sort(_ge),P.sort(Age)}o(k,"reorderLinks");function I(O,D){let P=O.y0-(O.sourceLinks.length-1)*s/2;for(let{target:F,width:B}of O.sourceLinks){if(F===D)break;P+=B+s}for(let{source:F,width:B}of D.targetLinks){if(F===O)break;P-=B}return P}o(I,"targetTop");function C(O,D){let P=D.y0-(D.targetLinks.length-1)*s/2;for(let{source:F,width:B}of D.targetLinks){if(F===O)break;P+=B+s}for(let{target:F,width:B}of O.sourceLinks){if(F===D)break;P-=B}return P}return o(C,"sourceTop"),g}var Rge=R(()=>{"use strict";AB();RB();Sge();o(Age,"ascendingSourceBreadth");o(_ge,"ascendingTargetBreadth");o(tC,"ascendingBreadth");o(NB,"value");o(NZe,"defaultId");o(MZe,"defaultNodes");o(IZe,"defaultLinks");o(Lge,"find");o(Dge,"computeLinkBreadths");o(rC,"Sankey")});function OB(){this._x0=this._y0=this._x1=this._y1=null,this._=""}function Nge(){return new OB}var MB,IB,Q0,OZe,PB,Mge=R(()=>{"use strict";MB=Math.PI,IB=2*MB,Q0=1e-6,OZe=IB-Q0;o(OB,"Path");o(Nge,"path");OB.prototype=Nge.prototype={constructor:OB,moveTo:o(function(t,e){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)},"moveTo"),closePath:o(function(){this._x1!==null&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")},"closePath"),lineTo:o(function(t,e){this._+="L"+(this._x1=+t)+","+(this._y1=+e)},"lineTo"),quadraticCurveTo:o(function(t,e,r,n){this._+="Q"+ +t+","+ +e+","+(this._x1=+r)+","+(this._y1=+n)},"quadraticCurveTo"),bezierCurveTo:o(function(t,e,r,n,i,a){this._+="C"+ +t+","+ +e+","+ +r+","+ +n+","+(this._x1=+i)+","+(this._y1=+a)},"bezierCurveTo"),arcTo:o(function(t,e,r,n,i){t=+t,e=+e,r=+r,n=+n,i=+i;var a=this._x1,s=this._y1,l=r-t,u=n-e,h=a-t,f=s-e,d=h*h+f*f;if(i<0)throw new Error("negative radius: "+i);if(this._x1===null)this._+="M"+(this._x1=t)+","+(this._y1=e);else if(d>Q0)if(!(Math.abs(f*l-u*h)>Q0)||!i)this._+="L"+(this._x1=t)+","+(this._y1=e);else{var p=r-a,m=n-s,g=l*l+u*u,y=p*p+m*m,v=Math.sqrt(g),x=Math.sqrt(d),b=i*Math.tan((MB-Math.acos((g+d-y)/(2*v*x)))/2),w=b/x,S=b/v;Math.abs(w-1)>Q0&&(this._+="L"+(t+w*h)+","+(e+w*f)),this._+="A"+i+","+i+",0,0,"+ +(f*p>h*m)+","+(this._x1=t+S*l)+","+(this._y1=e+S*u)}},"arcTo"),arc:o(function(t,e,r,n,i,a){t=+t,e=+e,r=+r,a=!!a;var s=r*Math.cos(n),l=r*Math.sin(n),u=t+s,h=e+l,f=1^a,d=a?n-i:i-n;if(r<0)throw new Error("negative radius: "+r);this._x1===null?this._+="M"+u+","+h:(Math.abs(this._x1-u)>Q0||Math.abs(this._y1-h)>Q0)&&(this._+="L"+u+","+h),r&&(d<0&&(d=d%IB+IB),d>OZe?this._+="A"+r+","+r+",0,1,"+f+","+(t-s)+","+(e-l)+"A"+r+","+r+",0,1,"+f+","+(this._x1=u)+","+(this._y1=h):d>Q0&&(this._+="A"+r+","+r+",0,"+ +(d>=MB)+","+f+","+(this._x1=t+r*Math.cos(i))+","+(this._y1=e+r*Math.sin(i))))},"arc"),rect:o(function(t,e,r,n){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)+"h"+ +r+"v"+ +n+"h"+-r+"Z"},"rect"),toString:o(function(){return this._},"toString")};PB=Nge});var Ige=R(()=>{"use strict";Mge()});function nC(t){return o(function(){return t},"constant")}var Oge=R(()=>{"use strict";o(nC,"default")});function Pge(t){return t[0]}function Bge(t){return t[1]}var Fge=R(()=>{"use strict";o(Pge,"x");o(Bge,"y")});var zge,Gge=R(()=>{"use strict";zge=Array.prototype.slice});function PZe(t){return t.source}function BZe(t){return t.target}function FZe(t){var e=PZe,r=BZe,n=Pge,i=Bge,a=null;function s(){var l,u=zge.call(arguments),h=e.apply(this,u),f=r.apply(this,u);if(a||(a=l=PB()),t(a,+n.apply(this,(u[0]=h,u)),+i.apply(this,u),+n.apply(this,(u[0]=f,u)),+i.apply(this,u)),l)return a=null,l+""||null}return o(s,"link"),s.source=function(l){return arguments.length?(e=l,s):e},s.target=function(l){return arguments.length?(r=l,s):r},s.x=function(l){return arguments.length?(n=typeof l=="function"?l:nC(+l),s):n},s.y=function(l){return arguments.length?(i=typeof l=="function"?l:nC(+l),s):i},s.context=function(l){return arguments.length?(a=l??null,s):a},s}function zZe(t,e,r,n,i){t.moveTo(e,r),t.bezierCurveTo(e=(e+n)/2,r,e,i,n,i)}function BB(){return FZe(zZe)}var $ge=R(()=>{"use strict";Ige();Gge();Oge();Fge();o(PZe,"linkSource");o(BZe,"linkTarget");o(FZe,"link");o(zZe,"curveHorizontal");o(BB,"linkHorizontal")});var Vge=R(()=>{"use strict";$ge()});function GZe(t){return[t.source.x1,t.y0]}function $Ze(t){return[t.target.x0,t.y1]}function iC(){return BB().source(GZe).target($Ze)}var Uge=R(()=>{"use strict";Vge();o(GZe,"horizontalSource");o($Ze,"horizontalTarget");o(iC,"default")});var Hge=R(()=>{"use strict";Rge();RB();Uge()});var fb,Yge=R(()=>{"use strict";fb=class t{static{o(this,"Uid")}static{this.count=0}static next(e){return new t(e+ ++t.count)}constructor(e){this.id=e,this.href=`#${e}`}toString(){return"url("+this.href+")"}}});var VZe,UZe,Wge,qge=R(()=>{"use strict";_t();Zt();Hge();Yn();Yge();VZe={left:_B,right:LB,center:DB,justify:hb},UZe=o(function(t,e,r,n){let{securityLevel:i,sankey:a}=de(),s=_4.sankey,l;i==="sandbox"&&(l=$e("#i"+e));let u=i==="sandbox"?$e(l.nodes()[0].contentDocument.body):$e("body"),h=i==="sandbox"?u.select(`[id="${e}"]`):$e(`[id="${e}"]`),f=a?.width??s.width,d=a?.height??s.width,p=a?.useMaxWidth??s.useMaxWidth,m=a?.nodeAlignment??s.nodeAlignment,g=a?.prefix??s.prefix,y=a?.suffix??s.suffix,v=a?.showValues??s.showValues,x=n.db.getGraph(),b=VZe[m];rC().nodeId(M=>M.id).nodeWidth(10).nodePadding(10+(v?15:0)).nodeAlign(b).extent([[0,0],[f,d]])(x);let T=pu(Z8);h.append("g").attr("class","nodes").selectAll(".node").data(x.nodes).join("g").attr("class","node").attr("id",M=>(M.uid=fb.next("node-")).id).attr("transform",function(M){return"translate("+M.x0+","+M.y0+")"}).attr("x",M=>M.x0).attr("y",M=>M.y0).append("rect").attr("height",M=>M.y1-M.y0).attr("width",M=>M.x1-M.x0).attr("fill",M=>T(M.id));let E=o(({id:M,value:N})=>v?`${M} +${g}${Math.round(N*100)/100}${y}`:M,"getText");h.append("g").attr("class","node-labels").attr("font-family","sans-serif").attr("font-size",14).selectAll("text").data(x.nodes).join("text").attr("x",M=>M.x0(M.y1+M.y0)/2).attr("dy",`${v?"0":"0.35"}em`).attr("text-anchor",M=>M.x0(N.uid=fb.next("linearGradient-")).id).attr("gradientUnits","userSpaceOnUse").attr("x1",N=>N.source.x1).attr("x2",N=>N.target.x0);M.append("stop").attr("offset","0%").attr("stop-color",N=>T(N.source.id)),M.append("stop").attr("offset","100%").attr("stop-color",N=>T(N.target.id))}let L;switch(A){case"gradient":L=o(M=>M.uid,"coloring");break;case"source":L=o(M=>T(M.source.id),"coloring");break;case"target":L=o(M=>T(M.target.id),"coloring");break;default:L=A}_.append("path").attr("d",iC()).attr("stroke",L).attr("stroke-width",M=>Math.max(1,M.width)),Lo(void 0,h,0,p)},"draw"),Wge={draw:UZe}});var Xge,jge=R(()=>{"use strict";Xge=o(t=>t.replaceAll(/^[^\S\n\r]+|[^\S\n\r]+$/g,"").replaceAll(/([\n\r])+/g,` +`).trim(),"prepareTextForParsing")});var Kge={};hr(Kge,{diagram:()=>YZe});var HZe,YZe,Qge=R(()=>{"use strict";bge();Tge();qge();jge();HZe=cb.parse.bind(cb);cb.parse=t=>HZe(Xge(t));YZe={parser:cb,db:wge,renderer:Wge}});var e1e,FB,jZe,KZe,QZe,ZZe,JZe,Mf,zB=R(()=>{"use strict";qs();sl();xr();bi();e1e={packet:[]},FB=structuredClone(e1e),jZe=mr.packet,KZe=o(()=>{let t=Ts({...jZe,...Or().packet});return t.showBits&&(t.paddingY+=10),t},"getConfig"),QZe=o(()=>FB.packet,"getPacket"),ZZe=o(t=>{t.length>0&&FB.packet.push(t)},"pushWord"),JZe=o(()=>{vr(),FB=structuredClone(e1e)},"clear"),Mf={pushWord:ZZe,getPacket:QZe,getConfig:KZe,clear:JZe,setAccTitle:kr,getAccTitle:Ar,setDiagramTitle:nn,getDiagramTitle:Xr,getAccDescription:Lr,setAccDescription:_r}});var eJe,tJe,rJe,t1e,r1e=R(()=>{"use strict";Lg();ut();sx();zB();eJe=1e4,tJe=o(t=>{cf(t,Mf);let e=-1,r=[],n=1,{bitsPerRow:i}=Mf.getConfig();for(let{start:a,end:s,label:l}of t.blocks){if(s&&s{if(t.end===void 0&&(t.end=t.start),t.start>t.end)throw new Error(`Block start ${t.start} is greater than block end ${t.end}.`);return t.end+1<=e*r?[t,void 0]:[{start:t.start,end:e*r-1,label:t.label},{start:e*r,end:t.end,label:t.label}]},"getNextFittingBlock"),t1e={parse:o(async t=>{let e=await Fl("packet",t);V.debug(e),tJe(e)},"parse")}});var nJe,iJe,n1e,i1e=R(()=>{"use strict";pf();Yn();nJe=o((t,e,r,n)=>{let i=n.db,a=i.getConfig(),{rowHeight:s,paddingY:l,bitWidth:u,bitsPerRow:h}=a,f=i.getPacket(),d=i.getDiagramTitle(),p=s+l,m=p*(f.length+1)-(d?0:s),g=u*h+2,y=Ps(e);y.attr("viewbox",`0 0 ${g} ${m}`),Sr(y,m,g,a.useMaxWidth);for(let[v,x]of f.entries())iJe(y,x,v,a);y.append("text").text(d).attr("x",g/2).attr("y",m-p/2).attr("dominant-baseline","middle").attr("text-anchor","middle").attr("class","packetTitle")},"draw"),iJe=o((t,e,r,{rowHeight:n,paddingX:i,paddingY:a,bitWidth:s,bitsPerRow:l,showBits:u})=>{let h=t.append("g"),f=r*(n+a)+a;for(let d of e){let p=d.start%l*s+1,m=(d.end-d.start+1)*s-i;if(h.append("rect").attr("x",p).attr("y",f).attr("width",m).attr("height",n).attr("class","packetBlock"),h.append("text").attr("x",p+m/2).attr("y",f+n/2).attr("class","packetLabel").attr("dominant-baseline","middle").attr("text-anchor","middle").text(d.label),!u)continue;let g=d.end===d.start,y=f-2;h.append("text").attr("x",p+(g?m/2:0)).attr("y",y).attr("class","packetByte start").attr("dominant-baseline","auto").attr("text-anchor",g?"middle":"start").text(d.start),g||h.append("text").attr("x",p+m).attr("y",y).attr("class","packetByte end").attr("dominant-baseline","auto").attr("text-anchor","end").text(d.end)}},"drawWord"),n1e={draw:nJe}});var aJe,a1e,s1e=R(()=>{"use strict";xr();aJe={byteFontSize:"10px",startByteColor:"black",endByteColor:"black",labelColor:"black",labelFontSize:"12px",titleColor:"black",titleFontSize:"14px",blockStrokeColor:"black",blockStrokeWidth:"1",blockFillColor:"#efefef"},a1e=o(({packet:t}={})=>{let e=Ts(aJe,t);return` + .packetByte { + font-size: ${e.byteFontSize}; + } + .packetByte.start { + fill: ${e.startByteColor}; + } + .packetByte.end { + fill: ${e.endByteColor}; + } + .packetLabel { + fill: ${e.labelColor}; + font-size: ${e.labelFontSize}; + } + .packetTitle { + fill: ${e.titleColor}; + font-size: ${e.titleFontSize}; + } + .packetBlock { + stroke: ${e.blockStrokeColor}; + stroke-width: ${e.blockStrokeWidth}; + fill: ${e.blockFillColor}; + } + `},"styles")});var o1e={};hr(o1e,{diagram:()=>sJe});var sJe,l1e=R(()=>{"use strict";zB();r1e();i1e();s1e();sJe={parser:t1e,db:Mf,renderer:n1e,styles:a1e}});var GB,h1e,f1e=R(()=>{"use strict";GB=function(){var t=o(function(w,S,T,E){for(T=T||{},E=w.length;E--;T[w[E]]=S);return T},"o"),e=[1,7],r=[1,13],n=[1,14],i=[1,15],a=[1,19],s=[1,16],l=[1,17],u=[1,18],h=[8,30],f=[8,21,28,29,30,31,32,40,44,47],d=[1,23],p=[1,24],m=[8,15,16,21,28,29,30,31,32,40,44,47],g=[8,15,16,21,27,28,29,30,31,32,40,44,47],y=[1,49],v={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,spaceLines:3,SPACELINE:4,NL:5,separator:6,SPACE:7,EOF:8,start:9,BLOCK_DIAGRAM_KEY:10,document:11,stop:12,statement:13,link:14,LINK:15,START_LINK:16,LINK_LABEL:17,STR:18,nodeStatement:19,columnsStatement:20,SPACE_BLOCK:21,blockStatement:22,classDefStatement:23,cssClassStatement:24,styleStatement:25,node:26,SIZE:27,COLUMNS:28,"id-block":29,end:30,block:31,NODE_ID:32,nodeShapeNLabel:33,dirList:34,DIR:35,NODE_DSTART:36,NODE_DEND:37,BLOCK_ARROW_START:38,BLOCK_ARROW_END:39,classDef:40,CLASSDEF_ID:41,CLASSDEF_STYLEOPTS:42,DEFAULT:43,class:44,CLASSENTITY_IDS:45,STYLECLASS:46,style:47,STYLE_ENTITY_IDS:48,STYLE_DEFINITION_DATA:49,$accept:0,$end:1},terminals_:{2:"error",4:"SPACELINE",5:"NL",7:"SPACE",8:"EOF",10:"BLOCK_DIAGRAM_KEY",15:"LINK",16:"START_LINK",17:"LINK_LABEL",18:"STR",21:"SPACE_BLOCK",27:"SIZE",28:"COLUMNS",29:"id-block",30:"end",31:"block",32:"NODE_ID",35:"DIR",36:"NODE_DSTART",37:"NODE_DEND",38:"BLOCK_ARROW_START",39:"BLOCK_ARROW_END",40:"classDef",41:"CLASSDEF_ID",42:"CLASSDEF_STYLEOPTS",43:"DEFAULT",44:"class",45:"CLASSENTITY_IDS",46:"STYLECLASS",47:"style",48:"STYLE_ENTITY_IDS",49:"STYLE_DEFINITION_DATA"},productions_:[0,[3,1],[3,2],[3,2],[6,1],[6,1],[6,1],[9,3],[12,1],[12,1],[12,2],[12,2],[11,1],[11,2],[14,1],[14,4],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[19,3],[19,2],[19,1],[20,1],[22,4],[22,3],[26,1],[26,2],[34,1],[34,2],[33,3],[33,4],[23,3],[23,3],[24,3],[25,3]],performAction:o(function(S,T,E,_,A,L,M){var N=L.length-1;switch(A){case 4:_.getLogger().debug("Rule: separator (NL) ");break;case 5:_.getLogger().debug("Rule: separator (Space) ");break;case 6:_.getLogger().debug("Rule: separator (EOF) ");break;case 7:_.getLogger().debug("Rule: hierarchy: ",L[N-1]),_.setHierarchy(L[N-1]);break;case 8:_.getLogger().debug("Stop NL ");break;case 9:_.getLogger().debug("Stop EOF ");break;case 10:_.getLogger().debug("Stop NL2 ");break;case 11:_.getLogger().debug("Stop EOF2 ");break;case 12:_.getLogger().debug("Rule: statement: ",L[N]),typeof L[N].length=="number"?this.$=L[N]:this.$=[L[N]];break;case 13:_.getLogger().debug("Rule: statement #2: ",L[N-1]),this.$=[L[N-1]].concat(L[N]);break;case 14:_.getLogger().debug("Rule: link: ",L[N],S),this.$={edgeTypeStr:L[N],label:""};break;case 15:_.getLogger().debug("Rule: LABEL link: ",L[N-3],L[N-1],L[N]),this.$={edgeTypeStr:L[N],label:L[N-1]};break;case 18:let k=parseInt(L[N]),I=_.generateId();this.$={id:I,type:"space",label:"",width:k,children:[]};break;case 23:_.getLogger().debug("Rule: (nodeStatement link node) ",L[N-2],L[N-1],L[N]," typestr: ",L[N-1].edgeTypeStr);let C=_.edgeStrToEdgeData(L[N-1].edgeTypeStr);this.$=[{id:L[N-2].id,label:L[N-2].label,type:L[N-2].type,directions:L[N-2].directions},{id:L[N-2].id+"-"+L[N].id,start:L[N-2].id,end:L[N].id,label:L[N-1].label,type:"edge",directions:L[N].directions,arrowTypeEnd:C,arrowTypeStart:"arrow_open"},{id:L[N].id,label:L[N].label,type:_.typeStr2Type(L[N].typeStr),directions:L[N].directions}];break;case 24:_.getLogger().debug("Rule: nodeStatement (abc88 node size) ",L[N-1],L[N]),this.$={id:L[N-1].id,label:L[N-1].label,type:_.typeStr2Type(L[N-1].typeStr),directions:L[N-1].directions,widthInColumns:parseInt(L[N],10)};break;case 25:_.getLogger().debug("Rule: nodeStatement (node) ",L[N]),this.$={id:L[N].id,label:L[N].label,type:_.typeStr2Type(L[N].typeStr),directions:L[N].directions,widthInColumns:1};break;case 26:_.getLogger().debug("APA123",this?this:"na"),_.getLogger().debug("COLUMNS: ",L[N]),this.$={type:"column-setting",columns:L[N]==="auto"?-1:parseInt(L[N])};break;case 27:_.getLogger().debug("Rule: id-block statement : ",L[N-2],L[N-1]);let O=_.generateId();this.$={...L[N-2],type:"composite",children:L[N-1]};break;case 28:_.getLogger().debug("Rule: blockStatement : ",L[N-2],L[N-1],L[N]);let D=_.generateId();this.$={id:D,type:"composite",label:"",children:L[N-1]};break;case 29:_.getLogger().debug("Rule: node (NODE_ID separator): ",L[N]),this.$={id:L[N]};break;case 30:_.getLogger().debug("Rule: node (NODE_ID nodeShapeNLabel separator): ",L[N-1],L[N]),this.$={id:L[N-1],label:L[N].label,typeStr:L[N].typeStr,directions:L[N].directions};break;case 31:_.getLogger().debug("Rule: dirList: ",L[N]),this.$=[L[N]];break;case 32:_.getLogger().debug("Rule: dirList: ",L[N-1],L[N]),this.$=[L[N-1]].concat(L[N]);break;case 33:_.getLogger().debug("Rule: nodeShapeNLabel: ",L[N-2],L[N-1],L[N]),this.$={typeStr:L[N-2]+L[N],label:L[N-1]};break;case 34:_.getLogger().debug("Rule: BLOCK_ARROW nodeShapeNLabel: ",L[N-3],L[N-2]," #3:",L[N-1],L[N]),this.$={typeStr:L[N-3]+L[N],label:L[N-2],directions:L[N-1]};break;case 35:case 36:this.$={type:"classDef",id:L[N-1].trim(),css:L[N].trim()};break;case 37:this.$={type:"applyClass",id:L[N-1].trim(),styleClass:L[N].trim()};break;case 38:this.$={type:"applyStyles",id:L[N-1].trim(),stylesStr:L[N].trim()};break}},"anonymous"),table:[{9:1,10:[1,2]},{1:[3]},{11:3,13:4,19:5,20:6,21:e,22:8,23:9,24:10,25:11,26:12,28:r,29:n,31:i,32:a,40:s,44:l,47:u},{8:[1,20]},t(h,[2,12],{13:4,19:5,20:6,22:8,23:9,24:10,25:11,26:12,11:21,21:e,28:r,29:n,31:i,32:a,40:s,44:l,47:u}),t(f,[2,16],{14:22,15:d,16:p}),t(f,[2,17]),t(f,[2,18]),t(f,[2,19]),t(f,[2,20]),t(f,[2,21]),t(f,[2,22]),t(m,[2,25],{27:[1,25]}),t(f,[2,26]),{19:26,26:12,32:a},{11:27,13:4,19:5,20:6,21:e,22:8,23:9,24:10,25:11,26:12,28:r,29:n,31:i,32:a,40:s,44:l,47:u},{41:[1,28],43:[1,29]},{45:[1,30]},{48:[1,31]},t(g,[2,29],{33:32,36:[1,33],38:[1,34]}),{1:[2,7]},t(h,[2,13]),{26:35,32:a},{32:[2,14]},{17:[1,36]},t(m,[2,24]),{11:37,13:4,14:22,15:d,16:p,19:5,20:6,21:e,22:8,23:9,24:10,25:11,26:12,28:r,29:n,31:i,32:a,40:s,44:l,47:u},{30:[1,38]},{42:[1,39]},{42:[1,40]},{46:[1,41]},{49:[1,42]},t(g,[2,30]),{18:[1,43]},{18:[1,44]},t(m,[2,23]),{18:[1,45]},{30:[1,46]},t(f,[2,28]),t(f,[2,35]),t(f,[2,36]),t(f,[2,37]),t(f,[2,38]),{37:[1,47]},{34:48,35:y},{15:[1,50]},t(f,[2,27]),t(g,[2,33]),{39:[1,51]},{34:52,35:y,39:[2,31]},{32:[2,15]},t(g,[2,34]),{39:[2,32]}],defaultActions:{20:[2,7],23:[2,14],50:[2,15],52:[2,32]},parseError:o(function(S,T){if(T.recoverable)this.trace(S);else{var E=new Error(S);throw E.hash=T,E}},"parseError"),parse:o(function(S){var T=this,E=[0],_=[],A=[null],L=[],M=this.table,N="",k=0,I=0,C=0,O=2,D=1,P=L.slice.call(arguments,1),F=Object.create(this.lexer),B={yy:{}};for(var $ in this.yy)Object.prototype.hasOwnProperty.call(this.yy,$)&&(B.yy[$]=this.yy[$]);F.setInput(S,B.yy),B.yy.lexer=F,B.yy.parser=this,typeof F.yylloc>"u"&&(F.yylloc={});var z=F.yylloc;L.push(z);var Y=F.options&&F.options.ranges;typeof B.yy.parseError=="function"?this.parseError=B.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function Q(oe){E.length=E.length-2*oe,A.length=A.length-oe,L.length=L.length-oe}o(Q,"popStack");function X(){var oe;return oe=_.pop()||F.lex()||D,typeof oe!="number"&&(oe instanceof Array&&(_=oe,oe=_.pop()),oe=T.symbols_[oe]||oe),oe}o(X,"lex");for(var ie,j,J,Z,H,q,K={},se,ce,ue,te;;){if(J=E[E.length-1],this.defaultActions[J]?Z=this.defaultActions[J]:((ie===null||typeof ie>"u")&&(ie=X()),Z=M[J]&&M[J][ie]),typeof Z>"u"||!Z.length||!Z[0]){var De="";te=[];for(se in M[J])this.terminals_[se]&&se>O&&te.push("'"+this.terminals_[se]+"'");F.showPosition?De="Parse error on line "+(k+1)+`: +`+F.showPosition()+` +Expecting `+te.join(", ")+", got '"+(this.terminals_[ie]||ie)+"'":De="Parse error on line "+(k+1)+": Unexpected "+(ie==D?"end of input":"'"+(this.terminals_[ie]||ie)+"'"),this.parseError(De,{text:F.match,token:this.terminals_[ie]||ie,line:F.yylineno,loc:z,expected:te})}if(Z[0]instanceof Array&&Z.length>1)throw new Error("Parse Error: multiple actions possible at state: "+J+", token: "+ie);switch(Z[0]){case 1:E.push(ie),A.push(F.yytext),L.push(F.yylloc),E.push(Z[1]),ie=null,j?(ie=j,j=null):(I=F.yyleng,N=F.yytext,k=F.yylineno,z=F.yylloc,C>0&&C--);break;case 2:if(ce=this.productions_[Z[1]][1],K.$=A[A.length-ce],K._$={first_line:L[L.length-(ce||1)].first_line,last_line:L[L.length-1].last_line,first_column:L[L.length-(ce||1)].first_column,last_column:L[L.length-1].last_column},Y&&(K._$.range=[L[L.length-(ce||1)].range[0],L[L.length-1].range[1]]),q=this.performAction.apply(K,[N,I,k,B.yy,Z[1],A,L].concat(P)),typeof q<"u")return q;ce&&(E=E.slice(0,-1*ce*2),A=A.slice(0,-1*ce),L=L.slice(0,-1*ce)),E.push(this.productions_[Z[1]][0]),A.push(K.$),L.push(K._$),ue=M[E[E.length-2]][E[E.length-1]],E.push(ue);break;case 3:return!0}}return!0},"parse")},x=function(){var w={EOF:1,parseError:o(function(T,E){if(this.yy.parser)this.yy.parser.parseError(T,E);else throw new Error(T)},"parseError"),setInput:o(function(S,T){return this.yy=T||this.yy||{},this._input=S,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var S=this._input[0];this.yytext+=S,this.yyleng++,this.offset++,this.match+=S,this.matched+=S;var T=S.match(/(?:\r\n?|\n).*/g);return T?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),S},"input"),unput:o(function(S){var T=S.length,E=S.split(/(?:\r\n?|\n)/g);this._input=S+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-T),this.offset-=T;var _=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),E.length-1&&(this.yylineno-=E.length-1);var A=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:E?(E.length===_.length?this.yylloc.first_column:0)+_[_.length-E.length].length-E[0].length:this.yylloc.first_column-T},this.options.ranges&&(this.yylloc.range=[A[0],A[0]+this.yyleng-T]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). +`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(S){this.unput(this.match.slice(S))},"less"),pastInput:o(function(){var S=this.matched.substr(0,this.matched.length-this.match.length);return(S.length>20?"...":"")+S.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var S=this.match;return S.length<20&&(S+=this._input.substr(0,20-S.length)),(S.substr(0,20)+(S.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var S=this.pastInput(),T=new Array(S.length+1).join("-");return S+this.upcomingInput()+` +`+T+"^"},"showPosition"),test_match:o(function(S,T){var E,_,A;if(this.options.backtrack_lexer&&(A={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(A.yylloc.range=this.yylloc.range.slice(0))),_=S[0].match(/(?:\r\n?|\n).*/g),_&&(this.yylineno+=_.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:_?_[_.length-1].length-_[_.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+S[0].length},this.yytext+=S[0],this.match+=S[0],this.matches=S,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(S[0].length),this.matched+=S[0],E=this.performAction.call(this,this.yy,this,T,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),E)return E;if(this._backtrack){for(var L in A)this[L]=A[L];return!1}return!1},"test_match"),next:o(function(){if(this.done)return this.EOF;this._input||(this.done=!0);var S,T,E,_;this._more||(this.yytext="",this.match="");for(var A=this._currentRules(),L=0;LT[0].length)){if(T=E,_=L,this.options.backtrack_lexer){if(S=this.test_match(E,A[L]),S!==!1)return S;if(this._backtrack){T=!1;continue}else return!1}else if(!this.options.flex)break}return T?(S=this.test_match(T,A[_]),S!==!1?S:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text. +`+this.showPosition(),{text:"",token:null,line:this.yylineno})},"next"),lex:o(function(){var T=this.next();return T||this.lex()},"lex"),begin:o(function(T){this.conditionStack.push(T)},"begin"),popState:o(function(){var T=this.conditionStack.length-1;return T>0?this.conditionStack.pop():this.conditionStack[0]},"popState"),_currentRules:o(function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},"_currentRules"),topState:o(function(T){return T=this.conditionStack.length-1-Math.abs(T||0),T>=0?this.conditionStack[T]:"INITIAL"},"topState"),pushState:o(function(T){this.begin(T)},"pushState"),stateStackSize:o(function(){return this.conditionStack.length},"stateStackSize"),options:{},performAction:o(function(T,E,_,A){var L=A;switch(_){case 0:return 10;case 1:return T.getLogger().debug("Found space-block"),31;break;case 2:return T.getLogger().debug("Found nl-block"),31;break;case 3:return T.getLogger().debug("Found space-block"),29;break;case 4:T.getLogger().debug(".",E.yytext);break;case 5:T.getLogger().debug("_",E.yytext);break;case 6:return 5;case 7:return E.yytext=-1,28;break;case 8:return E.yytext=E.yytext.replace(/columns\s+/,""),T.getLogger().debug("COLUMNS (LEX)",E.yytext),28;break;case 9:this.pushState("md_string");break;case 10:return"MD_STR";case 11:this.popState();break;case 12:this.pushState("string");break;case 13:T.getLogger().debug("LEX: POPPING STR:",E.yytext),this.popState();break;case 14:return T.getLogger().debug("LEX: STR end:",E.yytext),"STR";break;case 15:return E.yytext=E.yytext.replace(/space\:/,""),T.getLogger().debug("SPACE NUM (LEX)",E.yytext),21;break;case 16:return E.yytext="1",T.getLogger().debug("COLUMNS (LEX)",E.yytext),21;break;case 17:return 43;case 18:return"LINKSTYLE";case 19:return"INTERPOLATE";case 20:return this.pushState("CLASSDEF"),40;break;case 21:return this.popState(),this.pushState("CLASSDEFID"),"DEFAULT_CLASSDEF_ID";break;case 22:return this.popState(),this.pushState("CLASSDEFID"),41;break;case 23:return this.popState(),42;break;case 24:return this.pushState("CLASS"),44;break;case 25:return this.popState(),this.pushState("CLASS_STYLE"),45;break;case 26:return this.popState(),46;break;case 27:return this.pushState("STYLE_STMNT"),47;break;case 28:return this.popState(),this.pushState("STYLE_DEFINITION"),48;break;case 29:return this.popState(),49;break;case 30:return this.pushState("acc_title"),"acc_title";break;case 31:return this.popState(),"acc_title_value";break;case 32:return this.pushState("acc_descr"),"acc_descr";break;case 33:return this.popState(),"acc_descr_value";break;case 34:this.pushState("acc_descr_multiline");break;case 35:this.popState();break;case 36:return"acc_descr_multiline_value";case 37:return 30;case 38:return this.popState(),T.getLogger().debug("Lex: (("),"NODE_DEND";break;case 39:return this.popState(),T.getLogger().debug("Lex: (("),"NODE_DEND";break;case 40:return this.popState(),T.getLogger().debug("Lex: ))"),"NODE_DEND";break;case 41:return this.popState(),T.getLogger().debug("Lex: (("),"NODE_DEND";break;case 42:return this.popState(),T.getLogger().debug("Lex: (("),"NODE_DEND";break;case 43:return this.popState(),T.getLogger().debug("Lex: (-"),"NODE_DEND";break;case 44:return this.popState(),T.getLogger().debug("Lex: -)"),"NODE_DEND";break;case 45:return this.popState(),T.getLogger().debug("Lex: (("),"NODE_DEND";break;case 46:return this.popState(),T.getLogger().debug("Lex: ]]"),"NODE_DEND";break;case 47:return this.popState(),T.getLogger().debug("Lex: ("),"NODE_DEND";break;case 48:return this.popState(),T.getLogger().debug("Lex: ])"),"NODE_DEND";break;case 49:return this.popState(),T.getLogger().debug("Lex: /]"),"NODE_DEND";break;case 50:return this.popState(),T.getLogger().debug("Lex: /]"),"NODE_DEND";break;case 51:return this.popState(),T.getLogger().debug("Lex: )]"),"NODE_DEND";break;case 52:return this.popState(),T.getLogger().debug("Lex: )"),"NODE_DEND";break;case 53:return this.popState(),T.getLogger().debug("Lex: ]>"),"NODE_DEND";break;case 54:return this.popState(),T.getLogger().debug("Lex: ]"),"NODE_DEND";break;case 55:return T.getLogger().debug("Lexa: -)"),this.pushState("NODE"),36;break;case 56:return T.getLogger().debug("Lexa: (-"),this.pushState("NODE"),36;break;case 57:return T.getLogger().debug("Lexa: ))"),this.pushState("NODE"),36;break;case 58:return T.getLogger().debug("Lexa: )"),this.pushState("NODE"),36;break;case 59:return T.getLogger().debug("Lex: ((("),this.pushState("NODE"),36;break;case 60:return T.getLogger().debug("Lexa: )"),this.pushState("NODE"),36;break;case 61:return T.getLogger().debug("Lexa: )"),this.pushState("NODE"),36;break;case 62:return T.getLogger().debug("Lexa: )"),this.pushState("NODE"),36;break;case 63:return T.getLogger().debug("Lexc: >"),this.pushState("NODE"),36;break;case 64:return T.getLogger().debug("Lexa: (["),this.pushState("NODE"),36;break;case 65:return T.getLogger().debug("Lexa: )"),this.pushState("NODE"),36;break;case 66:return this.pushState("NODE"),36;break;case 67:return this.pushState("NODE"),36;break;case 68:return this.pushState("NODE"),36;break;case 69:return this.pushState("NODE"),36;break;case 70:return this.pushState("NODE"),36;break;case 71:return this.pushState("NODE"),36;break;case 72:return this.pushState("NODE"),36;break;case 73:return T.getLogger().debug("Lexa: ["),this.pushState("NODE"),36;break;case 74:return this.pushState("BLOCK_ARROW"),T.getLogger().debug("LEX ARR START"),38;break;case 75:return T.getLogger().debug("Lex: NODE_ID",E.yytext),32;break;case 76:return T.getLogger().debug("Lex: EOF",E.yytext),8;break;case 77:this.pushState("md_string");break;case 78:this.pushState("md_string");break;case 79:return"NODE_DESCR";case 80:this.popState();break;case 81:T.getLogger().debug("Lex: Starting string"),this.pushState("string");break;case 82:T.getLogger().debug("LEX ARR: Starting string"),this.pushState("string");break;case 83:return T.getLogger().debug("LEX: NODE_DESCR:",E.yytext),"NODE_DESCR";break;case 84:T.getLogger().debug("LEX POPPING"),this.popState();break;case 85:T.getLogger().debug("Lex: =>BAE"),this.pushState("ARROW_DIR");break;case 86:return E.yytext=E.yytext.replace(/^,\s*/,""),T.getLogger().debug("Lex (right): dir:",E.yytext),"DIR";break;case 87:return E.yytext=E.yytext.replace(/^,\s*/,""),T.getLogger().debug("Lex (left):",E.yytext),"DIR";break;case 88:return E.yytext=E.yytext.replace(/^,\s*/,""),T.getLogger().debug("Lex (x):",E.yytext),"DIR";break;case 89:return E.yytext=E.yytext.replace(/^,\s*/,""),T.getLogger().debug("Lex (y):",E.yytext),"DIR";break;case 90:return E.yytext=E.yytext.replace(/^,\s*/,""),T.getLogger().debug("Lex (up):",E.yytext),"DIR";break;case 91:return E.yytext=E.yytext.replace(/^,\s*/,""),T.getLogger().debug("Lex (down):",E.yytext),"DIR";break;case 92:return E.yytext="]>",T.getLogger().debug("Lex (ARROW_DIR end):",E.yytext),this.popState(),this.popState(),"BLOCK_ARROW_END";break;case 93:return T.getLogger().debug("Lex: LINK","#"+E.yytext+"#"),15;break;case 94:return T.getLogger().debug("Lex: LINK",E.yytext),15;break;case 95:return T.getLogger().debug("Lex: LINK",E.yytext),15;break;case 96:return T.getLogger().debug("Lex: LINK",E.yytext),15;break;case 97:return T.getLogger().debug("Lex: START_LINK",E.yytext),this.pushState("LLABEL"),16;break;case 98:return T.getLogger().debug("Lex: START_LINK",E.yytext),this.pushState("LLABEL"),16;break;case 99:return T.getLogger().debug("Lex: START_LINK",E.yytext),this.pushState("LLABEL"),16;break;case 100:this.pushState("md_string");break;case 101:return T.getLogger().debug("Lex: Starting string"),this.pushState("string"),"LINK_LABEL";break;case 102:return this.popState(),T.getLogger().debug("Lex: LINK","#"+E.yytext+"#"),15;break;case 103:return this.popState(),T.getLogger().debug("Lex: LINK",E.yytext),15;break;case 104:return this.popState(),T.getLogger().debug("Lex: LINK",E.yytext),15;break;case 105:return T.getLogger().debug("Lex: COLON",E.yytext),E.yytext=E.yytext.slice(1),27;break}},"anonymous"),rules:[/^(?:block-beta\b)/,/^(?:block\s+)/,/^(?:block\n+)/,/^(?:block:)/,/^(?:[\s]+)/,/^(?:[\n]+)/,/^(?:((\u000D\u000A)|(\u000A)))/,/^(?:columns\s+auto\b)/,/^(?:columns\s+[\d]+)/,/^(?:["][`])/,/^(?:[^`"]+)/,/^(?:[`]["])/,/^(?:["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:space[:]\d+)/,/^(?:space\b)/,/^(?:default\b)/,/^(?:linkStyle\b)/,/^(?:interpolate\b)/,/^(?:classDef\s+)/,/^(?:DEFAULT\s+)/,/^(?:\w+\s+)/,/^(?:[^\n]*)/,/^(?:class\s+)/,/^(?:(\w+)+((,\s*\w+)*))/,/^(?:[^\n]*)/,/^(?:style\s+)/,/^(?:(\w+)+((,\s*\w+)*))/,/^(?:[^\n]*)/,/^(?:accTitle\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*\{\s*)/,/^(?:[\}])/,/^(?:[^\}]*)/,/^(?:end\b\s*)/,/^(?:\(\(\()/,/^(?:\)\)\))/,/^(?:[\)]\))/,/^(?:\}\})/,/^(?:\})/,/^(?:\(-)/,/^(?:-\))/,/^(?:\(\()/,/^(?:\]\])/,/^(?:\()/,/^(?:\]\))/,/^(?:\\\])/,/^(?:\/\])/,/^(?:\)\])/,/^(?:[\)])/,/^(?:\]>)/,/^(?:[\]])/,/^(?:-\))/,/^(?:\(-)/,/^(?:\)\))/,/^(?:\))/,/^(?:\(\(\()/,/^(?:\(\()/,/^(?:\{\{)/,/^(?:\{)/,/^(?:>)/,/^(?:\(\[)/,/^(?:\()/,/^(?:\[\[)/,/^(?:\[\|)/,/^(?:\[\()/,/^(?:\)\)\))/,/^(?:\[\\)/,/^(?:\[\/)/,/^(?:\[\\)/,/^(?:\[)/,/^(?:<\[)/,/^(?:[^\(\[\n\-\)\{\}\s\<\>:]+)/,/^(?:$)/,/^(?:["][`])/,/^(?:["][`])/,/^(?:[^`"]+)/,/^(?:[`]["])/,/^(?:["])/,/^(?:["])/,/^(?:[^"]+)/,/^(?:["])/,/^(?:\]>\s*\()/,/^(?:,?\s*right\s*)/,/^(?:,?\s*left\s*)/,/^(?:,?\s*x\s*)/,/^(?:,?\s*y\s*)/,/^(?:,?\s*up\s*)/,/^(?:,?\s*down\s*)/,/^(?:\)\s*)/,/^(?:\s*[xo<]?--+[-xo>]\s*)/,/^(?:\s*[xo<]?==+[=xo>]\s*)/,/^(?:\s*[xo<]?-?\.+-[xo>]?\s*)/,/^(?:\s*~~[\~]+\s*)/,/^(?:\s*[xo<]?--\s*)/,/^(?:\s*[xo<]?==\s*)/,/^(?:\s*[xo<]?-\.\s*)/,/^(?:["][`])/,/^(?:["])/,/^(?:\s*[xo<]?--+[-xo>]\s*)/,/^(?:\s*[xo<]?==+[=xo>]\s*)/,/^(?:\s*[xo<]?-?\.+-[xo>]?\s*)/,/^(?::\d+)/],conditions:{STYLE_DEFINITION:{rules:[29],inclusive:!1},STYLE_STMNT:{rules:[28],inclusive:!1},CLASSDEFID:{rules:[23],inclusive:!1},CLASSDEF:{rules:[21,22],inclusive:!1},CLASS_STYLE:{rules:[26],inclusive:!1},CLASS:{rules:[25],inclusive:!1},LLABEL:{rules:[100,101,102,103,104],inclusive:!1},ARROW_DIR:{rules:[86,87,88,89,90,91,92],inclusive:!1},BLOCK_ARROW:{rules:[77,82,85],inclusive:!1},NODE:{rules:[38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,78,81],inclusive:!1},md_string:{rules:[10,11,79,80],inclusive:!1},space:{rules:[],inclusive:!1},string:{rules:[13,14,83,84],inclusive:!1},acc_descr_multiline:{rules:[35,36],inclusive:!1},acc_descr:{rules:[33],inclusive:!1},acc_title:{rules:[31],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,12,15,16,17,18,19,20,24,27,30,32,34,37,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,93,94,95,96,97,98,99,105],inclusive:!0}}};return w}();v.lexer=x;function b(){this.yy={}}return o(b,"Parser"),b.prototype=v,v.Parser=b,new b}();GB.parser=GB;h1e=GB});function gJe(t){switch(V.debug("typeStr2Type",t),t){case"[]":return"square";case"()":return V.debug("we have a round"),"round";case"(())":return"circle";case">]":return"rect_left_inv_arrow";case"{}":return"diamond";case"{{}}":return"hexagon";case"([])":return"stadium";case"[[]]":return"subroutine";case"[()]":return"cylinder";case"((()))":return"doublecircle";case"[//]":return"lean_right";case"[\\\\]":return"lean_left";case"[/\\]":return"trapezoid";case"[\\/]":return"inv_trapezoid";case"<[]>":return"block_arrow";default:return"na"}}function yJe(t){switch(V.debug("typeStr2Type",t),t){case"==":return"thick";default:return"normal"}}function vJe(t){switch(t.trim()){case"--x":return"arrow_cross";case"--o":return"arrow_circle";default:return"arrow_point"}}var Yl,VB,$B,d1e,p1e,cJe,g1e,uJe,aC,hJe,fJe,dJe,pJe,y1e,UB,db,mJe,m1e,xJe,bJe,wJe,TJe,kJe,EJe,CJe,SJe,AJe,_Je,LJe,v1e,x1e=R(()=>{"use strict";gL();qs();_t();ut();rr();bi();Yl=new Map,VB=[],$B=new Map,d1e="color",p1e="fill",cJe="bgFill",g1e=",",uJe=de(),aC=new Map,hJe=o(t=>We.sanitizeText(t,uJe),"sanitizeText"),fJe=o(function(t,e=""){let r=aC.get(t);r||(r={id:t,styles:[],textStyles:[]},aC.set(t,r)),e?.split(g1e).forEach(n=>{let i=n.replace(/([^;]*);/,"$1").trim();if(RegExp(d1e).exec(n)){let s=i.replace(p1e,cJe).replace(d1e,p1e);r.textStyles.push(s)}r.styles.push(i)})},"addStyleClass"),dJe=o(function(t,e=""){let r=Yl.get(t);e!=null&&(r.styles=e.split(g1e))},"addStyle2Node"),pJe=o(function(t,e){t.split(",").forEach(function(r){let n=Yl.get(r);if(n===void 0){let i=r.trim();n={id:i,type:"na",children:[]},Yl.set(i,n)}n.classes||(n.classes=[]),n.classes.push(e)})},"setCssClass"),y1e=o((t,e)=>{let r=t.flat(),n=[];for(let i of r){if(i.label&&(i.label=hJe(i.label)),i.type==="classDef"){fJe(i.id,i.css);continue}if(i.type==="applyClass"){pJe(i.id,i?.styleClass??"");continue}if(i.type==="applyStyles"){i?.stylesStr&&dJe(i.id,i?.stylesStr);continue}if(i.type==="column-setting")e.columns=i.columns??-1;else if(i.type==="edge"){let a=($B.get(i.id)??0)+1;$B.set(i.id,a),i.id=a+"-"+i.id,VB.push(i)}else{i.label||(i.type==="composite"?i.label="":i.label=i.id);let a=Yl.get(i.id);if(a===void 0?Yl.set(i.id,i):(i.type!=="na"&&(a.type=i.type),i.label!==i.id&&(a.label=i.label)),i.children&&y1e(i.children,i),i.type==="space"){let s=i.width??1;for(let l=0;l{V.debug("Clear called"),vr(),db={id:"root",type:"composite",children:[],columns:-1},Yl=new Map([["root",db]]),UB=[],aC=new Map,VB=[],$B=new Map},"clear");o(gJe,"typeStr2Type");o(yJe,"edgeTypeStr2Type");o(vJe,"edgeStrToEdgeData");m1e=0,xJe=o(()=>(m1e++,"id-"+Math.random().toString(36).substr(2,12)+"-"+m1e),"generateId"),bJe=o(t=>{db.children=t,y1e(t,db),UB=db.children},"setHierarchy"),wJe=o(t=>{let e=Yl.get(t);return e?e.columns?e.columns:e.children?e.children.length:-1:-1},"getColumns"),TJe=o(()=>[...Yl.values()],"getBlocksFlat"),kJe=o(()=>UB||[],"getBlocks"),EJe=o(()=>VB,"getEdges"),CJe=o(t=>Yl.get(t),"getBlock"),SJe=o(t=>{Yl.set(t.id,t)},"setBlock"),AJe=o(()=>console,"getLogger"),_Je=o(function(){return aC},"getClasses"),LJe={getConfig:o(()=>Or().block,"getConfig"),typeStr2Type:gJe,edgeTypeStr2Type:yJe,edgeStrToEdgeData:vJe,getLogger:AJe,getBlocksFlat:TJe,getBlocks:kJe,getEdges:EJe,setHierarchy:bJe,getBlock:CJe,setBlock:SJe,getColumns:wJe,getClasses:_Je,clear:mJe,generateId:xJe},v1e=LJe});var sC,DJe,b1e,w1e=R(()=>{"use strict";al();sC=o((t,e)=>{let r=X1,n=r(t,"r"),i=r(t,"g"),a=r(t,"b");return Ws(n,i,a,e)},"fade"),DJe=o(t=>`.label { + font-family: ${t.fontFamily}; + color: ${t.nodeTextColor||t.textColor}; + } + .cluster-label text { + fill: ${t.titleColor}; + } + .cluster-label span,p { + color: ${t.titleColor}; + } + + + + .label text,span,p { + fill: ${t.nodeTextColor||t.textColor}; + color: ${t.nodeTextColor||t.textColor}; + } + + .node rect, + .node circle, + .node ellipse, + .node polygon, + .node path { + fill: ${t.mainBkg}; + stroke: ${t.nodeBorder}; + stroke-width: 1px; + } + .flowchart-label text { + text-anchor: middle; + } + // .flowchart-label .text-outer-tspan { + // text-anchor: middle; + // } + // .flowchart-label .text-inner-tspan { + // text-anchor: start; + // } + + .node .label { + text-align: center; + } + .node.clickable { + cursor: pointer; + } + + .arrowheadPath { + fill: ${t.arrowheadColor}; + } + + .edgePath .path { + stroke: ${t.lineColor}; + stroke-width: 2.0px; + } + + .flowchart-link { + stroke: ${t.lineColor}; + fill: none; + } + + .edgeLabel { + background-color: ${t.edgeLabelBackground}; + rect { + opacity: 0.5; + background-color: ${t.edgeLabelBackground}; + fill: ${t.edgeLabelBackground}; + } + text-align: center; + } + + /* For html labels only */ + .labelBkg { + background-color: ${sC(t.edgeLabelBackground,.5)}; + // background-color: + } + + .node .cluster { + // fill: ${sC(t.mainBkg,.5)}; + fill: ${sC(t.clusterBkg,.5)}; + stroke: ${sC(t.clusterBorder,.2)}; + box-shadow: rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px; + stroke-width: 1px; + } + + .cluster text { + fill: ${t.titleColor}; + } + + .cluster span,p { + color: ${t.titleColor}; + } + /* .cluster div { + color: ${t.titleColor}; + } */ + + div.mermaidTooltip { + position: absolute; + text-align: center; + max-width: 200px; + padding: 2px; + font-family: ${t.fontFamily}; + font-size: 12px; + background: ${t.tertiaryColor}; + border: 1px solid ${t.border2}; + border-radius: 2px; + pointer-events: none; + z-index: 100; + } + + .flowchartTitleText { + text-anchor: middle; + font-size: 18px; + fill: ${t.textColor}; + } +`,"getStyles"),b1e=DJe});function RJe(t,e){if(t===0||!Number.isInteger(t))throw new Error("Columns must be an integer !== 0.");if(e<0||!Number.isInteger(e))throw new Error("Position must be a non-negative integer."+e);if(t<0)return{px:e,py:0};if(t===1)return{px:0,py:e};let r=e%t,n=Math.floor(e/t);return{px:r,py:n}}function HB(t,e,r=0,n=0){V.debug("setBlockSizes abc95 (start)",t.id,t?.size?.x,"block width =",t?.size,"sieblingWidth",r),t?.size?.width||(t.size={width:r,height:n,x:0,y:0});let i=0,a=0;if(t.children?.length>0){for(let m of t.children)HB(m,e);let s=NJe(t);i=s.width,a=s.height,V.debug("setBlockSizes abc95 maxWidth of",t.id,":s children is ",i,a);for(let m of t.children)m.size&&(V.debug(`abc95 Setting size of children of ${t.id} id=${m.id} ${i} ${a} ${JSON.stringify(m.size)}`),m.size.width=i*(m.widthInColumns??1)+mi*((m.widthInColumns??1)-1),m.size.height=a,m.size.x=0,m.size.y=0,V.debug(`abc95 updating size of ${t.id} children child:${m.id} maxWidth:${i} maxHeight:${a}`));for(let m of t.children)HB(m,e,i,a);let l=t.columns??-1,u=0;for(let m of t.children)u+=m.widthInColumns??1;let h=t.children.length;l>0&&l0?Math.min(t.children.length,l):t.children.length;if(m>0){let g=(d-m*mi-mi)/m;V.debug("abc95 (growing to fit) width",t.id,d,t.size?.width,g);for(let y of t.children)y.size&&(y.size.width=g)}}t.size={width:d,height:p,x:0,y:0}}V.debug("setBlockSizes abc94 (done)",t.id,t?.size?.x,t?.size?.width,t?.size?.y,t?.size?.height)}function T1e(t,e){V.debug(`abc85 layout blocks (=>layoutBlocks) ${t.id} x: ${t?.size?.x} y: ${t?.size?.y} width: ${t?.size?.width}`);let r=t.columns??-1;if(V.debug("layoutBlocks columns abc95",t.id,"=>",r,t),t.children&&t.children.length>0){let n=t?.children[0]?.size?.width??0,i=t.children.length*n+(t.children.length-1)*mi;V.debug("widthOfChildren 88",i,"posX");let a=0;V.debug("abc91 block?.size?.x",t.id,t?.size?.x);let s=t?.size?.x?t?.size?.x+(-t?.size?.width/2||0):-mi,l=0;for(let u of t.children){let h=t;if(!u.size)continue;let{width:f,height:d}=u.size,{px:p,py:m}=RJe(r,a);if(m!=l&&(l=m,s=t?.size?.x?t?.size?.x+(-t?.size?.width/2||0):-mi,V.debug("New row in layout for block",t.id," and child ",u.id,l)),V.debug(`abc89 layout blocks (child) id: ${u.id} Pos: ${a} (px, py) ${p},${m} (${h?.size?.x},${h?.size?.y}) parent: ${h.id} width: ${f}${mi}`),h.size){let g=f/2;u.size.x=s+mi+g,V.debug(`abc91 layout blocks (calc) px, pyid:${u.id} startingPos=X${s} new startingPosX${u.size.x} ${g} padding=${mi} width=${f} halfWidth=${g} => x:${u.size.x} y:${u.size.y} ${u.widthInColumns} (width * (child?.w || 1)) / 2 ${f*(u?.widthInColumns??1)/2}`),s=u.size.x+g,u.size.y=h.size.y-h.size.height/2+m*(d+mi)+d/2+mi,V.debug(`abc88 layout blocks (calc) px, pyid:${u.id}startingPosX${s}${mi}${g}=>x:${u.size.x}y:${u.size.y}${u.widthInColumns}(width * (child?.w || 1)) / 2${f*(u?.widthInColumns??1)/2}`)}u.children&&T1e(u,e),a+=u?.widthInColumns??1,V.debug("abc88 columnsPos",u,a)}}V.debug(`layout blocks (<==layoutBlocks) ${t.id} x: ${t?.size?.x} y: ${t?.size?.y} width: ${t?.size?.width}`)}function k1e(t,{minX:e,minY:r,maxX:n,maxY:i}={minX:0,minY:0,maxX:0,maxY:0}){if(t.size&&t.id!=="root"){let{x:a,y:s,width:l,height:u}=t.size;a-l/2n&&(n=a+l/2),s+u/2>i&&(i=s+u/2)}if(t.children)for(let a of t.children)({minX:e,minY:r,maxX:n,maxY:i}=k1e(a,{minX:e,minY:r,maxX:n,maxY:i}));return{minX:e,minY:r,maxX:n,maxY:i}}function E1e(t){let e=t.getBlock("root");if(!e)return;HB(e,t,0,0),T1e(e,t),V.debug("getBlocks",JSON.stringify(e,null,2));let{minX:r,minY:n,maxX:i,maxY:a}=k1e(e),s=a-n,l=i-r;return{x:r,y:n,width:l,height:s}}var mi,NJe,C1e=R(()=>{"use strict";ut();_t();mi=de()?.block?.padding??8;o(RJe,"calculateBlockPosition");NJe=o(t=>{let e=0,r=0;for(let n of t.children){let{width:i,height:a,x:s,y:l}=n.size??{width:0,height:0,x:0,y:0};V.debug("getMaxChildSize abc95 child:",n.id,"width:",i,"height:",a,"x:",s,"y:",l,n.type),n.type!=="space"&&(i>e&&(e=i/(t.widthInColumns??1)),a>r&&(r=a))}return{width:e,height:r}},"getMaxChildSize");o(HB,"setBlockSizes");o(T1e,"layoutBlocks");o(k1e,"findBounds");o(E1e,"layout")});function S1e(t,e,r=!1){let n=t,i="default";(n?.classes?.length||0)>0&&(i=(n?.classes??[]).join(" ")),i=i+" flowchart-label";let a=0,s="",l;switch(n.type){case"round":a=5,s="rect";break;case"composite":a=0,s="composite",l=0;break;case"square":s="rect";break;case"diamond":s="question";break;case"hexagon":s="hexagon";break;case"block_arrow":s="block_arrow";break;case"odd":s="rect_left_inv_arrow";break;case"lean_right":s="lean_right";break;case"lean_left":s="lean_left";break;case"trapezoid":s="trapezoid";break;case"inv_trapezoid":s="inv_trapezoid";break;case"rect_left_inv_arrow":s="rect_left_inv_arrow";break;case"circle":s="circle";break;case"ellipse":s="ellipse";break;case"stadium":s="stadium";break;case"subroutine":s="subroutine";break;case"cylinder":s="cylinder";break;case"group":s="rect";break;case"doublecircle":s="doublecircle";break;default:s="rect"}let u=lm(n?.styles??[]),h=n.label,f=n.size??{width:0,height:0,x:0,y:0};return{labelStyle:u.labelStyle,shape:s,labelText:h,rx:a,ry:a,class:i,style:u.style,id:n.id,directions:n.directions,width:f.width,height:f.height,x:f.x,y:f.y,positioned:r,intersect:void 0,type:n.type,padding:l??Or()?.block?.padding??0}}async function MJe(t,e,r){let n=S1e(e,r,!1);if(n.type==="group")return;let i=await pm(t,n),a=i.node().getBBox(),s=r.getBlock(n.id);s.size={width:a.width,height:a.height,x:0,y:0,node:i},r.setBlock(s),i.remove()}async function IJe(t,e,r){let n=S1e(e,r,!0);r.getBlock(n.id).type!=="space"&&(await pm(t,n),e.intersect=n?.intersect,wv(n))}async function YB(t,e,r,n){for(let i of e)await n(t,i,r),i.children&&await YB(t,i.children,r,n)}async function A1e(t,e,r){await YB(t,e,r,MJe)}async function _1e(t,e,r){await YB(t,e,r,IJe)}async function L1e(t,e,r,n,i){let a=new lr({multigraph:!0,compound:!0});a.setGraph({rankdir:"TB",nodesep:10,ranksep:10,marginx:8,marginy:8});for(let s of r)s.size&&a.setNode(s.id,{width:s.size.width,height:s.size.height,intersect:s.intersect});for(let s of e)if(s.start&&s.end){let l=n.getBlock(s.start),u=n.getBlock(s.end);if(l?.size&&u?.size){let h=l.size,f=u.size,d=[{x:h.x,y:h.y},{x:h.x+(f.x-h.x)/2,y:h.y+(f.y-h.y)/2},{x:f.x,y:f.y}];PE(t,{v:s.start,w:s.end,name:s.id},{...s,arrowTypeEnd:s.arrowTypeEnd,arrowTypeStart:s.arrowTypeStart,points:d,classes:"edge-thickness-normal edge-pattern-solid flowchart-link LS-a1 LE-b1"},void 0,"block",a,i),s.label&&(await IE(t,{...s,label:s.label,labelStyle:"stroke: #333; stroke-width: 1.5px;fill:none;",arrowTypeEnd:s.arrowTypeEnd,arrowTypeStart:s.arrowTypeStart,points:d,classes:"edge-thickness-normal edge-pattern-solid flowchart-link LS-a1 LE-b1"}),OE({...s,x:d[1].x,y:d[1].y},{originalPath:d}))}}}var D1e=R(()=>{"use strict";ya();qs();DO();M5();xr();o(S1e,"getNodeFromBlock");o(MJe,"calculateBlockSize");o(IJe,"insertBlockPositioned");o(YB,"performOperations");o(A1e,"calculateBlockSizes");o(_1e,"insertBlocks");o(L1e,"insertEdges")});var OJe,PJe,R1e,N1e=R(()=>{"use strict";Zt();qs();LO();ut();Yn();C1e();D1e();OJe=o(function(t,e){return e.db.getClasses()},"getClasses"),PJe=o(async function(t,e,r,n){let{securityLevel:i,block:a}=Or(),s=n.db,l;i==="sandbox"&&(l=$e("#i"+e));let u=i==="sandbox"?$e(l.nodes()[0].contentDocument.body):$e("body"),h=i==="sandbox"?u.select(`[id="${e}"]`):$e(`[id="${e}"]`);LE(h,["point","circle","cross"],n.type,e);let d=s.getBlocks(),p=s.getBlocksFlat(),m=s.getEdges(),g=h.insert("g").attr("class","block");await A1e(g,d,s);let y=E1e(s);if(await _1e(g,d,s),await L1e(g,m,p,s,e),y){let v=y,x=Math.max(1,Math.round(.125*(v.width/v.height))),b=v.height+x+10,w=v.width+10,{useMaxWidth:S}=a;Sr(h,b,w,!!S),V.debug("Here Bounds",y,v),h.attr("viewBox",`${v.x-5} ${v.y-5} ${v.width+10} ${v.height+10}`)}},"draw"),R1e={draw:PJe,getClasses:OJe}});var M1e={};hr(M1e,{diagram:()=>BJe});var BJe,I1e=R(()=>{"use strict";f1e();x1e();w1e();N1e();BJe={parser:h1e,db:v1e,renderer:R1e,styles:b1e}});var WB,qB,pb,B1e,XB,cs,Wc,oC,F1e,$Je,mb,z1e,G1e,$1e,V1e,lC,If,cC=R(()=>{"use strict";WB={L:"left",R:"right",T:"top",B:"bottom"},qB={L:o(t=>`${t},${t/2} 0,${t} 0,0`,"L"),R:o(t=>`0,${t/2} ${t},0 ${t},${t}`,"R"),T:o(t=>`0,0 ${t},0 ${t/2},${t}`,"T"),B:o(t=>`${t/2},0 ${t},${t} 0,${t}`,"B")},pb={L:o((t,e)=>t-e+2,"L"),R:o((t,e)=>t-2,"R"),T:o((t,e)=>t-e+2,"T"),B:o((t,e)=>t-2,"B")},B1e=o(function(t){return cs(t)?t==="L"?"R":"L":t==="T"?"B":"T"},"getOppositeArchitectureDirection"),XB=o(function(t){let e=t;return e==="L"||e==="R"||e==="T"||e==="B"},"isArchitectureDirection"),cs=o(function(t){let e=t;return e==="L"||e==="R"},"isArchitectureDirectionX"),Wc=o(function(t){let e=t;return e==="T"||e==="B"},"isArchitectureDirectionY"),oC=o(function(t,e){let r=cs(t)&&Wc(e),n=Wc(t)&&cs(e);return r||n},"isArchitectureDirectionXY"),F1e=o(function(t){let e=t[0],r=t[1],n=cs(e)&&Wc(r),i=Wc(e)&&cs(r);return n||i},"isArchitecturePairXY"),$Je=o(function(t){return t!=="LL"&&t!=="RR"&&t!=="TT"&&t!=="BB"},"isValidArchitectureDirectionPair"),mb=o(function(t,e){let r=`${t}${e}`;return $Je(r)?r:void 0},"getArchitectureDirectionPair"),z1e=o(function([t,e],r){let n=r[0],i=r[1];return cs(n)?Wc(i)?[t+(n==="L"?-1:1),e+(i==="T"?1:-1)]:[t+(n==="L"?-1:1),e]:cs(i)?[t+(i==="L"?1:-1),e+(n==="T"?1:-1)]:[t,e+(n==="T"?1:-1)]},"shiftPositionByArchitectureDirectionPair"),G1e=o(function(t){return t==="LT"||t==="TL"?[1,1]:t==="BL"||t==="LB"?[1,-1]:t==="BR"||t==="RB"?[-1,-1]:[-1,1]},"getArchitectureDirectionXYFactors"),$1e=o(function(t){return t.type==="service"},"isArchitectureService"),V1e=o(function(t){return t.type==="junction"},"isArchitectureJunction"),lC=o(t=>t.data(),"edgeData"),If=o(t=>t.data(),"nodeData")});function Ci(t){let e=de().architecture;return e?.[t]?e[t]:U1e[t]}var U1e,nr,VJe,UJe,HJe,YJe,WJe,qJe,XJe,jJe,KJe,QJe,ZJe,JJe,eet,tet,Z0,gb=R(()=>{"use strict";sl();_t();Jk();bi();cC();U1e=mr.architecture,nr=new uf(()=>({nodes:{},groups:{},edges:[],registeredIds:{},config:U1e,dataStructures:void 0,elements:{}})),VJe=o(()=>{nr.reset(),vr()},"clear"),UJe=o(function({id:t,icon:e,in:r,title:n,iconText:i}){if(nr.records.registeredIds[t]!==void 0)throw new Error(`The service id [${t}] is already in use by another ${nr.records.registeredIds[t]}`);if(r!==void 0){if(t===r)throw new Error(`The service [${t}] cannot be placed within itself`);if(nr.records.registeredIds[r]===void 0)throw new Error(`The service [${t}]'s parent does not exist. Please make sure the parent is created before this service`);if(nr.records.registeredIds[r]==="node")throw new Error(`The service [${t}]'s parent is not a group`)}nr.records.registeredIds[t]="node",nr.records.nodes[t]={id:t,type:"service",icon:e,iconText:i,title:n,edges:[],in:r}},"addService"),HJe=o(()=>Object.values(nr.records.nodes).filter($1e),"getServices"),YJe=o(function({id:t,in:e}){nr.records.registeredIds[t]="node",nr.records.nodes[t]={id:t,type:"junction",edges:[],in:e}},"addJunction"),WJe=o(()=>Object.values(nr.records.nodes).filter(V1e),"getJunctions"),qJe=o(()=>Object.values(nr.records.nodes),"getNodes"),XJe=o(t=>nr.records.nodes[t],"getNode"),jJe=o(function({id:t,icon:e,in:r,title:n}){if(nr.records.registeredIds[t]!==void 0)throw new Error(`The group id [${t}] is already in use by another ${nr.records.registeredIds[t]}`);if(r!==void 0){if(t===r)throw new Error(`The group [${t}] cannot be placed within itself`);if(nr.records.registeredIds[r]===void 0)throw new Error(`The group [${t}]'s parent does not exist. Please make sure the parent is created before this group`);if(nr.records.registeredIds[r]==="node")throw new Error(`The group [${t}]'s parent is not a group`)}nr.records.registeredIds[t]="group",nr.records.groups[t]={id:t,icon:e,title:n,in:r}},"addGroup"),KJe=o(()=>Object.values(nr.records.groups),"getGroups"),QJe=o(function({lhsId:t,rhsId:e,lhsDir:r,rhsDir:n,lhsInto:i,rhsInto:a,lhsGroup:s,rhsGroup:l,title:u}){if(!XB(r))throw new Error(`Invalid direction given for left hand side of edge ${t}--${e}. Expected (L,R,T,B) got ${r}`);if(!XB(n))throw new Error(`Invalid direction given for right hand side of edge ${t}--${e}. Expected (L,R,T,B) got ${n}`);if(nr.records.nodes[t]===void 0&&nr.records.groups[t]===void 0)throw new Error(`The left-hand id [${t}] does not yet exist. Please create the service/group before declaring an edge to it.`);if(nr.records.nodes[e]===void 0&&nr.records.groups[t]===void 0)throw new Error(`The right-hand id [${e}] does not yet exist. Please create the service/group before declaring an edge to it.`);let h=nr.records.nodes[t].in,f=nr.records.nodes[e].in;if(s&&h&&f&&h==f)throw new Error(`The left-hand id [${t}] is modified to traverse the group boundary, but the edge does not pass through two groups.`);if(l&&h&&f&&h==f)throw new Error(`The right-hand id [${e}] is modified to traverse the group boundary, but the edge does not pass through two groups.`);let d={lhsId:t,lhsDir:r,lhsInto:i,lhsGroup:s,rhsId:e,rhsDir:n,rhsInto:a,rhsGroup:l,title:u};nr.records.edges.push(d),nr.records.nodes[t]&&nr.records.nodes[e]&&(nr.records.nodes[t].edges.push(nr.records.edges[nr.records.edges.length-1]),nr.records.nodes[e].edges.push(nr.records.edges[nr.records.edges.length-1]))},"addEdge"),ZJe=o(()=>nr.records.edges,"getEdges"),JJe=o(()=>{if(nr.records.dataStructures===void 0){let t=Object.entries(nr.records.nodes).reduce((s,[l,u])=>(s[l]=u.edges.reduce((h,f)=>{if(f.lhsId===l){let d=mb(f.lhsDir,f.rhsDir);d&&(h[d]=f.rhsId)}else{let d=mb(f.rhsDir,f.lhsDir);d&&(h[d]=f.lhsId)}return h},{}),s),{}),e=Object.keys(t)[0],r={[e]:1},n=Object.keys(t).reduce((s,l)=>l===e?s:{...s,[l]:1},{}),i=o(s=>{let l={[s]:[0,0]},u=[s];for(;u.length>0;){let h=u.shift();if(h){r[h]=1,delete n[h];let f=t[h],[d,p]=l[h];Object.entries(f).forEach(([m,g])=>{r[g]||(l[g]=z1e([d,p],m),u.push(g))})}}return l},"BFS"),a=[i(e)];for(;Object.keys(n).length>0;)a.push(i(Object.keys(n)[0]));nr.records.dataStructures={adjList:t,spatialMaps:a}}return nr.records.dataStructures},"getDataStructures"),eet=o((t,e)=>{nr.records.elements[t]=e},"setElementForId"),tet=o(t=>nr.records.elements[t],"getElementById"),Z0={clear:VJe,setDiagramTitle:nn,getDiagramTitle:Xr,setAccTitle:kr,getAccTitle:Ar,setAccDescription:_r,getAccDescription:Lr,addService:UJe,getServices:HJe,addJunction:YJe,getJunctions:WJe,getNodes:qJe,getNode:XJe,addGroup:jJe,getGroups:KJe,addEdge:QJe,getEdges:ZJe,setElementForId:eet,getElementById:tet,getDataStructures:JJe};o(Ci,"getConfigField")});var ret,H1e,Y1e=R(()=>{"use strict";Lg();ut();sx();gb();ret=o((t,e)=>{cf(t,e),t.groups.map(e.addGroup),t.services.map(r=>e.addService({...r,type:"service"})),t.junctions.map(r=>e.addJunction({...r,type:"junction"})),t.edges.map(e.addEdge)},"populateDb"),H1e={parse:o(async t=>{let e=await Fl("architecture",t);V.debug(e),ret(e,Z0)},"parse")}});var net,W1e,q1e=R(()=>{"use strict";net=o(t=>` + .edge { + stroke-width: ${t.archEdgeWidth}; + stroke: ${t.archEdgeColor}; + fill: none; + } + + .arrow { + fill: ${t.archEdgeArrowColor}; + } + + .node-bkg { + fill: none; + stroke: ${t.archGroupBorderColor}; + stroke-width: ${t.archGroupBorderWidth}; + stroke-dasharray: 8; + } + .node-icon-text { + display: flex; + align-items: center; + } + + .node-icon-text > div { + color: #fff; + margin: 1px; + height: fit-content; + text-align: center; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + } +`,"getStyles"),W1e=net});var KB=gi((yb,jB)=>{"use strict";o(function(e,r){typeof yb=="object"&&typeof jB=="object"?jB.exports=r():typeof define=="function"&&define.amd?define([],r):typeof yb=="object"?yb.layoutBase=r():e.layoutBase=r()},"webpackUniversalModuleDefinition")(yb,function(){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var i=e[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}return o(r,"__webpack_require__"),r.m=t,r.c=e,r.i=function(n){return n},r.d=function(n,i,a){r.o(n,i)||Object.defineProperty(n,i,{configurable:!1,enumerable:!0,get:a})},r.n=function(n){var i=n&&n.__esModule?o(function(){return n.default},"getDefault"):o(function(){return n},"getModuleExports");return r.d(i,"a",i),i},r.o=function(n,i){return Object.prototype.hasOwnProperty.call(n,i)},r.p="",r(r.s=28)}([function(t,e,r){"use strict";function n(){}o(n,"LayoutConstants"),n.QUALITY=1,n.DEFAULT_CREATE_BENDS_AS_NEEDED=!1,n.DEFAULT_INCREMENTAL=!1,n.DEFAULT_ANIMATION_ON_LAYOUT=!0,n.DEFAULT_ANIMATION_DURING_LAYOUT=!1,n.DEFAULT_ANIMATION_PERIOD=50,n.DEFAULT_UNIFORM_LEAF_NODE_SIZES=!1,n.DEFAULT_GRAPH_MARGIN=15,n.NODE_DIMENSIONS_INCLUDE_LABELS=!1,n.SIMPLE_NODE_SIZE=40,n.SIMPLE_NODE_HALF_SIZE=n.SIMPLE_NODE_SIZE/2,n.EMPTY_COMPOUND_NODE_SIZE=40,n.MIN_EDGE_LENGTH=1,n.WORLD_BOUNDARY=1e6,n.INITIAL_WORLD_BOUNDARY=n.WORLD_BOUNDARY/1e3,n.WORLD_CENTER_X=1200,n.WORLD_CENTER_Y=900,t.exports=n},function(t,e,r){"use strict";var n=r(2),i=r(8),a=r(9);function s(u,h,f){n.call(this,f),this.isOverlapingSourceAndTarget=!1,this.vGraphObject=f,this.bendpoints=[],this.source=u,this.target=h}o(s,"LEdge"),s.prototype=Object.create(n.prototype);for(var l in n)s[l]=n[l];s.prototype.getSource=function(){return this.source},s.prototype.getTarget=function(){return this.target},s.prototype.isInterGraph=function(){return this.isInterGraph},s.prototype.getLength=function(){return this.length},s.prototype.isOverlapingSourceAndTarget=function(){return this.isOverlapingSourceAndTarget},s.prototype.getBendpoints=function(){return this.bendpoints},s.prototype.getLca=function(){return this.lca},s.prototype.getSourceInLca=function(){return this.sourceInLca},s.prototype.getTargetInLca=function(){return this.targetInLca},s.prototype.getOtherEnd=function(u){if(this.source===u)return this.target;if(this.target===u)return this.source;throw"Node is not incident with this edge"},s.prototype.getOtherEndInGraph=function(u,h){for(var f=this.getOtherEnd(u),d=h.getGraphManager().getRoot();;){if(f.getOwner()==h)return f;if(f.getOwner()==d)break;f=f.getOwner().getParent()}return null},s.prototype.updateLength=function(){var u=new Array(4);this.isOverlapingSourceAndTarget=i.getIntersection(this.target.getRect(),this.source.getRect(),u),this.isOverlapingSourceAndTarget||(this.lengthX=u[0]-u[2],this.lengthY=u[1]-u[3],Math.abs(this.lengthX)<1&&(this.lengthX=a.sign(this.lengthX)),Math.abs(this.lengthY)<1&&(this.lengthY=a.sign(this.lengthY)),this.length=Math.sqrt(this.lengthX*this.lengthX+this.lengthY*this.lengthY))},s.prototype.updateLengthSimple=function(){this.lengthX=this.target.getCenterX()-this.source.getCenterX(),this.lengthY=this.target.getCenterY()-this.source.getCenterY(),Math.abs(this.lengthX)<1&&(this.lengthX=a.sign(this.lengthX)),Math.abs(this.lengthY)<1&&(this.lengthY=a.sign(this.lengthY)),this.length=Math.sqrt(this.lengthX*this.lengthX+this.lengthY*this.lengthY)},t.exports=s},function(t,e,r){"use strict";function n(i){this.vGraphObject=i}o(n,"LGraphObject"),t.exports=n},function(t,e,r){"use strict";var n=r(2),i=r(10),a=r(13),s=r(0),l=r(16),u=r(5);function h(d,p,m,g){m==null&&g==null&&(g=p),n.call(this,g),d.graphManager!=null&&(d=d.graphManager),this.estimatedSize=i.MIN_VALUE,this.inclusionTreeDepth=i.MAX_VALUE,this.vGraphObject=g,this.edges=[],this.graphManager=d,m!=null&&p!=null?this.rect=new a(p.x,p.y,m.width,m.height):this.rect=new a}o(h,"LNode"),h.prototype=Object.create(n.prototype);for(var f in n)h[f]=n[f];h.prototype.getEdges=function(){return this.edges},h.prototype.getChild=function(){return this.child},h.prototype.getOwner=function(){return this.owner},h.prototype.getWidth=function(){return this.rect.width},h.prototype.setWidth=function(d){this.rect.width=d},h.prototype.getHeight=function(){return this.rect.height},h.prototype.setHeight=function(d){this.rect.height=d},h.prototype.getCenterX=function(){return this.rect.x+this.rect.width/2},h.prototype.getCenterY=function(){return this.rect.y+this.rect.height/2},h.prototype.getCenter=function(){return new u(this.rect.x+this.rect.width/2,this.rect.y+this.rect.height/2)},h.prototype.getLocation=function(){return new u(this.rect.x,this.rect.y)},h.prototype.getRect=function(){return this.rect},h.prototype.getDiagonal=function(){return Math.sqrt(this.rect.width*this.rect.width+this.rect.height*this.rect.height)},h.prototype.getHalfTheDiagonal=function(){return Math.sqrt(this.rect.height*this.rect.height+this.rect.width*this.rect.width)/2},h.prototype.setRect=function(d,p){this.rect.x=d.x,this.rect.y=d.y,this.rect.width=p.width,this.rect.height=p.height},h.prototype.setCenter=function(d,p){this.rect.x=d-this.rect.width/2,this.rect.y=p-this.rect.height/2},h.prototype.setLocation=function(d,p){this.rect.x=d,this.rect.y=p},h.prototype.moveBy=function(d,p){this.rect.x+=d,this.rect.y+=p},h.prototype.getEdgeListToNode=function(d){var p=[],m,g=this;return g.edges.forEach(function(y){if(y.target==d){if(y.source!=g)throw"Incorrect edge source!";p.push(y)}}),p},h.prototype.getEdgesBetween=function(d){var p=[],m,g=this;return g.edges.forEach(function(y){if(!(y.source==g||y.target==g))throw"Incorrect edge source and/or target";(y.target==d||y.source==d)&&p.push(y)}),p},h.prototype.getNeighborsList=function(){var d=new Set,p=this;return p.edges.forEach(function(m){if(m.source==p)d.add(m.target);else{if(m.target!=p)throw"Incorrect incidency!";d.add(m.source)}}),d},h.prototype.withChildren=function(){var d=new Set,p,m;if(d.add(this),this.child!=null)for(var g=this.child.getNodes(),y=0;yp?(this.rect.x-=(this.labelWidth-p)/2,this.setWidth(this.labelWidth)):this.labelPosHorizontal=="right"&&this.setWidth(p+this.labelWidth)),this.labelHeight&&(this.labelPosVertical=="top"?(this.rect.y-=this.labelHeight,this.setHeight(m+this.labelHeight)):this.labelPosVertical=="center"&&this.labelHeight>m?(this.rect.y-=(this.labelHeight-m)/2,this.setHeight(this.labelHeight)):this.labelPosVertical=="bottom"&&this.setHeight(m+this.labelHeight))}}},h.prototype.getInclusionTreeDepth=function(){if(this.inclusionTreeDepth==i.MAX_VALUE)throw"assert failed";return this.inclusionTreeDepth},h.prototype.transform=function(d){var p=this.rect.x;p>s.WORLD_BOUNDARY?p=s.WORLD_BOUNDARY:p<-s.WORLD_BOUNDARY&&(p=-s.WORLD_BOUNDARY);var m=this.rect.y;m>s.WORLD_BOUNDARY?m=s.WORLD_BOUNDARY:m<-s.WORLD_BOUNDARY&&(m=-s.WORLD_BOUNDARY);var g=new u(p,m),y=d.inverseTransformPoint(g);this.setLocation(y.x,y.y)},h.prototype.getLeft=function(){return this.rect.x},h.prototype.getRight=function(){return this.rect.x+this.rect.width},h.prototype.getTop=function(){return this.rect.y},h.prototype.getBottom=function(){return this.rect.y+this.rect.height},h.prototype.getParent=function(){return this.owner==null?null:this.owner.getParent()},t.exports=h},function(t,e,r){"use strict";var n=r(0);function i(){}o(i,"FDLayoutConstants");for(var a in n)i[a]=n[a];i.MAX_ITERATIONS=2500,i.DEFAULT_EDGE_LENGTH=50,i.DEFAULT_SPRING_STRENGTH=.45,i.DEFAULT_REPULSION_STRENGTH=4500,i.DEFAULT_GRAVITY_STRENGTH=.4,i.DEFAULT_COMPOUND_GRAVITY_STRENGTH=1,i.DEFAULT_GRAVITY_RANGE_FACTOR=3.8,i.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR=1.5,i.DEFAULT_USE_SMART_IDEAL_EDGE_LENGTH_CALCULATION=!0,i.DEFAULT_USE_SMART_REPULSION_RANGE_CALCULATION=!0,i.DEFAULT_COOLING_FACTOR_INCREMENTAL=.3,i.COOLING_ADAPTATION_FACTOR=.33,i.ADAPTATION_LOWER_NODE_LIMIT=1e3,i.ADAPTATION_UPPER_NODE_LIMIT=5e3,i.MAX_NODE_DISPLACEMENT_INCREMENTAL=100,i.MAX_NODE_DISPLACEMENT=i.MAX_NODE_DISPLACEMENT_INCREMENTAL*3,i.MIN_REPULSION_DIST=i.DEFAULT_EDGE_LENGTH/10,i.CONVERGENCE_CHECK_PERIOD=100,i.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR=.1,i.MIN_EDGE_LENGTH=1,i.GRID_CALCULATION_CHECK_PERIOD=10,t.exports=i},function(t,e,r){"use strict";function n(i,a){i==null&&a==null?(this.x=0,this.y=0):(this.x=i,this.y=a)}o(n,"PointD"),n.prototype.getX=function(){return this.x},n.prototype.getY=function(){return this.y},n.prototype.setX=function(i){this.x=i},n.prototype.setY=function(i){this.y=i},n.prototype.getDifference=function(i){return new DimensionD(this.x-i.x,this.y-i.y)},n.prototype.getCopy=function(){return new n(this.x,this.y)},n.prototype.translate=function(i){return this.x+=i.width,this.y+=i.height,this},t.exports=n},function(t,e,r){"use strict";var n=r(2),i=r(10),a=r(0),s=r(7),l=r(3),u=r(1),h=r(13),f=r(12),d=r(11);function p(g,y,v){n.call(this,v),this.estimatedSize=i.MIN_VALUE,this.margin=a.DEFAULT_GRAPH_MARGIN,this.edges=[],this.nodes=[],this.isConnected=!1,this.parent=g,y!=null&&y instanceof s?this.graphManager=y:y!=null&&y instanceof Layout&&(this.graphManager=y.graphManager)}o(p,"LGraph"),p.prototype=Object.create(n.prototype);for(var m in n)p[m]=n[m];p.prototype.getNodes=function(){return this.nodes},p.prototype.getEdges=function(){return this.edges},p.prototype.getGraphManager=function(){return this.graphManager},p.prototype.getParent=function(){return this.parent},p.prototype.getLeft=function(){return this.left},p.prototype.getRight=function(){return this.right},p.prototype.getTop=function(){return this.top},p.prototype.getBottom=function(){return this.bottom},p.prototype.isConnected=function(){return this.isConnected},p.prototype.add=function(g,y,v){if(y==null&&v==null){var x=g;if(this.graphManager==null)throw"Graph has no graph mgr!";if(this.getNodes().indexOf(x)>-1)throw"Node already in graph!";return x.owner=this,this.getNodes().push(x),x}else{var b=g;if(!(this.getNodes().indexOf(y)>-1&&this.getNodes().indexOf(v)>-1))throw"Source or target not in graph!";if(!(y.owner==v.owner&&y.owner==this))throw"Both owners must be this graph!";return y.owner!=v.owner?null:(b.source=y,b.target=v,b.isInterGraph=!1,this.getEdges().push(b),y.edges.push(b),v!=y&&v.edges.push(b),b)}},p.prototype.remove=function(g){var y=g;if(g instanceof l){if(y==null)throw"Node is null!";if(!(y.owner!=null&&y.owner==this))throw"Owner graph is invalid!";if(this.graphManager==null)throw"Owner graph manager is invalid!";for(var v=y.edges.slice(),x,b=v.length,w=0;w-1&&E>-1))throw"Source and/or target doesn't know this edge!";x.source.edges.splice(T,1),x.target!=x.source&&x.target.edges.splice(E,1);var S=x.source.owner.getEdges().indexOf(x);if(S==-1)throw"Not in owner's edge list!";x.source.owner.getEdges().splice(S,1)}},p.prototype.updateLeftTop=function(){for(var g=i.MAX_VALUE,y=i.MAX_VALUE,v,x,b,w=this.getNodes(),S=w.length,T=0;Tv&&(g=v),y>x&&(y=x)}return g==i.MAX_VALUE?null:(w[0].getParent().paddingLeft!=null?b=w[0].getParent().paddingLeft:b=this.margin,this.left=y-b,this.top=g-b,new f(this.left,this.top))},p.prototype.updateBounds=function(g){for(var y=i.MAX_VALUE,v=-i.MAX_VALUE,x=i.MAX_VALUE,b=-i.MAX_VALUE,w,S,T,E,_,A=this.nodes,L=A.length,M=0;Mw&&(y=w),vT&&(x=T),bw&&(y=w),vT&&(x=T),b=this.nodes.length){var L=0;v.forEach(function(M){M.owner==g&&L++}),L==this.nodes.length&&(this.isConnected=!0)}},t.exports=p},function(t,e,r){"use strict";var n,i=r(1);function a(s){n=r(6),this.layout=s,this.graphs=[],this.edges=[]}o(a,"LGraphManager"),a.prototype.addRoot=function(){var s=this.layout.newGraph(),l=this.layout.newNode(null),u=this.add(s,l);return this.setRootGraph(u),this.rootGraph},a.prototype.add=function(s,l,u,h,f){if(u==null&&h==null&&f==null){if(s==null)throw"Graph is null!";if(l==null)throw"Parent node is null!";if(this.graphs.indexOf(s)>-1)throw"Graph already in this graph mgr!";if(this.graphs.push(s),s.parent!=null)throw"Already has a parent!";if(l.child!=null)throw"Already has a child!";return s.parent=l,l.child=s,s}else{f=u,h=l,u=s;var d=h.getOwner(),p=f.getOwner();if(!(d!=null&&d.getGraphManager()==this))throw"Source not in this graph mgr!";if(!(p!=null&&p.getGraphManager()==this))throw"Target not in this graph mgr!";if(d==p)return u.isInterGraph=!1,d.add(u,h,f);if(u.isInterGraph=!0,u.source=h,u.target=f,this.edges.indexOf(u)>-1)throw"Edge already in inter-graph edge list!";if(this.edges.push(u),!(u.source!=null&&u.target!=null))throw"Edge source and/or target is null!";if(!(u.source.edges.indexOf(u)==-1&&u.target.edges.indexOf(u)==-1))throw"Edge already in source and/or target incidency list!";return u.source.edges.push(u),u.target.edges.push(u),u}},a.prototype.remove=function(s){if(s instanceof n){var l=s;if(l.getGraphManager()!=this)throw"Graph not in this graph mgr";if(!(l==this.rootGraph||l.parent!=null&&l.parent.graphManager==this))throw"Invalid parent node!";var u=[];u=u.concat(l.getEdges());for(var h,f=u.length,d=0;d=s.getRight()?l[0]+=Math.min(s.getX()-a.getX(),a.getRight()-s.getRight()):s.getX()<=a.getX()&&s.getRight()>=a.getRight()&&(l[0]+=Math.min(a.getX()-s.getX(),s.getRight()-a.getRight())),a.getY()<=s.getY()&&a.getBottom()>=s.getBottom()?l[1]+=Math.min(s.getY()-a.getY(),a.getBottom()-s.getBottom()):s.getY()<=a.getY()&&s.getBottom()>=a.getBottom()&&(l[1]+=Math.min(a.getY()-s.getY(),s.getBottom()-a.getBottom()));var f=Math.abs((s.getCenterY()-a.getCenterY())/(s.getCenterX()-a.getCenterX()));s.getCenterY()===a.getCenterY()&&s.getCenterX()===a.getCenterX()&&(f=1);var d=f*l[0],p=l[1]/f;l[0]d)return l[0]=u,l[1]=m,l[2]=f,l[3]=A,!1;if(hf)return l[0]=p,l[1]=h,l[2]=E,l[3]=d,!1;if(uf?(l[0]=y,l[1]=v,k=!0):(l[0]=g,l[1]=m,k=!0):C===D&&(u>f?(l[0]=p,l[1]=m,k=!0):(l[0]=x,l[1]=v,k=!0)),-O===D?f>u?(l[2]=_,l[3]=A,I=!0):(l[2]=E,l[3]=T,I=!0):O===D&&(f>u?(l[2]=S,l[3]=T,I=!0):(l[2]=L,l[3]=A,I=!0)),k&&I)return!1;if(u>f?h>d?(P=this.getCardinalDirection(C,D,4),F=this.getCardinalDirection(O,D,2)):(P=this.getCardinalDirection(-C,D,3),F=this.getCardinalDirection(-O,D,1)):h>d?(P=this.getCardinalDirection(-C,D,1),F=this.getCardinalDirection(-O,D,3)):(P=this.getCardinalDirection(C,D,2),F=this.getCardinalDirection(O,D,4)),!k)switch(P){case 1:$=m,B=u+-w/D,l[0]=B,l[1]=$;break;case 2:B=x,$=h+b*D,l[0]=B,l[1]=$;break;case 3:$=v,B=u+w/D,l[0]=B,l[1]=$;break;case 4:B=y,$=h+-b*D,l[0]=B,l[1]=$;break}if(!I)switch(F){case 1:Y=T,z=f+-N/D,l[2]=z,l[3]=Y;break;case 2:z=L,Y=d+M*D,l[2]=z,l[3]=Y;break;case 3:Y=A,z=f+N/D,l[2]=z,l[3]=Y;break;case 4:z=_,Y=d+-M*D,l[2]=z,l[3]=Y;break}}return!1},i.getCardinalDirection=function(a,s,l){return a>s?l:1+l%4},i.getIntersection=function(a,s,l,u){if(u==null)return this.getIntersection2(a,s,l);var h=a.x,f=a.y,d=s.x,p=s.y,m=l.x,g=l.y,y=u.x,v=u.y,x=void 0,b=void 0,w=void 0,S=void 0,T=void 0,E=void 0,_=void 0,A=void 0,L=void 0;return w=p-f,T=h-d,_=d*f-h*p,S=v-g,E=m-y,A=y*g-m*v,L=w*E-S*T,L===0?null:(x=(T*A-E*_)/L,b=(S*_-w*A)/L,new n(x,b))},i.angleOfVector=function(a,s,l,u){var h=void 0;return a!==l?(h=Math.atan((u-s)/(l-a)),l=0){var v=(-m+Math.sqrt(m*m-4*p*g))/(2*p),x=(-m-Math.sqrt(m*m-4*p*g))/(2*p),b=null;return v>=0&&v<=1?[v]:x>=0&&x<=1?[x]:b}else return null},i.HALF_PI=.5*Math.PI,i.ONE_AND_HALF_PI=1.5*Math.PI,i.TWO_PI=2*Math.PI,i.THREE_PI=3*Math.PI,t.exports=i},function(t,e,r){"use strict";function n(){}o(n,"IMath"),n.sign=function(i){return i>0?1:i<0?-1:0},n.floor=function(i){return i<0?Math.ceil(i):Math.floor(i)},n.ceil=function(i){return i<0?Math.floor(i):Math.ceil(i)},t.exports=n},function(t,e,r){"use strict";function n(){}o(n,"Integer"),n.MAX_VALUE=2147483647,n.MIN_VALUE=-2147483648,t.exports=n},function(t,e,r){"use strict";var n=function(){function h(f,d){for(var p=0;p"u"?"undefined":n(a);return a==null||s!="object"&&s!="function"},t.exports=i},function(t,e,r){"use strict";function n(m){if(Array.isArray(m)){for(var g=0,y=Array(m.length);g0&&g;){for(w.push(T[0]);w.length>0&&g;){var E=w[0];w.splice(0,1),b.add(E);for(var _=E.getEdges(),x=0;x<_.length;x++){var A=_[x].getOtherEnd(E);if(S.get(E)!=A)if(!b.has(A))w.push(A),S.set(A,E);else{g=!1;break}}}if(!g)m=[];else{var L=[].concat(n(b));m.push(L);for(var x=0;x-1&&T.splice(N,1)}b=new Set,S=new Map}}return m},p.prototype.createDummyNodesForBendpoints=function(m){for(var g=[],y=m.source,v=this.graphManager.calcLowestCommonAncestor(m.source,m.target),x=0;x0){for(var v=this.edgeToDummyNodes.get(y),x=0;x=0&&g.splice(A,1);var L=S.getNeighborsList();L.forEach(function(k){if(y.indexOf(k)<0){var I=v.get(k),C=I-1;C==1&&E.push(k),v.set(k,C)}})}y=y.concat(E),(g.length==1||g.length==2)&&(x=!0,b=g[0])}return b},p.prototype.setGraphManager=function(m){this.graphManager=m},t.exports=p},function(t,e,r){"use strict";function n(){}o(n,"RandomSeed"),n.seed=1,n.x=0,n.nextDouble=function(){return n.x=Math.sin(n.seed++)*1e4,n.x-Math.floor(n.x)},t.exports=n},function(t,e,r){"use strict";var n=r(5);function i(a,s){this.lworldOrgX=0,this.lworldOrgY=0,this.ldeviceOrgX=0,this.ldeviceOrgY=0,this.lworldExtX=1,this.lworldExtY=1,this.ldeviceExtX=1,this.ldeviceExtY=1}o(i,"Transform"),i.prototype.getWorldOrgX=function(){return this.lworldOrgX},i.prototype.setWorldOrgX=function(a){this.lworldOrgX=a},i.prototype.getWorldOrgY=function(){return this.lworldOrgY},i.prototype.setWorldOrgY=function(a){this.lworldOrgY=a},i.prototype.getWorldExtX=function(){return this.lworldExtX},i.prototype.setWorldExtX=function(a){this.lworldExtX=a},i.prototype.getWorldExtY=function(){return this.lworldExtY},i.prototype.setWorldExtY=function(a){this.lworldExtY=a},i.prototype.getDeviceOrgX=function(){return this.ldeviceOrgX},i.prototype.setDeviceOrgX=function(a){this.ldeviceOrgX=a},i.prototype.getDeviceOrgY=function(){return this.ldeviceOrgY},i.prototype.setDeviceOrgY=function(a){this.ldeviceOrgY=a},i.prototype.getDeviceExtX=function(){return this.ldeviceExtX},i.prototype.setDeviceExtX=function(a){this.ldeviceExtX=a},i.prototype.getDeviceExtY=function(){return this.ldeviceExtY},i.prototype.setDeviceExtY=function(a){this.ldeviceExtY=a},i.prototype.transformX=function(a){var s=0,l=this.lworldExtX;return l!=0&&(s=this.ldeviceOrgX+(a-this.lworldOrgX)*this.ldeviceExtX/l),s},i.prototype.transformY=function(a){var s=0,l=this.lworldExtY;return l!=0&&(s=this.ldeviceOrgY+(a-this.lworldOrgY)*this.ldeviceExtY/l),s},i.prototype.inverseTransformX=function(a){var s=0,l=this.ldeviceExtX;return l!=0&&(s=this.lworldOrgX+(a-this.ldeviceOrgX)*this.lworldExtX/l),s},i.prototype.inverseTransformY=function(a){var s=0,l=this.ldeviceExtY;return l!=0&&(s=this.lworldOrgY+(a-this.ldeviceOrgY)*this.lworldExtY/l),s},i.prototype.inverseTransformPoint=function(a){var s=new n(this.inverseTransformX(a.x),this.inverseTransformY(a.y));return s},t.exports=i},function(t,e,r){"use strict";function n(d){if(Array.isArray(d)){for(var p=0,m=Array(d.length);pa.ADAPTATION_LOWER_NODE_LIMIT&&(this.coolingFactor=Math.max(this.coolingFactor*a.COOLING_ADAPTATION_FACTOR,this.coolingFactor-(d-a.ADAPTATION_LOWER_NODE_LIMIT)/(a.ADAPTATION_UPPER_NODE_LIMIT-a.ADAPTATION_LOWER_NODE_LIMIT)*this.coolingFactor*(1-a.COOLING_ADAPTATION_FACTOR))),this.maxNodeDisplacement=a.MAX_NODE_DISPLACEMENT_INCREMENTAL):(d>a.ADAPTATION_LOWER_NODE_LIMIT?this.coolingFactor=Math.max(a.COOLING_ADAPTATION_FACTOR,1-(d-a.ADAPTATION_LOWER_NODE_LIMIT)/(a.ADAPTATION_UPPER_NODE_LIMIT-a.ADAPTATION_LOWER_NODE_LIMIT)*(1-a.COOLING_ADAPTATION_FACTOR)):this.coolingFactor=1,this.initialCoolingFactor=this.coolingFactor,this.maxNodeDisplacement=a.MAX_NODE_DISPLACEMENT),this.maxIterations=Math.max(this.getAllNodes().length*5,this.maxIterations),this.displacementThresholdPerNode=3*a.DEFAULT_EDGE_LENGTH/100,this.totalDisplacementThreshold=this.displacementThresholdPerNode*this.getAllNodes().length,this.repulsionRange=this.calcRepulsionRange()},h.prototype.calcSpringForces=function(){for(var d=this.getAllEdges(),p,m=0;m0&&arguments[0]!==void 0?arguments[0]:!0,p=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!1,m,g,y,v,x=this.getAllNodes(),b;if(this.useFRGridVariant)for(this.totalIterations%a.GRID_CALCULATION_CHECK_PERIOD==1&&d&&this.updateGrid(),b=new Set,m=0;mw||b>w)&&(d.gravitationForceX=-this.gravityConstant*y,d.gravitationForceY=-this.gravityConstant*v)):(w=p.getEstimatedSize()*this.compoundGravityRangeFactor,(x>w||b>w)&&(d.gravitationForceX=-this.gravityConstant*y*this.compoundGravityConstant,d.gravitationForceY=-this.gravityConstant*v*this.compoundGravityConstant))},h.prototype.isConverged=function(){var d,p=!1;return this.totalIterations>this.maxIterations/3&&(p=Math.abs(this.totalDisplacement-this.oldTotalDisplacement)<2),d=this.totalDisplacement=x.length||w>=x[0].length)){for(var S=0;Sh},"_defaultCompareFunction")}]),l}();t.exports=s},function(t,e,r){"use strict";function n(){}o(n,"SVD"),n.svd=function(i){this.U=null,this.V=null,this.s=null,this.m=0,this.n=0,this.m=i.length,this.n=i[0].length;var a=Math.min(this.m,this.n);this.s=function(it){for(var dt=[];it-- >0;)dt.push(0);return dt}(Math.min(this.m+1,this.n)),this.U=function(it){var dt=o(function lt(It){if(It.length==0)return 0;for(var mt=[],St=0;St0;)dt.push(0);return dt}(this.n),l=function(it){for(var dt=[];it-- >0;)dt.push(0);return dt}(this.m),u=!0,h=!0,f=Math.min(this.m-1,this.n),d=Math.max(0,Math.min(this.n-2,this.m)),p=0;p=0;D--)if(this.s[D]!==0){for(var P=D+1;P=0;X--){if(function(it,dt){return it&&dt}(X0;){var ue=void 0,te=void 0;for(ue=I-2;ue>=-1&&ue!==-1;ue--)if(Math.abs(s[ue])<=ce+se*(Math.abs(this.s[ue])+Math.abs(this.s[ue+1]))){s[ue]=0;break}if(ue===I-2)te=4;else{var De=void 0;for(De=I-1;De>=ue&&De!==ue;De--){var oe=(De!==I?Math.abs(s[De]):0)+(De!==ue+1?Math.abs(s[De-1]):0);if(Math.abs(this.s[De])<=ce+se*oe){this.s[De]=0;break}}De===ue?te=3:De===I-1?te=1:(te=2,ue=De)}switch(ue++,te){case 1:{var ke=s[I-2];s[I-2]=0;for(var Ie=I-2;Ie>=ue;Ie--){var Se=n.hypot(this.s[Ie],ke),Ue=this.s[Ie]/Se,Pe=ke/Se;if(this.s[Ie]=Se,Ie!==ue&&(ke=-Pe*s[Ie-1],s[Ie-1]=Ue*s[Ie-1]),h)for(var _e=0;_e=this.s[ue+1]);){var je=this.s[ue];if(this.s[ue]=this.s[ue+1],this.s[ue+1]=je,h&&ueMath.abs(a)?(s=a/i,s=Math.abs(i)*Math.sqrt(1+s*s)):a!=0?(s=i/a,s=Math.abs(a)*Math.sqrt(1+s*s)):s=0,s},t.exports=n},function(t,e,r){"use strict";var n=function(){function s(l,u){for(var h=0;h2&&arguments[2]!==void 0?arguments[2]:1,f=arguments.length>3&&arguments[3]!==void 0?arguments[3]:-1,d=arguments.length>4&&arguments[4]!==void 0?arguments[4]:-1;i(this,s),this.sequence1=l,this.sequence2=u,this.match_score=h,this.mismatch_penalty=f,this.gap_penalty=d,this.iMax=l.length+1,this.jMax=u.length+1,this.grid=new Array(this.iMax);for(var p=0;p=0;l--){var u=this.listeners[l];u.event===a&&u.callback===s&&this.listeners.splice(l,1)}},i.emit=function(a,s){for(var l=0;l{"use strict";o(function(e,r){typeof vb=="object"&&typeof QB=="object"?QB.exports=r(KB()):typeof define=="function"&&define.amd?define(["layout-base"],r):typeof vb=="object"?vb.coseBase=r(KB()):e.coseBase=r(e.layoutBase)},"webpackUniversalModuleDefinition")(vb,function(t){return(()=>{"use strict";var e={45:(a,s,l)=>{var u={};u.layoutBase=l(551),u.CoSEConstants=l(806),u.CoSEEdge=l(767),u.CoSEGraph=l(880),u.CoSEGraphManager=l(578),u.CoSELayout=l(765),u.CoSENode=l(991),u.ConstraintHandler=l(902),a.exports=u},806:(a,s,l)=>{var u=l(551).FDLayoutConstants;function h(){}o(h,"CoSEConstants");for(var f in u)h[f]=u[f];h.DEFAULT_USE_MULTI_LEVEL_SCALING=!1,h.DEFAULT_RADIAL_SEPARATION=u.DEFAULT_EDGE_LENGTH,h.DEFAULT_COMPONENT_SEPERATION=60,h.TILE=!0,h.TILING_PADDING_VERTICAL=10,h.TILING_PADDING_HORIZONTAL=10,h.TRANSFORM_ON_CONSTRAINT_HANDLING=!0,h.ENFORCE_CONSTRAINTS=!0,h.APPLY_LAYOUT=!0,h.RELAX_MOVEMENT_ON_CONSTRAINTS=!0,h.TREE_REDUCTION_ON_INCREMENTAL=!0,h.PURE_INCREMENTAL=h.DEFAULT_INCREMENTAL,a.exports=h},767:(a,s,l)=>{var u=l(551).FDLayoutEdge;function h(d,p,m){u.call(this,d,p,m)}o(h,"CoSEEdge"),h.prototype=Object.create(u.prototype);for(var f in u)h[f]=u[f];a.exports=h},880:(a,s,l)=>{var u=l(551).LGraph;function h(d,p,m){u.call(this,d,p,m)}o(h,"CoSEGraph"),h.prototype=Object.create(u.prototype);for(var f in u)h[f]=u[f];a.exports=h},578:(a,s,l)=>{var u=l(551).LGraphManager;function h(d){u.call(this,d)}o(h,"CoSEGraphManager"),h.prototype=Object.create(u.prototype);for(var f in u)h[f]=u[f];a.exports=h},765:(a,s,l)=>{var u=l(551).FDLayout,h=l(578),f=l(880),d=l(991),p=l(767),m=l(806),g=l(902),y=l(551).FDLayoutConstants,v=l(551).LayoutConstants,x=l(551).Point,b=l(551).PointD,w=l(551).DimensionD,S=l(551).Layout,T=l(551).Integer,E=l(551).IGeometry,_=l(551).LGraph,A=l(551).Transform,L=l(551).LinkedList;function M(){u.call(this),this.toBeTiled={},this.constraints={}}o(M,"CoSELayout"),M.prototype=Object.create(u.prototype);for(var N in u)M[N]=u[N];M.prototype.newGraphManager=function(){var k=new h(this);return this.graphManager=k,k},M.prototype.newGraph=function(k){return new f(null,this.graphManager,k)},M.prototype.newNode=function(k){return new d(this.graphManager,k)},M.prototype.newEdge=function(k){return new p(null,null,k)},M.prototype.initParameters=function(){u.prototype.initParameters.call(this,arguments),this.isSubLayout||(m.DEFAULT_EDGE_LENGTH<10?this.idealEdgeLength=10:this.idealEdgeLength=m.DEFAULT_EDGE_LENGTH,this.useSmartIdealEdgeLengthCalculation=m.DEFAULT_USE_SMART_IDEAL_EDGE_LENGTH_CALCULATION,this.gravityConstant=y.DEFAULT_GRAVITY_STRENGTH,this.compoundGravityConstant=y.DEFAULT_COMPOUND_GRAVITY_STRENGTH,this.gravityRangeFactor=y.DEFAULT_GRAVITY_RANGE_FACTOR,this.compoundGravityRangeFactor=y.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR,this.prunedNodesAll=[],this.growTreeIterations=0,this.afterGrowthIterations=0,this.isTreeGrowing=!1,this.isGrowthFinished=!1)},M.prototype.initSpringEmbedder=function(){u.prototype.initSpringEmbedder.call(this),this.coolingCycle=0,this.maxCoolingCycle=this.maxIterations/y.CONVERGENCE_CHECK_PERIOD,this.finalTemperature=.04,this.coolingAdjuster=1},M.prototype.layout=function(){var k=v.DEFAULT_CREATE_BENDS_AS_NEEDED;return k&&(this.createBendpoints(),this.graphManager.resetAllEdges()),this.level=0,this.classicLayout()},M.prototype.classicLayout=function(){if(this.nodesWithGravity=this.calculateNodesToApplyGravitationTo(),this.graphManager.setAllNodesToApplyGravitation(this.nodesWithGravity),this.calcNoOfChildrenForAllNodes(),this.graphManager.calcLowestCommonAncestors(),this.graphManager.calcInclusionTreeDepths(),this.graphManager.getRoot().calcEstimatedSize(),this.calcIdealEdgeLengths(),this.incremental){if(m.TREE_REDUCTION_ON_INCREMENTAL){this.reduceTrees(),this.graphManager.resetAllNodesToApplyGravitation();var I=new Set(this.getAllNodes()),C=this.nodesWithGravity.filter(function(P){return I.has(P)});this.graphManager.setAllNodesToApplyGravitation(C)}}else{var k=this.getFlatForest();if(k.length>0)this.positionNodesRadially(k);else{this.reduceTrees(),this.graphManager.resetAllNodesToApplyGravitation();var I=new Set(this.getAllNodes()),C=this.nodesWithGravity.filter(function(O){return I.has(O)});this.graphManager.setAllNodesToApplyGravitation(C),this.positionNodesRandomly()}}return Object.keys(this.constraints).length>0&&(g.handleConstraints(this),this.initConstraintVariables()),this.initSpringEmbedder(),m.APPLY_LAYOUT&&this.runSpringEmbedder(),!0},M.prototype.tick=function(){if(this.totalIterations++,this.totalIterations===this.maxIterations&&!this.isTreeGrowing&&!this.isGrowthFinished)if(this.prunedNodesAll.length>0)this.isTreeGrowing=!0;else return!0;if(this.totalIterations%y.CONVERGENCE_CHECK_PERIOD==0&&!this.isTreeGrowing&&!this.isGrowthFinished){if(this.isConverged())if(this.prunedNodesAll.length>0)this.isTreeGrowing=!0;else return!0;this.coolingCycle++,this.layoutQuality==0?this.coolingAdjuster=this.coolingCycle:this.layoutQuality==1&&(this.coolingAdjuster=this.coolingCycle/3),this.coolingFactor=Math.max(this.initialCoolingFactor-Math.pow(this.coolingCycle,Math.log(100*(this.initialCoolingFactor-this.finalTemperature))/Math.log(this.maxCoolingCycle))/100*this.coolingAdjuster,this.finalTemperature),this.animationPeriod=Math.ceil(this.initialAnimationPeriod*Math.sqrt(this.coolingFactor))}if(this.isTreeGrowing){if(this.growTreeIterations%10==0)if(this.prunedNodesAll.length>0){this.graphManager.updateBounds(),this.updateGrid(),this.growTree(this.prunedNodesAll),this.graphManager.resetAllNodesToApplyGravitation();var k=new Set(this.getAllNodes()),I=this.nodesWithGravity.filter(function(D){return k.has(D)});this.graphManager.setAllNodesToApplyGravitation(I),this.graphManager.updateBounds(),this.updateGrid(),m.PURE_INCREMENTAL?this.coolingFactor=y.DEFAULT_COOLING_FACTOR_INCREMENTAL/2:this.coolingFactor=y.DEFAULT_COOLING_FACTOR_INCREMENTAL}else this.isTreeGrowing=!1,this.isGrowthFinished=!0;this.growTreeIterations++}if(this.isGrowthFinished){if(this.isConverged())return!0;this.afterGrowthIterations%10==0&&(this.graphManager.updateBounds(),this.updateGrid()),m.PURE_INCREMENTAL?this.coolingFactor=y.DEFAULT_COOLING_FACTOR_INCREMENTAL/2*((100-this.afterGrowthIterations)/100):this.coolingFactor=y.DEFAULT_COOLING_FACTOR_INCREMENTAL*((100-this.afterGrowthIterations)/100),this.afterGrowthIterations++}var C=!this.isTreeGrowing&&!this.isGrowthFinished,O=this.growTreeIterations%10==1&&this.isTreeGrowing||this.afterGrowthIterations%10==1&&this.isGrowthFinished;return this.totalDisplacement=0,this.graphManager.updateBounds(),this.calcSpringForces(),this.calcRepulsionForces(C,O),this.calcGravitationalForces(),this.moveNodes(),this.animate(),!1},M.prototype.getPositionsData=function(){for(var k=this.graphManager.getAllNodes(),I={},C=0;C0&&this.updateDisplacements();for(var C=0;C0&&(O.fixedNodeWeight=P)}}if(this.constraints.relativePlacementConstraint){var F=new Map,B=new Map;if(this.dummyToNodeForVerticalAlignment=new Map,this.dummyToNodeForHorizontalAlignment=new Map,this.fixedNodesOnHorizontal=new Set,this.fixedNodesOnVertical=new Set,this.fixedNodeSet.forEach(function(J){k.fixedNodesOnHorizontal.add(J),k.fixedNodesOnVertical.add(J)}),this.constraints.alignmentConstraint){if(this.constraints.alignmentConstraint.vertical)for(var $=this.constraints.alignmentConstraint.vertical,C=0;C<$.length;C++)this.dummyToNodeForVerticalAlignment.set("dummy"+C,[]),$[C].forEach(function(Z){F.set(Z,"dummy"+C),k.dummyToNodeForVerticalAlignment.get("dummy"+C).push(Z),k.fixedNodeSet.has(Z)&&k.fixedNodesOnHorizontal.add("dummy"+C)});if(this.constraints.alignmentConstraint.horizontal)for(var z=this.constraints.alignmentConstraint.horizontal,C=0;C=2*J.length/3;q--)Z=Math.floor(Math.random()*(q+1)),H=J[q],J[q]=J[Z],J[Z]=H;return J},this.nodesInRelativeHorizontal=[],this.nodesInRelativeVertical=[],this.nodeToRelativeConstraintMapHorizontal=new Map,this.nodeToRelativeConstraintMapVertical=new Map,this.nodeToTempPositionMapHorizontal=new Map,this.nodeToTempPositionMapVertical=new Map,this.constraints.relativePlacementConstraint.forEach(function(J){if(J.left){var Z=F.has(J.left)?F.get(J.left):J.left,H=F.has(J.right)?F.get(J.right):J.right;k.nodesInRelativeHorizontal.includes(Z)||(k.nodesInRelativeHorizontal.push(Z),k.nodeToRelativeConstraintMapHorizontal.set(Z,[]),k.dummyToNodeForVerticalAlignment.has(Z)?k.nodeToTempPositionMapHorizontal.set(Z,k.idToNodeMap.get(k.dummyToNodeForVerticalAlignment.get(Z)[0]).getCenterX()):k.nodeToTempPositionMapHorizontal.set(Z,k.idToNodeMap.get(Z).getCenterX())),k.nodesInRelativeHorizontal.includes(H)||(k.nodesInRelativeHorizontal.push(H),k.nodeToRelativeConstraintMapHorizontal.set(H,[]),k.dummyToNodeForVerticalAlignment.has(H)?k.nodeToTempPositionMapHorizontal.set(H,k.idToNodeMap.get(k.dummyToNodeForVerticalAlignment.get(H)[0]).getCenterX()):k.nodeToTempPositionMapHorizontal.set(H,k.idToNodeMap.get(H).getCenterX())),k.nodeToRelativeConstraintMapHorizontal.get(Z).push({right:H,gap:J.gap}),k.nodeToRelativeConstraintMapHorizontal.get(H).push({left:Z,gap:J.gap})}else{var q=B.has(J.top)?B.get(J.top):J.top,K=B.has(J.bottom)?B.get(J.bottom):J.bottom;k.nodesInRelativeVertical.includes(q)||(k.nodesInRelativeVertical.push(q),k.nodeToRelativeConstraintMapVertical.set(q,[]),k.dummyToNodeForHorizontalAlignment.has(q)?k.nodeToTempPositionMapVertical.set(q,k.idToNodeMap.get(k.dummyToNodeForHorizontalAlignment.get(q)[0]).getCenterY()):k.nodeToTempPositionMapVertical.set(q,k.idToNodeMap.get(q).getCenterY())),k.nodesInRelativeVertical.includes(K)||(k.nodesInRelativeVertical.push(K),k.nodeToRelativeConstraintMapVertical.set(K,[]),k.dummyToNodeForHorizontalAlignment.has(K)?k.nodeToTempPositionMapVertical.set(K,k.idToNodeMap.get(k.dummyToNodeForHorizontalAlignment.get(K)[0]).getCenterY()):k.nodeToTempPositionMapVertical.set(K,k.idToNodeMap.get(K).getCenterY())),k.nodeToRelativeConstraintMapVertical.get(q).push({bottom:K,gap:J.gap}),k.nodeToRelativeConstraintMapVertical.get(K).push({top:q,gap:J.gap})}});else{var Y=new Map,Q=new Map;this.constraints.relativePlacementConstraint.forEach(function(J){if(J.left){var Z=F.has(J.left)?F.get(J.left):J.left,H=F.has(J.right)?F.get(J.right):J.right;Y.has(Z)?Y.get(Z).push(H):Y.set(Z,[H]),Y.has(H)?Y.get(H).push(Z):Y.set(H,[Z])}else{var q=B.has(J.top)?B.get(J.top):J.top,K=B.has(J.bottom)?B.get(J.bottom):J.bottom;Q.has(q)?Q.get(q).push(K):Q.set(q,[K]),Q.has(K)?Q.get(K).push(q):Q.set(K,[q])}});var X=o(function(Z,H){var q=[],K=[],se=new L,ce=new Set,ue=0;return Z.forEach(function(te,De){if(!ce.has(De)){q[ue]=[],K[ue]=!1;var oe=De;for(se.push(oe),ce.add(oe),q[ue].push(oe);se.length!=0;){oe=se.shift(),H.has(oe)&&(K[ue]=!0);var ke=Z.get(oe);ke.forEach(function(Ie){ce.has(Ie)||(se.push(Ie),ce.add(Ie),q[ue].push(Ie))})}ue++}}),{components:q,isFixed:K}},"constructComponents"),ie=X(Y,k.fixedNodesOnHorizontal);this.componentsOnHorizontal=ie.components,this.fixedComponentsOnHorizontal=ie.isFixed;var j=X(Q,k.fixedNodesOnVertical);this.componentsOnVertical=j.components,this.fixedComponentsOnVertical=j.isFixed}}},M.prototype.updateDisplacements=function(){var k=this;if(this.constraints.fixedNodeConstraint&&this.constraints.fixedNodeConstraint.forEach(function(j){var J=k.idToNodeMap.get(j.nodeId);J.displacementX=0,J.displacementY=0}),this.constraints.alignmentConstraint){if(this.constraints.alignmentConstraint.vertical)for(var I=this.constraints.alignmentConstraint.vertical,C=0;C1){var B;for(B=0;BO&&(O=Math.floor(F.y)),P=Math.floor(F.x+m.DEFAULT_COMPONENT_SEPERATION)}this.transform(new b(v.WORLD_CENTER_X-F.x/2,v.WORLD_CENTER_Y-F.y/2))},M.radialLayout=function(k,I,C){var O=Math.max(this.maxDiagonalInTree(k),m.DEFAULT_RADIAL_SEPARATION);M.branchRadialLayout(I,null,0,359,0,O);var D=_.calculateBounds(k),P=new A;P.setDeviceOrgX(D.getMinX()),P.setDeviceOrgY(D.getMinY()),P.setWorldOrgX(C.x),P.setWorldOrgY(C.y);for(var F=0;F1;){var q=H[0];H.splice(0,1);var K=X.indexOf(q);K>=0&&X.splice(K,1),J--,ie--}I!=null?Z=(X.indexOf(H[0])+1)%J:Z=0;for(var se=Math.abs(O-C)/ie,ce=Z;j!=ie;ce=++ce%J){var ue=X[ce].getOtherEnd(k);if(ue!=I){var te=(C+j*se)%360,De=(te+se)%360;M.branchRadialLayout(ue,k,te,De,D+P,P),j++}}},M.maxDiagonalInTree=function(k){for(var I=T.MIN_VALUE,C=0;CI&&(I=D)}return I},M.prototype.calcRepulsionRange=function(){return 2*(this.level+1)*this.idealEdgeLength},M.prototype.groupZeroDegreeMembers=function(){var k=this,I={};this.memberGroups={},this.idToDummyNode={};for(var C=[],O=this.graphManager.getAllNodes(),D=0;D"u"&&(I[B]=[]),I[B]=I[B].concat(P)}Object.keys(I).forEach(function($){if(I[$].length>1){var z="DummyCompound_"+$;k.memberGroups[z]=I[$];var Y=I[$][0].getParent(),Q=new d(k.graphManager);Q.id=z,Q.paddingLeft=Y.paddingLeft||0,Q.paddingRight=Y.paddingRight||0,Q.paddingBottom=Y.paddingBottom||0,Q.paddingTop=Y.paddingTop||0,k.idToDummyNode[z]=Q;var X=k.getGraphManager().add(k.newGraph(),Q),ie=Y.getChild();ie.add(Q);for(var j=0;jD?(O.rect.x-=(O.labelWidth-D)/2,O.setWidth(O.labelWidth),O.labelMarginLeft=(O.labelWidth-D)/2):O.labelPosHorizontal=="right"&&O.setWidth(D+O.labelWidth)),O.labelHeight&&(O.labelPosVertical=="top"?(O.rect.y-=O.labelHeight,O.setHeight(P+O.labelHeight),O.labelMarginTop=O.labelHeight):O.labelPosVertical=="center"&&O.labelHeight>P?(O.rect.y-=(O.labelHeight-P)/2,O.setHeight(O.labelHeight),O.labelMarginTop=(O.labelHeight-P)/2):O.labelPosVertical=="bottom"&&O.setHeight(P+O.labelHeight))}})},M.prototype.repopulateCompounds=function(){for(var k=this.compoundOrder.length-1;k>=0;k--){var I=this.compoundOrder[k],C=I.id,O=I.paddingLeft,D=I.paddingTop,P=I.labelMarginLeft,F=I.labelMarginTop;this.adjustLocations(this.tiledMemberPack[C],I.rect.x,I.rect.y,O,D,P,F)}},M.prototype.repopulateZeroDegreeMembers=function(){var k=this,I=this.tiledZeroDegreePack;Object.keys(I).forEach(function(C){var O=k.idToDummyNode[C],D=O.paddingLeft,P=O.paddingTop,F=O.labelMarginLeft,B=O.labelMarginTop;k.adjustLocations(I[C],O.rect.x,O.rect.y,D,P,F,B)})},M.prototype.getToBeTiled=function(k){var I=k.id;if(this.toBeTiled[I]!=null)return this.toBeTiled[I];var C=k.getChild();if(C==null)return this.toBeTiled[I]=!1,!1;for(var O=C.getNodes(),D=0;D0)return this.toBeTiled[I]=!1,!1;if(P.getChild()==null){this.toBeTiled[P.id]=!1;continue}if(!this.getToBeTiled(P))return this.toBeTiled[I]=!1,!1}return this.toBeTiled[I]=!0,!0},M.prototype.getNodeDegree=function(k){for(var I=k.id,C=k.getEdges(),O=0,D=0;DY&&(Y=X.rect.height)}C+=Y+k.verticalPadding}},M.prototype.tileCompoundMembers=function(k,I){var C=this;this.tiledMemberPack=[],Object.keys(k).forEach(function(O){var D=I[O];if(C.tiledMemberPack[O]=C.tileNodes(k[O],D.paddingLeft+D.paddingRight),D.rect.width=C.tiledMemberPack[O].width,D.rect.height=C.tiledMemberPack[O].height,D.setCenter(C.tiledMemberPack[O].centerX,C.tiledMemberPack[O].centerY),D.labelMarginLeft=0,D.labelMarginTop=0,m.NODE_DIMENSIONS_INCLUDE_LABELS){var P=D.rect.width,F=D.rect.height;D.labelWidth&&(D.labelPosHorizontal=="left"?(D.rect.x-=D.labelWidth,D.setWidth(P+D.labelWidth),D.labelMarginLeft=D.labelWidth):D.labelPosHorizontal=="center"&&D.labelWidth>P?(D.rect.x-=(D.labelWidth-P)/2,D.setWidth(D.labelWidth),D.labelMarginLeft=(D.labelWidth-P)/2):D.labelPosHorizontal=="right"&&D.setWidth(P+D.labelWidth)),D.labelHeight&&(D.labelPosVertical=="top"?(D.rect.y-=D.labelHeight,D.setHeight(F+D.labelHeight),D.labelMarginTop=D.labelHeight):D.labelPosVertical=="center"&&D.labelHeight>F?(D.rect.y-=(D.labelHeight-F)/2,D.setHeight(D.labelHeight),D.labelMarginTop=(D.labelHeight-F)/2):D.labelPosVertical=="bottom"&&D.setHeight(F+D.labelHeight))}})},M.prototype.tileNodes=function(k,I){var C=this.tileNodesByFavoringDim(k,I,!0),O=this.tileNodesByFavoringDim(k,I,!1),D=this.getOrgRatio(C),P=this.getOrgRatio(O),F;return PB&&(B=j.getWidth())});var $=P/D,z=F/D,Y=Math.pow(C-O,2)+4*($+O)*(z+C)*D,Q=(O-C+Math.sqrt(Y))/(2*($+O)),X;I?(X=Math.ceil(Q),X==Q&&X++):X=Math.floor(Q);var ie=X*($+O)-O;return B>ie&&(ie=B),ie+=O*2,ie},M.prototype.tileNodesByFavoringDim=function(k,I,C){var O=m.TILING_PADDING_VERTICAL,D=m.TILING_PADDING_HORIZONTAL,P=m.TILING_COMPARE_BY,F={rows:[],rowWidth:[],rowHeight:[],width:0,height:I,verticalPadding:O,horizontalPadding:D,centerX:0,centerY:0};P&&(F.idealRowWidth=this.calcIdealRowWidth(k,C));var B=o(function(J){return J.rect.width*J.rect.height},"getNodeArea"),$=o(function(J,Z){return B(Z)-B(J)},"areaCompareFcn");k.sort(function(j,J){var Z=$;return F.idealRowWidth?(Z=P,Z(j.id,J.id)):Z(j,J)});for(var z=0,Y=0,Q=0;Q0&&(F+=k.horizontalPadding),k.rowWidth[C]=F,k.width0&&(B+=k.verticalPadding);var $=0;B>k.rowHeight[C]&&($=k.rowHeight[C],k.rowHeight[C]=B,$=k.rowHeight[C]-$),k.height+=$,k.rows[C].push(I)},M.prototype.getShortestRowIndex=function(k){for(var I=-1,C=Number.MAX_VALUE,O=0;OC&&(I=O,C=k.rowWidth[O]);return I},M.prototype.canAddHorizontal=function(k,I,C){if(k.idealRowWidth){var O=k.rows.length-1,D=k.rowWidth[O];return D+I+k.horizontalPadding<=k.idealRowWidth}var P=this.getShortestRowIndex(k);if(P<0)return!0;var F=k.rowWidth[P];if(F+k.horizontalPadding+I<=k.width)return!0;var B=0;k.rowHeight[P]0&&(B=C+k.verticalPadding-k.rowHeight[P]);var $;k.width-F>=I+k.horizontalPadding?$=(k.height+B)/(F+I+k.horizontalPadding):$=(k.height+B)/k.width,B=C+k.verticalPadding;var z;return k.widthP&&I!=C){O.splice(-1,1),k.rows[C].push(D),k.rowWidth[I]=k.rowWidth[I]-P,k.rowWidth[C]=k.rowWidth[C]+P,k.width=k.rowWidth[instance.getLongestRowIndex(k)];for(var F=Number.MIN_VALUE,B=0;BF&&(F=O[B].height);I>0&&(F+=k.verticalPadding);var $=k.rowHeight[I]+k.rowHeight[C];k.rowHeight[I]=F,k.rowHeight[C]0)for(var ie=D;ie<=P;ie++)X[0]+=this.grid[ie][F-1].length+this.grid[ie][F].length-1;if(P0)for(var ie=F;ie<=B;ie++)X[3]+=this.grid[D-1][ie].length+this.grid[D][ie].length-1;for(var j=T.MAX_VALUE,J,Z,H=0;H{var u=l(551).FDLayoutNode,h=l(551).IMath;function f(p,m,g,y){u.call(this,p,m,g,y)}o(f,"CoSENode"),f.prototype=Object.create(u.prototype);for(var d in u)f[d]=u[d];f.prototype.calculateDisplacement=function(){var p=this.graphManager.getLayout();this.getChild()!=null&&this.fixedNodeWeight?(this.displacementX+=p.coolingFactor*(this.springForceX+this.repulsionForceX+this.gravitationForceX)/this.fixedNodeWeight,this.displacementY+=p.coolingFactor*(this.springForceY+this.repulsionForceY+this.gravitationForceY)/this.fixedNodeWeight):(this.displacementX+=p.coolingFactor*(this.springForceX+this.repulsionForceX+this.gravitationForceX)/this.noOfChildren,this.displacementY+=p.coolingFactor*(this.springForceY+this.repulsionForceY+this.gravitationForceY)/this.noOfChildren),Math.abs(this.displacementX)>p.coolingFactor*p.maxNodeDisplacement&&(this.displacementX=p.coolingFactor*p.maxNodeDisplacement*h.sign(this.displacementX)),Math.abs(this.displacementY)>p.coolingFactor*p.maxNodeDisplacement&&(this.displacementY=p.coolingFactor*p.maxNodeDisplacement*h.sign(this.displacementY)),this.child&&this.child.getNodes().length>0&&this.propogateDisplacementToChildren(this.displacementX,this.displacementY)},f.prototype.propogateDisplacementToChildren=function(p,m){for(var g=this.getChild().getNodes(),y,v=0;v{function u(g){if(Array.isArray(g)){for(var y=0,v=Array(g.length);y0){var Je=0;Ye.forEach(function(je){we=="horizontal"?(ye.set(je,x.has(je)?b[x.get(je)]:Ce.get(je)),Je+=ye.get(je)):(ye.set(je,x.has(je)?w[x.get(je)]:Ce.get(je)),Je+=ye.get(je))}),Je=Je/Ye.length,tt.forEach(function(je){Te.has(je)||ye.set(je,Je)})}else{var Ve=0;tt.forEach(function(je){we=="horizontal"?Ve+=x.has(je)?b[x.get(je)]:Ce.get(je):Ve+=x.has(je)?w[x.get(je)]:Ce.get(je)}),Ve=Ve/tt.length,tt.forEach(function(je){ye.set(je,Ve)})}});for(var Ze=o(function(){var Ye=ze.shift(),Je=ae.get(Ye);Je.forEach(function(Ve){if(ye.get(Ve.id)je&&(je=mt),Stkt&&(kt=St)}}catch(Qn){xt=!0,it=Qn}finally{try{!at&&dt.return&&dt.return()}finally{if(xt)throw it}}var gr=(Je+je)/2-(Ve+kt)/2,xn=!0,jt=!1,rn=void 0;try{for(var Er=tt[Symbol.iterator](),Kn;!(xn=(Kn=Er.next()).done);xn=!0){var hn=Kn.value;ye.set(hn,ye.get(hn)+gr)}}catch(Qn){jt=!0,rn=Qn}finally{try{!xn&&Er.return&&Er.return()}finally{if(jt)throw rn}}})}return ye},"findAppropriatePositionForRelativePlacement"),N=o(function(ae){var we=0,Te=0,Ce=0,Ae=0;if(ae.forEach(function(He){He.left?b[x.get(He.left)]-b[x.get(He.right)]>=0?we++:Te++:w[x.get(He.top)]-w[x.get(He.bottom)]>=0?Ce++:Ae++}),we>Te&&Ce>Ae)for(var Ge=0;GeTe)for(var Me=0;MeAe)for(var ye=0;ye1)y.fixedNodeConstraint.forEach(function(ne,ae){O[ae]=[ne.position.x,ne.position.y],D[ae]=[b[x.get(ne.nodeId)],w[x.get(ne.nodeId)]]}),P=!0;else if(y.alignmentConstraint)(function(){var ne=0;if(y.alignmentConstraint.vertical){for(var ae=y.alignmentConstraint.vertical,we=o(function(ye){var He=new Set;ae[ye].forEach(function(gt){He.add(gt)});var ze=new Set([].concat(u(He)).filter(function(gt){return B.has(gt)})),Ze=void 0;ze.size>0?Ze=b[x.get(ze.values().next().value)]:Ze=L(He).x,ae[ye].forEach(function(gt){O[ne]=[Ze,w[x.get(gt)]],D[ne]=[b[x.get(gt)],w[x.get(gt)]],ne++})},"_loop2"),Te=0;Te0?Ze=b[x.get(ze.values().next().value)]:Ze=L(He).y,Ce[ye].forEach(function(gt){O[ne]=[b[x.get(gt)],Ze],D[ne]=[b[x.get(gt)],w[x.get(gt)]],ne++})},"_loop3"),Ge=0;GeQ&&(Q=Y[ie].length,X=ie);if(Q0){var Ue={x:0,y:0};y.fixedNodeConstraint.forEach(function(ne,ae){var we={x:b[x.get(ne.nodeId)],y:w[x.get(ne.nodeId)]},Te=ne.position,Ce=A(Te,we);Ue.x+=Ce.x,Ue.y+=Ce.y}),Ue.x/=y.fixedNodeConstraint.length,Ue.y/=y.fixedNodeConstraint.length,b.forEach(function(ne,ae){b[ae]+=Ue.x}),w.forEach(function(ne,ae){w[ae]+=Ue.y}),y.fixedNodeConstraint.forEach(function(ne){b[x.get(ne.nodeId)]=ne.position.x,w[x.get(ne.nodeId)]=ne.position.y})}if(y.alignmentConstraint){if(y.alignmentConstraint.vertical)for(var Pe=y.alignmentConstraint.vertical,_e=o(function(ae){var we=new Set;Pe[ae].forEach(function(Ae){we.add(Ae)});var Te=new Set([].concat(u(we)).filter(function(Ae){return B.has(Ae)})),Ce=void 0;Te.size>0?Ce=b[x.get(Te.values().next().value)]:Ce=L(we).x,we.forEach(function(Ae){B.has(Ae)||(b[x.get(Ae)]=Ce)})},"_loop4"),me=0;me0?Ce=w[x.get(Te.values().next().value)]:Ce=L(we).y,we.forEach(function(Ae){B.has(Ae)||(w[x.get(Ae)]=Ce)})},"_loop5"),ge=0;ge{a.exports=t}},r={};function n(a){var s=r[a];if(s!==void 0)return s.exports;var l=r[a]={exports:{}};return e[a](l,l.exports,n),l.exports}o(n,"__webpack_require__");var i=n(45);return i})()})});var X1e=gi((xb,JB)=>{"use strict";o(function(e,r){typeof xb=="object"&&typeof JB=="object"?JB.exports=r(ZB()):typeof define=="function"&&define.amd?define(["cose-base"],r):typeof xb=="object"?xb.cytoscapeFcose=r(ZB()):e.cytoscapeFcose=r(e.coseBase)},"webpackUniversalModuleDefinition")(xb,function(t){return(()=>{"use strict";var e={658:a=>{a.exports=Object.assign!=null?Object.assign.bind(Object):function(s){for(var l=arguments.length,u=Array(l>1?l-1:0),h=1;h{var u=function(){function d(p,m){var g=[],y=!0,v=!1,x=void 0;try{for(var b=p[Symbol.iterator](),w;!(y=(w=b.next()).done)&&(g.push(w.value),!(m&&g.length===m));y=!0);}catch(S){v=!0,x=S}finally{try{!y&&b.return&&b.return()}finally{if(v)throw x}}return g}return o(d,"sliceIterator"),function(p,m){if(Array.isArray(p))return p;if(Symbol.iterator in Object(p))return d(p,m);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),h=l(140).layoutBase.LinkedList,f={};f.getTopMostNodes=function(d){for(var p={},m=0;m0&&P.merge(z)});for(var F=0;F1){w=x[0],S=w.connectedEdges().length,x.forEach(function(D){D.connectedEdges().length0&&g.set("dummy"+(g.size+1),_),A},f.relocateComponent=function(d,p,m){if(!m.fixedNodeConstraint){var g=Number.POSITIVE_INFINITY,y=Number.NEGATIVE_INFINITY,v=Number.POSITIVE_INFINITY,x=Number.NEGATIVE_INFINITY;if(m.quality=="draft"){var b=!0,w=!1,S=void 0;try{for(var T=p.nodeIndexes[Symbol.iterator](),E;!(b=(E=T.next()).done);b=!0){var _=E.value,A=u(_,2),L=A[0],M=A[1],N=m.cy.getElementById(L);if(N){var k=N.boundingBox(),I=p.xCoords[M]-k.w/2,C=p.xCoords[M]+k.w/2,O=p.yCoords[M]-k.h/2,D=p.yCoords[M]+k.h/2;Iy&&(y=C),Ox&&(x=D)}}}catch(z){w=!0,S=z}finally{try{!b&&T.return&&T.return()}finally{if(w)throw S}}var P=d.x-(y+g)/2,F=d.y-(x+v)/2;p.xCoords=p.xCoords.map(function(z){return z+P}),p.yCoords=p.yCoords.map(function(z){return z+F})}else{Object.keys(p).forEach(function(z){var Y=p[z],Q=Y.getRect().x,X=Y.getRect().x+Y.getRect().width,ie=Y.getRect().y,j=Y.getRect().y+Y.getRect().height;Qy&&(y=X),iex&&(x=j)});var B=d.x-(y+g)/2,$=d.y-(x+v)/2;Object.keys(p).forEach(function(z){var Y=p[z];Y.setCenter(Y.getCenterX()+B,Y.getCenterY()+$)})}}},f.calcBoundingBox=function(d,p,m,g){for(var y=Number.MAX_SAFE_INTEGER,v=Number.MIN_SAFE_INTEGER,x=Number.MAX_SAFE_INTEGER,b=Number.MIN_SAFE_INTEGER,w=void 0,S=void 0,T=void 0,E=void 0,_=d.descendants().not(":parent"),A=_.length,L=0;Lw&&(y=w),vT&&(x=T),b{var u=l(548),h=l(140).CoSELayout,f=l(140).CoSENode,d=l(140).layoutBase.PointD,p=l(140).layoutBase.DimensionD,m=l(140).layoutBase.LayoutConstants,g=l(140).layoutBase.FDLayoutConstants,y=l(140).CoSEConstants,v=o(function(b,w){var S=b.cy,T=b.eles,E=T.nodes(),_=T.edges(),A=void 0,L=void 0,M=void 0,N={};b.randomize&&(A=w.nodeIndexes,L=w.xCoords,M=w.yCoords);var k=o(function(z){return typeof z=="function"},"isFn"),I=o(function(z,Y){return k(z)?z(Y):z},"optFn"),C=u.calcParentsWithoutChildren(S,T),O=o(function $(z,Y,Q,X){for(var ie=Y.length,j=0;j0){var se=void 0;se=Q.getGraphManager().add(Q.newGraph(),H),$(se,Z,Q,X)}}},"processChildrenList"),D=o(function(z,Y,Q){for(var X=0,ie=0,j=0;j0?y.DEFAULT_EDGE_LENGTH=g.DEFAULT_EDGE_LENGTH=X/ie:k(b.idealEdgeLength)?y.DEFAULT_EDGE_LENGTH=g.DEFAULT_EDGE_LENGTH=50:y.DEFAULT_EDGE_LENGTH=g.DEFAULT_EDGE_LENGTH=b.idealEdgeLength,y.MIN_REPULSION_DIST=g.MIN_REPULSION_DIST=g.DEFAULT_EDGE_LENGTH/10,y.DEFAULT_RADIAL_SEPARATION=g.DEFAULT_EDGE_LENGTH)},"processEdges"),P=o(function(z,Y){Y.fixedNodeConstraint&&(z.constraints.fixedNodeConstraint=Y.fixedNodeConstraint),Y.alignmentConstraint&&(z.constraints.alignmentConstraint=Y.alignmentConstraint),Y.relativePlacementConstraint&&(z.constraints.relativePlacementConstraint=Y.relativePlacementConstraint)},"processConstraints");b.nestingFactor!=null&&(y.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR=g.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR=b.nestingFactor),b.gravity!=null&&(y.DEFAULT_GRAVITY_STRENGTH=g.DEFAULT_GRAVITY_STRENGTH=b.gravity),b.numIter!=null&&(y.MAX_ITERATIONS=g.MAX_ITERATIONS=b.numIter),b.gravityRange!=null&&(y.DEFAULT_GRAVITY_RANGE_FACTOR=g.DEFAULT_GRAVITY_RANGE_FACTOR=b.gravityRange),b.gravityCompound!=null&&(y.DEFAULT_COMPOUND_GRAVITY_STRENGTH=g.DEFAULT_COMPOUND_GRAVITY_STRENGTH=b.gravityCompound),b.gravityRangeCompound!=null&&(y.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR=g.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR=b.gravityRangeCompound),b.initialEnergyOnIncremental!=null&&(y.DEFAULT_COOLING_FACTOR_INCREMENTAL=g.DEFAULT_COOLING_FACTOR_INCREMENTAL=b.initialEnergyOnIncremental),b.tilingCompareBy!=null&&(y.TILING_COMPARE_BY=b.tilingCompareBy),b.quality=="proof"?m.QUALITY=2:m.QUALITY=0,y.NODE_DIMENSIONS_INCLUDE_LABELS=g.NODE_DIMENSIONS_INCLUDE_LABELS=m.NODE_DIMENSIONS_INCLUDE_LABELS=b.nodeDimensionsIncludeLabels,y.DEFAULT_INCREMENTAL=g.DEFAULT_INCREMENTAL=m.DEFAULT_INCREMENTAL=!b.randomize,y.ANIMATE=g.ANIMATE=m.ANIMATE=b.animate,y.TILE=b.tile,y.TILING_PADDING_VERTICAL=typeof b.tilingPaddingVertical=="function"?b.tilingPaddingVertical.call():b.tilingPaddingVertical,y.TILING_PADDING_HORIZONTAL=typeof b.tilingPaddingHorizontal=="function"?b.tilingPaddingHorizontal.call():b.tilingPaddingHorizontal,y.DEFAULT_INCREMENTAL=g.DEFAULT_INCREMENTAL=m.DEFAULT_INCREMENTAL=!0,y.PURE_INCREMENTAL=!b.randomize,m.DEFAULT_UNIFORM_LEAF_NODE_SIZES=b.uniformNodeDimensions,b.step=="transformed"&&(y.TRANSFORM_ON_CONSTRAINT_HANDLING=!0,y.ENFORCE_CONSTRAINTS=!1,y.APPLY_LAYOUT=!1),b.step=="enforced"&&(y.TRANSFORM_ON_CONSTRAINT_HANDLING=!1,y.ENFORCE_CONSTRAINTS=!0,y.APPLY_LAYOUT=!1),b.step=="cose"&&(y.TRANSFORM_ON_CONSTRAINT_HANDLING=!1,y.ENFORCE_CONSTRAINTS=!1,y.APPLY_LAYOUT=!0),b.step=="all"&&(b.randomize?y.TRANSFORM_ON_CONSTRAINT_HANDLING=!0:y.TRANSFORM_ON_CONSTRAINT_HANDLING=!1,y.ENFORCE_CONSTRAINTS=!0,y.APPLY_LAYOUT=!0),b.fixedNodeConstraint||b.alignmentConstraint||b.relativePlacementConstraint?y.TREE_REDUCTION_ON_INCREMENTAL=!1:y.TREE_REDUCTION_ON_INCREMENTAL=!0;var F=new h,B=F.newGraphManager();return O(B.addRoot(),u.getTopMostNodes(E),F,b),D(F,B,_),P(F,b),F.runLayout(),N},"coseLayout");a.exports={coseLayout:v}},212:(a,s,l)=>{var u=function(){function b(w,S){for(var T=0;T0)if(D){var B=d.getTopMostNodes(T.eles.nodes());if(k=d.connectComponents(E,T.eles,B),k.forEach(function(oe){var ke=oe.boundingBox();I.push({x:ke.x1+ke.w/2,y:ke.y1+ke.h/2})}),T.randomize&&k.forEach(function(oe){T.eles=oe,A.push(m(T))}),T.quality=="default"||T.quality=="proof"){var $=E.collection();if(T.tile){var z=new Map,Y=[],Q=[],X=0,ie={nodeIndexes:z,xCoords:Y,yCoords:Q},j=[];if(k.forEach(function(oe,ke){oe.edges().length==0&&(oe.nodes().forEach(function(Ie,Se){$.merge(oe.nodes()[Se]),Ie.isParent()||(ie.nodeIndexes.set(oe.nodes()[Se].id(),X++),ie.xCoords.push(oe.nodes()[0].position().x),ie.yCoords.push(oe.nodes()[0].position().y))}),j.push(ke))}),$.length>1){var J=$.boundingBox();I.push({x:J.x1+J.w/2,y:J.y1+J.h/2}),k.push($),A.push(ie);for(var Z=j.length-1;Z>=0;Z--)k.splice(j[Z],1),A.splice(j[Z],1),I.splice(j[Z],1)}}k.forEach(function(oe,ke){T.eles=oe,N.push(y(T,A[ke])),d.relocateComponent(I[ke],N[ke],T)})}else k.forEach(function(oe,ke){d.relocateComponent(I[ke],A[ke],T)});var H=new Set;if(k.length>1){var q=[],K=_.filter(function(oe){return oe.css("display")=="none"});k.forEach(function(oe,ke){var Ie=void 0;if(T.quality=="draft"&&(Ie=A[ke].nodeIndexes),oe.nodes().not(K).length>0){var Se={};Se.edges=[],Se.nodes=[];var Ue=void 0;oe.nodes().not(K).forEach(function(Pe){if(T.quality=="draft")if(!Pe.isParent())Ue=Ie.get(Pe.id()),Se.nodes.push({x:A[ke].xCoords[Ue]-Pe.boundingbox().w/2,y:A[ke].yCoords[Ue]-Pe.boundingbox().h/2,width:Pe.boundingbox().w,height:Pe.boundingbox().h});else{var _e=d.calcBoundingBox(Pe,A[ke].xCoords,A[ke].yCoords,Ie);Se.nodes.push({x:_e.topLeftX,y:_e.topLeftY,width:_e.width,height:_e.height})}else N[ke][Pe.id()]&&Se.nodes.push({x:N[ke][Pe.id()].getLeft(),y:N[ke][Pe.id()].getTop(),width:N[ke][Pe.id()].getWidth(),height:N[ke][Pe.id()].getHeight()})}),oe.edges().forEach(function(Pe){var _e=Pe.source(),me=Pe.target();if(_e.css("display")!="none"&&me.css("display")!="none")if(T.quality=="draft"){var W=Ie.get(_e.id()),fe=Ie.get(me.id()),ge=[],re=[];if(_e.isParent()){var he=d.calcBoundingBox(_e,A[ke].xCoords,A[ke].yCoords,Ie);ge.push(he.topLeftX+he.width/2),ge.push(he.topLeftY+he.height/2)}else ge.push(A[ke].xCoords[W]),ge.push(A[ke].yCoords[W]);if(me.isParent()){var ne=d.calcBoundingBox(me,A[ke].xCoords,A[ke].yCoords,Ie);re.push(ne.topLeftX+ne.width/2),re.push(ne.topLeftY+ne.height/2)}else re.push(A[ke].xCoords[fe]),re.push(A[ke].yCoords[fe]);Se.edges.push({startX:ge[0],startY:ge[1],endX:re[0],endY:re[1]})}else N[ke][_e.id()]&&N[ke][me.id()]&&Se.edges.push({startX:N[ke][_e.id()].getCenterX(),startY:N[ke][_e.id()].getCenterY(),endX:N[ke][me.id()].getCenterX(),endY:N[ke][me.id()].getCenterY()})}),Se.nodes.length>0&&(q.push(Se),H.add(ke))}});var se=O.packComponents(q,T.randomize).shifts;if(T.quality=="draft")A.forEach(function(oe,ke){var Ie=oe.xCoords.map(function(Ue){return Ue+se[ke].dx}),Se=oe.yCoords.map(function(Ue){return Ue+se[ke].dy});oe.xCoords=Ie,oe.yCoords=Se});else{var ce=0;H.forEach(function(oe){Object.keys(N[oe]).forEach(function(ke){var Ie=N[oe][ke];Ie.setCenter(Ie.getCenterX()+se[ce].dx,Ie.getCenterY()+se[ce].dy)}),ce++})}}}else{var P=T.eles.boundingBox();if(I.push({x:P.x1+P.w/2,y:P.y1+P.h/2}),T.randomize){var F=m(T);A.push(F)}T.quality=="default"||T.quality=="proof"?(N.push(y(T,A[0])),d.relocateComponent(I[0],N[0],T)):d.relocateComponent(I[0],A[0],T)}var ue=o(function(ke,Ie){if(T.quality=="default"||T.quality=="proof"){typeof ke=="number"&&(ke=Ie);var Se=void 0,Ue=void 0,Pe=ke.data("id");return N.forEach(function(me){Pe in me&&(Se={x:me[Pe].getRect().getCenterX(),y:me[Pe].getRect().getCenterY()},Ue=me[Pe])}),T.nodeDimensionsIncludeLabels&&(Ue.labelWidth&&(Ue.labelPosHorizontal=="left"?Se.x+=Ue.labelWidth/2:Ue.labelPosHorizontal=="right"&&(Se.x-=Ue.labelWidth/2)),Ue.labelHeight&&(Ue.labelPosVertical=="top"?Se.y+=Ue.labelHeight/2:Ue.labelPosVertical=="bottom"&&(Se.y-=Ue.labelHeight/2))),Se==null&&(Se={x:ke.position("x"),y:ke.position("y")}),{x:Se.x,y:Se.y}}else{var _e=void 0;return A.forEach(function(me){var W=me.nodeIndexes.get(ke.id());W!=null&&(_e={x:me.xCoords[W],y:me.yCoords[W]})}),_e==null&&(_e={x:ke.position("x"),y:ke.position("y")}),{x:_e.x,y:_e.y}}},"getPositions");if(T.quality=="default"||T.quality=="proof"||T.randomize){var te=d.calcParentsWithoutChildren(E,_),De=_.filter(function(oe){return oe.css("display")=="none"});T.eles=_.not(De),_.nodes().not(":parent").not(De).layoutPositions(S,T,ue),te.length>0&&te.forEach(function(oe){oe.position(ue(oe))})}else console.log("If randomize option is set to false, then quality option must be 'default' or 'proof'.")},"run")}]),b}();a.exports=x},657:(a,s,l)=>{var u=l(548),h=l(140).layoutBase.Matrix,f=l(140).layoutBase.SVD,d=o(function(m){var g=m.cy,y=m.eles,v=y.nodes(),x=y.nodes(":parent"),b=new Map,w=new Map,S=new Map,T=[],E=[],_=[],A=[],L=[],M=[],N=[],k=[],I=void 0,C=void 0,O=1e8,D=1e-9,P=m.piTol,F=m.samplingType,B=m.nodeSeparation,$=void 0,z=o(function(){for(var we=0,Te=0,Ce=!1;Te<$;){we=Math.floor(Math.random()*C),Ce=!1;for(var Ae=0;Ae=Ge;){ye=Ae[Ge++];for(var tt=T[ye],Ye=0;YeZe&&(Ze=L[Ve],gt=Ve)}return gt},"BFS"),Q=o(function(we){var Te=void 0;if(we){Te=Math.floor(Math.random()*C),I=Te;for(var Ae=0;Ae=1)break;Ze=ze}for(var tt=0;tt=1)break;Ze=ze}for(var Je=0;Je0&&(Te.isParent()?T[we].push(S.get(Te.id())):T[we].push(Te.id()))})});var te=o(function(we){var Te=w.get(we),Ce=void 0;b.get(we).forEach(function(Ae){g.getElementById(Ae).isParent()?Ce=S.get(Ae):Ce=Ae,T[Te].push(Ce),T[w.get(Ce)].push(we)})},"_loop"),De=!0,oe=!1,ke=void 0;try{for(var Ie=b.keys()[Symbol.iterator](),Se;!(De=(Se=Ie.next()).done);De=!0){var Ue=Se.value;te(Ue)}}catch(ae){oe=!0,ke=ae}finally{try{!De&&Ie.return&&Ie.return()}finally{if(oe)throw ke}}C=w.size;var Pe=void 0;if(C>2){$=C{var u=l(212),h=o(function(d){d&&d("layout","fcose",u)},"register");typeof cytoscape<"u"&&h(cytoscape),a.exports=h},140:a=>{a.exports=t}},r={};function n(a){var s=r[a];if(s!==void 0)return s.exports;var l=r[a]={exports:{}};return e[a](l,l.exports,n),l.exports}o(n,"__webpack_require__");var i=n(579);return i})()})});var T1,J0,eF=R(()=>{"use strict";V1();T1=o(t=>`${t}`,"wrapIcon"),J0={prefix:"mermaid-architecture",height:80,width:80,icons:{database:{body:T1('')},server:{body:T1('')},disk:{body:T1('')},internet:{body:T1('')},cloud:{body:T1('')},unknown:FC,blank:{body:T1("")}}}});var j1e,K1e,Q1e,Z1e,J1e=R(()=>{"use strict";V1();_t();Al();gb();eF();cC();j1e=o(async function(t,e){let r=Ci("padding"),n=Ci("iconSize"),i=n/2,a=n/6,s=a/2;await Promise.all(e.edges().map(async l=>{let{source:u,sourceDir:h,sourceArrow:f,sourceGroup:d,target:p,targetDir:m,targetArrow:g,targetGroup:y,label:v}=lC(l),{x,y:b}=l[0].sourceEndpoint(),{x:w,y:S}=l[0].midpoint(),{x:T,y:E}=l[0].targetEndpoint(),_=r+4;if(d&&(cs(h)?x+=h==="L"?-_:_:b+=h==="T"?-_:_+18),y&&(cs(m)?T+=m==="L"?-_:_:E+=m==="T"?-_:_+18),!d&&Z0.getNode(u)?.type==="junction"&&(cs(h)?x+=h==="L"?i:-i:b+=h==="T"?i:-i),!y&&Z0.getNode(p)?.type==="junction"&&(cs(m)?T+=m==="L"?i:-i:E+=m==="T"?i:-i),l[0]._private.rscratch){let A=t.insert("g");if(A.insert("path").attr("d",`M ${x},${b} L ${w},${S} L${T},${E} `).attr("class","edge"),f){let L=cs(h)?pb[h](x,a):x-s,M=Wc(h)?pb[h](b,a):b-s;A.insert("polygon").attr("points",qB[h](a)).attr("transform",`translate(${L},${M})`).attr("class","arrow")}if(g){let L=cs(m)?pb[m](T,a):T-s,M=Wc(m)?pb[m](E,a):E-s;A.insert("polygon").attr("points",qB[m](a)).attr("transform",`translate(${L},${M})`).attr("class","arrow")}if(v){let L=oC(h,m)?"XY":cs(h)?"X":"Y",M=0;L==="X"?M=Math.abs(x-T):L==="Y"?M=Math.abs(b-E)/1.5:M=Math.abs(x-T)/2;let N=A.append("g");if(await ta(N,v,{useHtmlLabels:!1,width:M,classes:"architecture-service-label"},de()),N.attr("dy","1em").attr("alignment-baseline","middle").attr("dominant-baseline","middle").attr("text-anchor","middle"),L==="X")N.attr("transform","translate("+w+", "+S+")");else if(L==="Y")N.attr("transform","translate("+w+", "+S+") rotate(-90)");else if(L==="XY"){let k=mb(h,m);if(k&&F1e(k)){let I=N.node().getBoundingClientRect(),[C,O]=G1e(k);N.attr("dominant-baseline","auto").attr("transform",`rotate(${-1*C*O*45})`);let D=N.node().getBoundingClientRect();N.attr("transform",` + translate(${w}, ${S-I.height/2}) + translate(${C*D.width/2}, ${O*D.height/2}) + rotate(${-1*C*O*45}, 0, ${I.height/2}) + `)}}}}}))},"drawEdges"),K1e=o(async function(t,e){let n=Ci("padding")*.75,i=Ci("fontSize"),s=Ci("iconSize")/2;await Promise.all(e.nodes().map(async l=>{let u=If(l);if(u.type==="group"){let{h,w:f,x1:d,y1:p}=l.boundingBox();t.append("rect").attr("x",d+s).attr("y",p+s).attr("width",f).attr("height",h).attr("class","node-bkg");let m=t.append("g"),g=d,y=p;if(u.icon){let v=m.append("g");v.html(`${await zb(u.icon,{height:n,width:n,fallbackPrefix:J0.prefix})}`),v.attr("transform","translate("+(g+s+1)+", "+(y+s+1)+")"),g+=n,y+=i/2-1-2}if(u.label){let v=m.append("g");await ta(v,u.label,{useHtmlLabels:!1,width:f,classes:"architecture-service-label"},de()),v.attr("dy","1em").attr("alignment-baseline","middle").attr("dominant-baseline","start").attr("text-anchor","start"),v.attr("transform","translate("+(g+s+4)+", "+(y+s+2)+")")}}}))},"drawGroups"),Q1e=o(async function(t,e,r){for(let n of r){let i=e.append("g"),a=Ci("iconSize");if(n.title){let h=i.append("g");await ta(h,n.title,{useHtmlLabels:!1,width:a*1.5,classes:"architecture-service-label"},de()),h.attr("dy","1em").attr("alignment-baseline","middle").attr("dominant-baseline","middle").attr("text-anchor","middle"),h.attr("transform","translate("+a/2+", "+a+")")}let s=i.append("g");if(n.icon)s.html(`${await zb(n.icon,{height:a,width:a,fallbackPrefix:J0.prefix})}`);else if(n.iconText){s.html(`${await zb("blank",{height:a,width:a,fallbackPrefix:J0.prefix})}`);let d=s.append("g").append("foreignObject").attr("width",a).attr("height",a).append("div").attr("class","node-icon-text").attr("style",`height: ${a}px;`).append("div").html(n.iconText),p=parseInt(window.getComputedStyle(d.node(),null).getPropertyValue("font-size").replace(/\D/g,""))??16;d.attr("style",`-webkit-line-clamp: ${Math.floor((a-2)/p)};`)}else s.append("path").attr("class","node-bkg").attr("id","node-"+n.id).attr("d",`M0 ${a} v${-a} q0,-5 5,-5 h${a} q5,0 5,5 v${a} H0 Z`);i.attr("class","architecture-service");let{width:l,height:u}=i._groups[0][0].getBBox();n.width=l,n.height=u,t.setElementForId(n.id,i)}return 0},"drawServices"),Z1e=o(function(t,e,r){r.forEach(n=>{let i=e.append("g"),a=Ci("iconSize");i.append("g").append("rect").attr("id","node-"+n.id).attr("fill-opacity","0").attr("width",a).attr("height",a),i.attr("class","architecture-junction");let{width:l,height:u}=i._groups[0][0].getBBox();i.width=l,i.height=u,t.setElementForId(n.id,i)})},"drawJunctions")});function iet(t,e){t.forEach(r=>{e.add({group:"nodes",data:{type:"service",id:r.id,icon:r.icon,label:r.title,parent:r.in,width:Ci("iconSize"),height:Ci("iconSize")},classes:"node-service"})})}function aet(t,e){t.forEach(r=>{e.add({group:"nodes",data:{type:"junction",id:r.id,parent:r.in,width:Ci("iconSize"),height:Ci("iconSize")},classes:"node-junction"})})}function set(t,e){e.nodes().map(r=>{let n=If(r);if(n.type==="group")return;n.x=r.position().x,n.y=r.position().y,t.getElementById(n.id).attr("transform","translate("+(n.x||0)+","+(n.y||0)+")")})}function oet(t,e){t.forEach(r=>{e.add({group:"nodes",data:{type:"group",id:r.id,icon:r.icon,label:r.title,parent:r.in},classes:"node-group"})})}function cet(t,e){t.forEach(r=>{let{lhsId:n,rhsId:i,lhsInto:a,lhsGroup:s,rhsInto:l,lhsDir:u,rhsDir:h,rhsGroup:f,title:d}=r,p=oC(r.lhsDir,r.rhsDir)?"segments":"straight",m={id:`${n}-${i}`,label:d,source:n,sourceDir:u,sourceArrow:a,sourceGroup:s,sourceEndpoint:u==="L"?"0 50%":u==="R"?"100% 50%":u==="T"?"50% 0":"50% 100%",target:i,targetDir:h,targetArrow:l,targetGroup:f,targetEndpoint:h==="L"?"0 50%":h==="R"?"100% 50%":h==="T"?"50% 0":"50% 100%"};e.add({group:"edges",data:m,classes:p})})}function uet(t){let e=t.map(i=>{let a={},s={};return Object.entries(i).forEach(([l,[u,h]])=>{a[h]||(a[h]=[]),s[u]||(s[u]=[]),a[h].push(l),s[u].push(l)}),{horiz:Object.values(a).filter(l=>l.length>1),vert:Object.values(s).filter(l=>l.length>1)}}),[r,n]=e.reduce(([i,a],{horiz:s,vert:l})=>[[...i,...s],[...a,...l]],[[],[]]);return{horizontal:r,vertical:n}}function het(t){let e=[],r=o(i=>`${i[0]},${i[1]}`,"posToStr"),n=o(i=>i.split(",").map(a=>parseInt(a)),"strToPos");return t.forEach(i=>{let a=Object.fromEntries(Object.entries(i).map(([h,f])=>[r(f),h])),s=[r([0,0])],l={},u={L:[-1,0],R:[1,0],T:[0,1],B:[0,-1]};for(;s.length>0;){let h=s.shift();if(h){l[h]=1;let f=a[h];if(f){let d=n(h);Object.entries(u).forEach(([p,m])=>{let g=r([d[0]+m[0],d[1]+m[1]]),y=a[g];y&&!l[g]&&(s.push(g),e.push({[WB[p]]:y,[WB[B1e(p)]]:f,gap:1.5*Ci("iconSize")}))})}}}}),e}function fet(t,e,r,n,{spatialMaps:i}){return new Promise(a=>{let s=$e("body").append("div").attr("id","cy").attr("style","display:none"),l=rl({container:document.getElementById("cy"),style:[{selector:"edge",style:{"curve-style":"straight",label:"data(label)","source-endpoint":"data(sourceEndpoint)","target-endpoint":"data(targetEndpoint)"}},{selector:"edge.segments",style:{"curve-style":"segments","segment-weights":"0","segment-distances":[.5],"edge-distances":"endpoints","source-endpoint":"data(sourceEndpoint)","target-endpoint":"data(targetEndpoint)"}},{selector:"node",style:{"compound-sizing-wrt-labels":"include"}},{selector:"node[label]",style:{"text-valign":"bottom","text-halign":"center","font-size":`${Ci("fontSize")}px`}},{selector:".node-service",style:{label:"data(label)",width:"data(width)",height:"data(height)"}},{selector:".node-junction",style:{width:"data(width)",height:"data(height)"}},{selector:".node-group",style:{padding:`${Ci("padding")}px`}}]});s.remove(),oet(r,l),iet(t,l),aet(e,l),cet(n,l);let u=uet(i),h=het(i),f=l.layout({name:"fcose",quality:"proof",styleEnabled:!1,animate:!1,nodeDimensionsIncludeLabels:!1,idealEdgeLength(d){let[p,m]=d.connectedNodes(),{parent:g}=If(p),{parent:y}=If(m);return g===y?1.5*Ci("iconSize"):.5*Ci("iconSize")},edgeElasticity(d){let[p,m]=d.connectedNodes(),{parent:g}=If(p),{parent:y}=If(m);return g===y?.45:.001},alignmentConstraint:u,relativePlacementConstraint:h});f.one("layoutstop",()=>{function d(p,m,g,y){let v,x,{x:b,y:w}=p,{x:S,y:T}=m;x=(y-w+(b-g)*(w-T)/(b-S))/Math.sqrt(1+Math.pow((w-T)/(b-S),2)),v=Math.sqrt(Math.pow(y-w,2)+Math.pow(g-b,2)-Math.pow(x,2));let E=Math.sqrt(Math.pow(S-b,2)+Math.pow(T-w,2));v=v/E;let _=(S-b)*(y-w)-(T-w)*(g-b);switch(!0){case _>=0:_=1;break;case _<0:_=-1;break}let A=(S-b)*(g-b)+(T-w)*(y-w);switch(!0){case A>=0:A=1;break;case A<0:A=-1;break}return x=Math.abs(x)*_,v=v*A,{distances:x,weights:v}}o(d,"getSegmentWeights"),l.startBatch();for(let p of Object.values(l.edges()))if(p.data?.()){let{x:m,y:g}=p.source().position(),{x:y,y:v}=p.target().position();if(m!==y&&g!==v){let x=p.sourceEndpoint(),b=p.targetEndpoint(),{sourceDir:w}=lC(p),[S,T]=Wc(w)?[x.x,b.y]:[b.x,x.y],{weights:E,distances:_}=d(x,b,S,T);p.style("segment-distances",_),p.style("segment-weights",E)}}l.endBatch(),f.run()}),f.run(),l.ready(d=>{V.info("Ready",d),a(l)})})}var eye,det,tye,rye=R(()=>{"use strict";V1();vB();eye=Xi(X1e(),1);Zt();ut();pf();Yn();gb();eF();cC();J1e();Fb([{name:J0.prefix,icons:J0}]);rl.use(eye.default);o(iet,"addServices");o(aet,"addJunctions");o(set,"positionNodes");o(oet,"addGroups");o(cet,"addEdges");o(uet,"getAlignments");o(het,"getRelativeConstraints");o(fet,"layoutArchitecture");det=o(async(t,e,r,n)=>{let i=n.db,a=i.getServices(),s=i.getJunctions(),l=i.getGroups(),u=i.getEdges(),h=i.getDataStructures(),f=Ps(e),d=f.append("g");d.attr("class","architecture-edges");let p=f.append("g");p.attr("class","architecture-services");let m=f.append("g");m.attr("class","architecture-groups"),await Q1e(i,p,a),Z1e(i,p,s);let g=await fet(a,s,l,u,h);await j1e(d,g),await K1e(m,g),set(i,g),Lo(void 0,f,Ci("padding"),Ci("useMaxWidth"))},"draw"),tye={draw:det}});var nye={};hr(nye,{diagram:()=>pet});var pet,iye=R(()=>{"use strict";Y1e();gb();q1e();rye();pet={parser:H1e,db:Z0,renderer:tye,styles:W1e}});var knt={};hr(knt,{default:()=>Tnt});V1();zC();Hf();var BX="c4",mCe=o(t=>/^\s*C4Context|C4Container|C4Component|C4Dynamic|C4Deployment/.test(t),"detector"),gCe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(PX(),OX));return{id:BX,diagram:t}},"loader"),yCe={id:BX,detector:mCe,loader:gCe},FX=yCe;var Zre="flowchart",SNe=o((t,e)=>e?.flowchart?.defaultRenderer==="dagre-wrapper"||e?.flowchart?.defaultRenderer==="elk"?!1:/^\s*graph/.test(t),"detector"),ANe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(uT(),cT));return{id:Zre,diagram:t}},"loader"),_Ne={id:Zre,detector:SNe,loader:ANe},Jre=_Ne;var ene="flowchart-v2",LNe=o((t,e)=>e?.flowchart?.defaultRenderer==="dagre-d3"?!1:(e?.flowchart?.defaultRenderer==="elk"&&(e.layout="elk"),/^\s*graph/.test(t)&&e?.flowchart?.defaultRenderer==="dagre-wrapper"?!0:/^\s*flowchart/.test(t)),"detector"),DNe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(uT(),cT));return{id:ene,diagram:t}},"loader"),RNe={id:ene,detector:LNe,loader:DNe},tne=RNe;var Dne="er",sMe=o(t=>/^\s*erDiagram/.test(t),"detector"),oMe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(Lne(),_ne));return{id:Dne,diagram:t}},"loader"),lMe={id:Dne,detector:sMe,loader:oMe},Rne=lMe;var $le="gitGraph",NBe=o(t=>/^\s*gitGraph/.test(t),"detector"),MBe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(Gle(),zle));return{id:$le,diagram:t}},"loader"),IBe={id:$le,detector:NBe,loader:MBe},Vle=IBe;var vce="gantt",wFe=o(t=>/^\s*gantt/.test(t),"detector"),TFe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(yce(),gce));return{id:vce,diagram:t}},"loader"),kFe={id:vce,detector:wFe,loader:TFe},xce=kFe;var _ce="info",LFe=o(t=>/^\s*info/.test(t),"detector"),DFe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(Ace(),Sce));return{id:_ce,diagram:t}},"loader"),Lce={id:_ce,detector:LFe,loader:DFe};var zce="pie",UFe=o(t=>/^\s*pie/.test(t),"detector"),HFe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(Fce(),Bce));return{id:zce,diagram:t}},"loader"),Gce={id:zce,detector:UFe,loader:HFe};var Jce="quadrantChart",lze=o(t=>/^\s*quadrantChart/.test(t),"detector"),cze=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(Zce(),Qce));return{id:Jce,diagram:t}},"loader"),uze={id:Jce,detector:lze,loader:cze},eue=uze;var Aue="xychart",Sze=o(t=>/^\s*xychart-beta/.test(t),"detector"),Aze=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(Sue(),Cue));return{id:Aue,diagram:t}},"loader"),_ze={id:Aue,detector:Sze,loader:Aze},_ue=_ze;var Hue="requirement",rGe=o(t=>/^\s*requirement(Diagram)?/.test(t),"detector"),nGe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(Uue(),Vue));return{id:Hue,diagram:t}},"loader"),iGe={id:Hue,detector:rGe,loader:nGe},Yue=iGe;var vhe="sequence",o$e=o(t=>/^\s*sequenceDiagram/.test(t),"detector"),l$e=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(yhe(),ghe));return{id:vhe,diagram:t}},"loader"),c$e={id:vhe,detector:o$e,loader:l$e},xhe=c$e;var Ihe="class",U$e=o((t,e)=>e?.class?.defaultRenderer==="dagre-wrapper"?!1:/^\s*classDiagram/.test(t),"detector"),H$e=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(Mhe(),Nhe));return{id:Ihe,diagram:t}},"loader"),Y$e={id:Ihe,detector:U$e,loader:H$e},Ohe=Y$e;var cfe="classDiagram",vVe=o((t,e)=>/^\s*classDiagram/.test(t)&&e?.class?.defaultRenderer==="dagre-wrapper"?!0:/^\s*classDiagram-v2/.test(t),"detector"),xVe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(lfe(),ofe));return{id:cfe,diagram:t}},"loader"),bVe={id:cfe,detector:vVe,loader:xVe},ufe=bVe;var tde="state",mUe=o((t,e)=>e?.state?.defaultRenderer==="dagre-wrapper"?!1:/^\s*stateDiagram/.test(t),"detector"),gUe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(ede(),Jfe));return{id:tde,diagram:t}},"loader"),yUe={id:tde,detector:mUe,loader:gUe},rde=yUe;var ade="stateDiagram",xUe=o((t,e)=>!!(/^\s*stateDiagram-v2/.test(t)||/^\s*stateDiagram/.test(t)&&e?.state?.defaultRenderer==="dagre-wrapper"),"detector"),bUe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(ide(),nde));return{id:ade,diagram:t}},"loader"),wUe={id:ade,detector:xUe,loader:bUe},sde=wUe;var Tde="journey",VUe=o(t=>/^\s*journey/.test(t),"detector"),UUe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(wde(),bde));return{id:Tde,diagram:t}},"loader"),HUe={id:Tde,detector:VUe,loader:UUe},kde=HUe;ut();pf();Yn();var YUe=o((t,e,r)=>{V.debug(`rendering svg for syntax error +`);let n=Ps(e),i=n.append("g");n.attr("viewBox","0 0 2412 512"),Sr(n,100,512,!0),i.append("path").attr("class","error-icon").attr("d","m411.313,123.313c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32-9.375,9.375-20.688-20.688c-12.484-12.5-32.766-12.5-45.25,0l-16,16c-1.261,1.261-2.304,2.648-3.31,4.051-21.739-8.561-45.324-13.426-70.065-13.426-105.867,0-192,86.133-192,192s86.133,192 192,192 192-86.133 192-192c0-24.741-4.864-48.327-13.426-70.065 1.402-1.007 2.79-2.049 4.051-3.31l16-16c12.5-12.492 12.5-32.758 0-45.25l-20.688-20.688 9.375-9.375 32.001-31.999zm-219.313,100.687c-52.938,0-96,43.063-96,96 0,8.836-7.164,16-16,16s-16-7.164-16-16c0-70.578 57.422-128 128-128 8.836,0 16,7.164 16,16s-7.164,16-16,16z"),i.append("path").attr("class","error-icon").attr("d","m459.02,148.98c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l16,16c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16.001-16z"),i.append("path").attr("class","error-icon").attr("d","m340.395,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16-16c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l15.999,16z"),i.append("path").attr("class","error-icon").attr("d","m400,64c8.844,0 16-7.164 16-16v-32c0-8.836-7.156-16-16-16-8.844,0-16,7.164-16,16v32c0,8.836 7.156,16 16,16z"),i.append("path").attr("class","error-icon").attr("d","m496,96.586h-32c-8.844,0-16,7.164-16,16 0,8.836 7.156,16 16,16h32c8.844,0 16-7.164 16-16 0-8.836-7.156-16-16-16z"),i.append("path").attr("class","error-icon").attr("d","m436.98,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688l32-32c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32c-6.251,6.25-6.251,16.375-0.001,22.625z"),i.append("text").attr("class","error-text").attr("x",1440).attr("y",250).attr("font-size","150px").style("text-anchor","middle").text("Syntax error in text"),i.append("text").attr("class","error-text").attr("x",1250).attr("y",400).attr("font-size","100px").style("text-anchor","middle").text(`mermaid version ${r}`)},"draw"),fP={draw:YUe},Ede=fP;var WUe={db:{},renderer:fP,parser:{parse:o(()=>{},"parse")}},Cde=WUe;var Sde="flowchart-elk",qUe=o((t,e={})=>/^\s*flowchart-elk/.test(t)||/^\s*flowchart|graph/.test(t)&&e?.flowchart?.defaultRenderer==="elk"?(e.layout="elk",!0):!1,"detector"),XUe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(uT(),cT));return{id:Sde,diagram:t}},"loader"),jUe={id:Sde,detector:qUe,loader:XUe},Ade=jUe;var Jde="timeline",pHe=o(t=>/^\s*timeline/.test(t),"detector"),mHe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(Zde(),Qde));return{id:Jde,diagram:t}},"loader"),gHe={id:Jde,detector:pHe,loader:mHe},e0e=gHe;var vge="mindmap",TZe=o(t=>/^\s*mindmap/.test(t),"detector"),kZe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(yge(),gge));return{id:vge,diagram:t}},"loader"),EZe={id:vge,detector:TZe,loader:kZe},xge=EZe;var Zge="sankey",WZe=o(t=>/^\s*sankey-beta/.test(t),"detector"),qZe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(Qge(),Kge));return{id:Zge,diagram:t}},"loader"),XZe={id:Zge,detector:WZe,loader:qZe},Jge=XZe;var c1e="packet",oJe=o(t=>/^\s*packet-beta/.test(t),"detector"),lJe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(l1e(),o1e));return{id:c1e,diagram:t}},"loader"),u1e={id:c1e,detector:oJe,loader:lJe};var O1e="block",FJe=o(t=>/^\s*block-beta/.test(t),"detector"),zJe=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(I1e(),M1e));return{id:O1e,diagram:t}},"loader"),GJe={id:O1e,detector:FJe,loader:zJe},P1e=GJe;var aye="architecture",met=o(t=>/^\s*architecture/.test(t),"detector"),get=o(async()=>{let{diagram:t}=await Promise.resolve().then(()=>(iye(),nye));return{id:aye,diagram:t}},"loader"),yet={id:aye,detector:met,loader:get},sye=yet;Hf();_t();var oye=!1,k1=o(()=>{oye||(oye=!0,Jf("error",Cde,t=>t.toLowerCase().trim()==="error"),Jf("---",{db:{clear:o(()=>{},"clear")},styles:{},renderer:{draw:o(()=>{},"draw")},parser:{parse:o(()=>{throw new Error("Diagrams beginning with --- are not valid. If you were trying to use a YAML front-matter, please ensure that you've correctly opened and closed the YAML front-matter with un-indented `---` blocks")},"parse")},init:o(()=>null,"init")},t=>t.toLowerCase().trimStart().startsWith("---")),Ub(FX,ufe,Ohe,Rne,xce,Lce,Gce,Yue,xhe,Ade,tne,Jre,xge,e0e,Vle,sde,rde,kde,eue,Jge,u1e,_ue,P1e,sye))},"addDiagrams");ut();Hf();_t();var lye=o(async()=>{V.debug("Loading registered diagrams");let e=(await Promise.allSettled(Object.entries(Uf).map(async([r,{detector:n,loader:i}])=>{if(i)try{cy(r)}catch{try{let{diagram:a,id:s}=await i();Jf(s,a,n)}catch(a){throw V.error(`Failed to load external diagram with key ${r}. Removing from detectors.`),delete Uf[r],a}}}))).filter(r=>r.status==="rejected");if(e.length>0){V.error(`Failed to load ${e.length} external diagrams`);for(let r of e)V.error(r);throw new Error(`Failed to load ${e.length} external diagrams`)}},"loadRegisteredDiagrams");ut();Zt();var uC="comm",hC="rule",fC="decl";var cye="@import";var uye="@keyframes";var hye="@layer";var tF=Math.abs,bb=String.fromCharCode;function dC(t){return t.trim()}o(dC,"trim");function wb(t,e,r){return t.replace(e,r)}o(wb,"replace");function fye(t,e,r){return t.indexOf(e,r)}o(fye,"indexof");function ep(t,e){return t.charCodeAt(e)|0}o(ep,"charat");function Of(t,e,r){return t.slice(e,r)}o(Of,"substr");function wo(t){return t.length}o(wo,"strlen");function dye(t){return t.length}o(dye,"sizeof");function E1(t,e){return e.push(t),t}o(E1,"append");var pC=1,C1=1,pye=0,nl=0,Si=0,A1="";function mC(t,e,r,n,i,a,s,l){return{value:t,root:e,parent:r,type:n,props:i,children:a,line:pC,column:C1,length:s,return:"",siblings:l}}o(mC,"node");function mye(){return Si}o(mye,"char");function gye(){return Si=nl>0?ep(A1,--nl):0,C1--,Si===10&&(C1=1,pC--),Si}o(gye,"prev");function il(){return Si=nl2||S1(Si)>3?"":" "}o(xye,"whitespace");function bye(t,e){for(;--e&&il()&&!(Si<48||Si>102||Si>57&&Si<65||Si>70&&Si<97););return gC(t,Tb()+(e<6&&th()==32&&il()==32))}o(bye,"escaping");function rF(t){for(;il();)switch(Si){case t:return nl;case 34:case 39:t!==34&&t!==39&&rF(Si);break;case 40:t===41&&rF(t);break;case 92:il();break}return nl}o(rF,"delimiter");function wye(t,e){for(;il()&&t+Si!==57;)if(t+Si===84&&th()===47)break;return"/*"+gC(e,nl-1)+"*"+bb(t===47?t:il())}o(wye,"commenter");function Tye(t){for(;!S1(th());)il();return gC(t,nl)}o(Tye,"identifier");function Cye(t){return vye(vC("",null,null,null,[""],t=yye(t),0,[0],t))}o(Cye,"compile");function vC(t,e,r,n,i,a,s,l,u){for(var h=0,f=0,d=s,p=0,m=0,g=0,y=1,v=1,x=1,b=0,w="",S=i,T=a,E=n,_=w;v;)switch(g=b,b=il()){case 40:if(g!=108&&ep(_,d-1)==58){fye(_+=wb(yC(b),"&","&\f"),"&\f",tF(h?l[h-1]:0))!=-1&&(x=-1);break}case 34:case 39:case 91:_+=yC(b);break;case 9:case 10:case 13:case 32:_+=xye(g);break;case 92:_+=bye(Tb()-1,7);continue;case 47:switch(th()){case 42:case 47:E1(vet(wye(il(),Tb()),e,r,u),u),(S1(g||1)==5||S1(th()||1)==5)&&wo(_)&&Of(_,-1,void 0)!==" "&&(_+=" ");break;default:_+="/"}break;case 123*y:l[h++]=wo(_)*x;case 125*y:case 59:case 0:switch(b){case 0:case 125:v=0;case 59+f:x==-1&&(_=wb(_,/\f/g,"")),m>0&&(wo(_)-d||y===0&&g===47)&&E1(m>32?Eye(_+";",n,r,d-1,u):Eye(wb(_," ","")+";",n,r,d-2,u),u);break;case 59:_+=";";default:if(E1(E=kye(_,e,r,h,f,i,l,w,S=[],T=[],d,a),a),b===123)if(f===0)vC(_,e,E,E,S,a,d,l,T);else switch(p===99&&ep(_,3)===110?100:p){case 100:case 108:case 109:case 115:vC(t,E,E,n&&E1(kye(t,E,E,0,0,i,l,w,i,S=[],d,T),T),i,T,d,l,n?S:T);break;default:vC(_,E,E,E,[""],T,0,l,T)}}h=f=m=0,y=x=1,w=_="",d=s;break;case 58:d=1+wo(_),m=g;default:if(y<1){if(b==123)--y;else if(b==125&&y++==0&&gye()==125)continue}switch(_+=bb(b),b*y){case 38:x=f>0?1:(_+="\f",-1);break;case 44:l[h++]=(wo(_)-1)*x,x=1;break;case 64:th()===45&&(_+=yC(il())),p=th(),f=d=wo(w=_+=Tye(Tb())),b++;break;case 45:g===45&&wo(_)==2&&(y=0)}}return a}o(vC,"parse");function kye(t,e,r,n,i,a,s,l,u,h,f,d){for(var p=i-1,m=i===0?a:[""],g=dye(m),y=0,v=0,x=0;y0?m[b]+" "+w:wb(w,/&\f/g,m[b])))&&(u[x++]=S);return mC(t,e,r,i===0?hC:l,u,h,f,d)}o(kye,"ruleset");function vet(t,e,r,n){return mC(t,e,r,uC,bb(mye()),Of(t,2,-2),0,n)}o(vet,"comment");function Eye(t,e,r,n,i){return mC(t,e,r,fC,Of(t,0,n),Of(t,n+1,-1),n,i)}o(Eye,"declaration");function xC(t,e){for(var r="",n=0;n{Lye.forEach(t=>{t()}),Lye=[]},"attachFunctions");ut();var Rye=o(t=>t.replace(/^\s*%%(?!{)[^\n]+\n?/gm,"").trimStart(),"cleanupComments");Vb();function qye(t){return typeof t>"u"||t===null}o(qye,"isNothing");function bet(t){return typeof t=="object"&&t!==null}o(bet,"isObject");function wet(t){return Array.isArray(t)?t:qye(t)?[]:[t]}o(wet,"toArray");function Tet(t,e){var r,n,i,a;if(e)for(a=Object.keys(e),r=0,n=a.length;rl&&(a=" ... ",e=n-l+a.length),r-n>l&&(s=" ...",r=n+l-s.length),{str:a+t.slice(e,r).replace(/\t/g,"\u2192")+s,pos:n-e+a.length}}o(nF,"getLine");function iF(t,e){return Wi.repeat(" ",e-t.length)+t}o(iF,"padStart");function Net(t,e){if(e=Object.create(e||null),!t.buffer)return null;e.maxLength||(e.maxLength=79),typeof e.indent!="number"&&(e.indent=1),typeof e.linesBefore!="number"&&(e.linesBefore=3),typeof e.linesAfter!="number"&&(e.linesAfter=2);for(var r=/\r?\n|\r|\0/g,n=[0],i=[],a,s=-1;a=r.exec(t.buffer);)i.push(a.index),n.push(a.index+a[0].length),t.position<=a.index&&s<0&&(s=n.length-2);s<0&&(s=n.length-1);var l="",u,h,f=Math.min(t.line+e.linesAfter,i.length).toString().length,d=e.maxLength-(e.indent+f+3);for(u=1;u<=e.linesBefore&&!(s-u<0);u++)h=nF(t.buffer,n[s-u],i[s-u],t.position-(n[s]-n[s-u]),d),l=Wi.repeat(" ",e.indent)+iF((t.line-u+1).toString(),f)+" | "+h.str+` +`+l;for(h=nF(t.buffer,n[s],i[s],t.position,d),l+=Wi.repeat(" ",e.indent)+iF((t.line+1).toString(),f)+" | "+h.str+` +`,l+=Wi.repeat("-",e.indent+f+3+h.pos)+`^ +`,u=1;u<=e.linesAfter&&!(s+u>=i.length);u++)h=nF(t.buffer,n[s+u],i[s+u],t.position-(n[s]-n[s+u]),d),l+=Wi.repeat(" ",e.indent)+iF((t.line+u+1).toString(),f)+" | "+h.str+` +`;return l.replace(/\n$/,"")}o(Net,"makeSnippet");var Met=Net,Iet=["kind","multi","resolve","construct","instanceOf","predicate","represent","representName","defaultStyle","styleAliases"],Oet=["scalar","sequence","mapping"];function Pet(t){var e={};return t!==null&&Object.keys(t).forEach(function(r){t[r].forEach(function(n){e[String(n)]=r})}),e}o(Pet,"compileStyleAliases");function Bet(t,e){if(e=e||{},Object.keys(e).forEach(function(r){if(Iet.indexOf(r)===-1)throw new $s('Unknown option "'+r+'" is met in definition of "'+t+'" YAML type.')}),this.options=e,this.tag=t,this.kind=e.kind||null,this.resolve=e.resolve||function(){return!0},this.construct=e.construct||function(r){return r},this.instanceOf=e.instanceOf||null,this.predicate=e.predicate||null,this.represent=e.represent||null,this.representName=e.representName||null,this.defaultStyle=e.defaultStyle||null,this.multi=e.multi||!1,this.styleAliases=Pet(e.styleAliases||null),Oet.indexOf(this.kind)===-1)throw new $s('Unknown kind "'+this.kind+'" is specified for "'+t+'" YAML type.')}o(Bet,"Type$1");var Va=Bet;function Nye(t,e){var r=[];return t[e].forEach(function(n){var i=r.length;r.forEach(function(a,s){a.tag===n.tag&&a.kind===n.kind&&a.multi===n.multi&&(i=s)}),r[i]=n}),r}o(Nye,"compileList");function Fet(){var t={scalar:{},sequence:{},mapping:{},fallback:{},multi:{scalar:[],sequence:[],mapping:[],fallback:[]}},e,r;function n(i){i.multi?(t.multi[i.kind].push(i),t.multi.fallback.push(i)):t[i.kind][i.tag]=t.fallback[i.tag]=i}for(o(n,"collectType"),e=0,r=arguments.length;e=0?"0b"+t.toString(2):"-0b"+t.toString(2).slice(1)},"binary"),octal:o(function(t){return t>=0?"0o"+t.toString(8):"-0o"+t.toString(8).slice(1)},"octal"),decimal:o(function(t){return t.toString(10)},"decimal"),hexadecimal:o(function(t){return t>=0?"0x"+t.toString(16).toUpperCase():"-0x"+t.toString(16).toUpperCase().slice(1)},"hexadecimal")},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}}),att=new RegExp("^(?:[-+]?(?:[0-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");function stt(t){return!(t===null||!att.test(t)||t[t.length-1]==="_")}o(stt,"resolveYamlFloat");function ott(t){var e,r;return e=t.replace(/_/g,"").toLowerCase(),r=e[0]==="-"?-1:1,"+-".indexOf(e[0])>=0&&(e=e.slice(1)),e===".inf"?r===1?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:e===".nan"?NaN:r*parseFloat(e,10)}o(ott,"constructYamlFloat");var ltt=/^[-+]?[0-9]+e/;function ctt(t,e){var r;if(isNaN(t))switch(e){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===t)switch(e){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===t)switch(e){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(Wi.isNegativeZero(t))return"-0.0";return r=t.toString(10),ltt.test(r)?r.replace("e",".e"):r}o(ctt,"representYamlFloat");function utt(t){return Object.prototype.toString.call(t)==="[object Number]"&&(t%1!==0||Wi.isNegativeZero(t))}o(utt,"isFloat");var htt=new Va("tag:yaml.org,2002:float",{kind:"scalar",resolve:stt,construct:ott,predicate:utt,represent:ctt,defaultStyle:"lowercase"}),jye=Uet.extend({implicit:[qet,Qet,itt,htt]}),ftt=jye,Kye=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"),Qye=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");function dtt(t){return t===null?!1:Kye.exec(t)!==null||Qye.exec(t)!==null}o(dtt,"resolveYamlTimestamp");function ptt(t){var e,r,n,i,a,s,l,u=0,h=null,f,d,p;if(e=Kye.exec(t),e===null&&(e=Qye.exec(t)),e===null)throw new Error("Date resolve error");if(r=+e[1],n=+e[2]-1,i=+e[3],!e[4])return new Date(Date.UTC(r,n,i));if(a=+e[4],s=+e[5],l=+e[6],e[7]){for(u=e[7].slice(0,3);u.length<3;)u+="0";u=+u}return e[9]&&(f=+e[10],d=+(e[11]||0),h=(f*60+d)*6e4,e[9]==="-"&&(h=-h)),p=new Date(Date.UTC(r,n,i,a,s,l,u)),h&&p.setTime(p.getTime()-h),p}o(ptt,"constructYamlTimestamp");function mtt(t){return t.toISOString()}o(mtt,"representYamlTimestamp");var gtt=new Va("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:dtt,construct:ptt,instanceOf:Date,represent:mtt});function ytt(t){return t==="<<"||t===null}o(ytt,"resolveYamlMerge");var vtt=new Va("tag:yaml.org,2002:merge",{kind:"scalar",resolve:ytt}),hF=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/= +\r`;function xtt(t){if(t===null)return!1;var e,r,n=0,i=t.length,a=hF;for(r=0;r64)){if(e<0)return!1;n+=6}return n%8===0}o(xtt,"resolveYamlBinary");function btt(t){var e,r,n=t.replace(/[\r\n=]/g,""),i=n.length,a=hF,s=0,l=[];for(e=0;e>16&255),l.push(s>>8&255),l.push(s&255)),s=s<<6|a.indexOf(n.charAt(e));return r=i%4*6,r===0?(l.push(s>>16&255),l.push(s>>8&255),l.push(s&255)):r===18?(l.push(s>>10&255),l.push(s>>2&255)):r===12&&l.push(s>>4&255),new Uint8Array(l)}o(btt,"constructYamlBinary");function wtt(t){var e="",r=0,n,i,a=t.length,s=hF;for(n=0;n>18&63],e+=s[r>>12&63],e+=s[r>>6&63],e+=s[r&63]),r=(r<<8)+t[n];return i=a%3,i===0?(e+=s[r>>18&63],e+=s[r>>12&63],e+=s[r>>6&63],e+=s[r&63]):i===2?(e+=s[r>>10&63],e+=s[r>>4&63],e+=s[r<<2&63],e+=s[64]):i===1&&(e+=s[r>>2&63],e+=s[r<<4&63],e+=s[64],e+=s[64]),e}o(wtt,"representYamlBinary");function Ttt(t){return Object.prototype.toString.call(t)==="[object Uint8Array]"}o(Ttt,"isBinary");var ktt=new Va("tag:yaml.org,2002:binary",{kind:"scalar",resolve:xtt,construct:btt,predicate:Ttt,represent:wtt}),Ett=Object.prototype.hasOwnProperty,Ctt=Object.prototype.toString;function Stt(t){if(t===null)return!0;var e=[],r,n,i,a,s,l=t;for(r=0,n=l.length;r>10)+55296,(t-65536&1023)+56320)}o(Htt,"charFromCodepoint");var nve=new Array(256),ive=new Array(256);for(tp=0;tp<256;tp++)nve[tp]=Oye(tp)?1:0,ive[tp]=Oye(tp);var tp;function Ytt(t,e){this.input=t,this.filename=e.filename||null,this.schema=e.schema||Zye,this.onWarning=e.onWarning||null,this.legacy=e.legacy||!1,this.json=e.json||!1,this.listener=e.listener||null,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=t.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.firstTabInLine=-1,this.documents=[]}o(Ytt,"State$1");function ave(t,e){var r={name:t.filename,buffer:t.input.slice(0,-1),position:t.position,line:t.line,column:t.position-t.lineStart};return r.snippet=Met(r),new $s(e,r)}o(ave,"generateError");function Gt(t,e){throw ave(t,e)}o(Gt,"throwError");function TC(t,e){t.onWarning&&t.onWarning.call(null,ave(t,e))}o(TC,"throwWarning");var Pye={YAML:o(function(e,r,n){var i,a,s;e.version!==null&&Gt(e,"duplication of %YAML directive"),n.length!==1&&Gt(e,"YAML directive accepts exactly one argument"),i=/^([0-9]+)\.([0-9]+)$/.exec(n[0]),i===null&&Gt(e,"ill-formed argument of the YAML directive"),a=parseInt(i[1],10),s=parseInt(i[2],10),a!==1&&Gt(e,"unacceptable YAML version of the document"),e.version=n[0],e.checkLineBreaks=s<2,s!==1&&s!==2&&TC(e,"unsupported YAML version of the document")},"handleYamlDirective"),TAG:o(function(e,r,n){var i,a;n.length!==2&&Gt(e,"TAG directive accepts exactly two arguments"),i=n[0],a=n[1],tve.test(i)||Gt(e,"ill-formed tag handle (first argument) of the TAG directive"),Bf.call(e.tagMap,i)&&Gt(e,'there is a previously declared suffix for "'+i+'" tag handle'),rve.test(a)||Gt(e,"ill-formed tag prefix (second argument) of the TAG directive");try{a=decodeURIComponent(a)}catch{Gt(e,"tag prefix is malformed: "+a)}e.tagMap[i]=a},"handleTagDirective")};function Pf(t,e,r,n){var i,a,s,l;if(e1&&(t.result+=Wi.repeat(` +`,e-1))}o(dF,"writeFoldedLines");function Wtt(t,e,r){var n,i,a,s,l,u,h,f,d=t.kind,p=t.result,m;if(m=t.input.charCodeAt(t.position),Vs(m)||D1(m)||m===35||m===38||m===42||m===33||m===124||m===62||m===39||m===34||m===37||m===64||m===96||(m===63||m===45)&&(i=t.input.charCodeAt(t.position+1),Vs(i)||r&&D1(i)))return!1;for(t.kind="scalar",t.result="",a=s=t.position,l=!1;m!==0;){if(m===58){if(i=t.input.charCodeAt(t.position+1),Vs(i)||r&&D1(i))break}else if(m===35){if(n=t.input.charCodeAt(t.position-1),Vs(n))break}else{if(t.position===t.lineStart&&CC(t)||r&&D1(m))break;if(qc(m))if(u=t.line,h=t.lineStart,f=t.lineIndent,Ai(t,!1,-1),t.lineIndent>=e){l=!0,m=t.input.charCodeAt(t.position);continue}else{t.position=s,t.line=u,t.lineStart=h,t.lineIndent=f;break}}l&&(Pf(t,a,s,!1),dF(t,t.line-u),a=s=t.position,l=!1),rp(m)||(s=t.position+1),m=t.input.charCodeAt(++t.position)}return Pf(t,a,s,!1),t.result?!0:(t.kind=d,t.result=p,!1)}o(Wtt,"readPlainScalar");function qtt(t,e){var r,n,i;if(r=t.input.charCodeAt(t.position),r!==39)return!1;for(t.kind="scalar",t.result="",t.position++,n=i=t.position;(r=t.input.charCodeAt(t.position))!==0;)if(r===39)if(Pf(t,n,t.position,!0),r=t.input.charCodeAt(++t.position),r===39)n=t.position,t.position++,i=t.position;else return!0;else qc(r)?(Pf(t,n,i,!0),dF(t,Ai(t,!1,e)),n=i=t.position):t.position===t.lineStart&&CC(t)?Gt(t,"unexpected end of the document within a single quoted scalar"):(t.position++,i=t.position);Gt(t,"unexpected end of the stream within a single quoted scalar")}o(qtt,"readSingleQuotedScalar");function Xtt(t,e){var r,n,i,a,s,l;if(l=t.input.charCodeAt(t.position),l!==34)return!1;for(t.kind="scalar",t.result="",t.position++,r=n=t.position;(l=t.input.charCodeAt(t.position))!==0;){if(l===34)return Pf(t,r,t.position,!0),t.position++,!0;if(l===92){if(Pf(t,r,t.position,!0),l=t.input.charCodeAt(++t.position),qc(l))Ai(t,!1,e);else if(l<256&&nve[l])t.result+=ive[l],t.position++;else if((s=Vtt(l))>0){for(i=s,a=0;i>0;i--)l=t.input.charCodeAt(++t.position),(s=$tt(l))>=0?a=(a<<4)+s:Gt(t,"expected hexadecimal character");t.result+=Htt(a),t.position++}else Gt(t,"unknown escape sequence");r=n=t.position}else qc(l)?(Pf(t,r,n,!0),dF(t,Ai(t,!1,e)),r=n=t.position):t.position===t.lineStart&&CC(t)?Gt(t,"unexpected end of the document within a double quoted scalar"):(t.position++,n=t.position)}Gt(t,"unexpected end of the stream within a double quoted scalar")}o(Xtt,"readDoubleQuotedScalar");function jtt(t,e){var r=!0,n,i,a,s=t.tag,l,u=t.anchor,h,f,d,p,m,g=Object.create(null),y,v,x,b;if(b=t.input.charCodeAt(t.position),b===91)f=93,m=!1,l=[];else if(b===123)f=125,m=!0,l={};else return!1;for(t.anchor!==null&&(t.anchorMap[t.anchor]=l),b=t.input.charCodeAt(++t.position);b!==0;){if(Ai(t,!0,e),b=t.input.charCodeAt(t.position),b===f)return t.position++,t.tag=s,t.anchor=u,t.kind=m?"mapping":"sequence",t.result=l,!0;r?b===44&&Gt(t,"expected the node content, but found ','"):Gt(t,"missed comma between flow collection entries"),v=y=x=null,d=p=!1,b===63&&(h=t.input.charCodeAt(t.position+1),Vs(h)&&(d=p=!0,t.position++,Ai(t,!0,e))),n=t.line,i=t.lineStart,a=t.position,N1(t,e,bC,!1,!0),v=t.tag,y=t.result,Ai(t,!0,e),b=t.input.charCodeAt(t.position),(p||t.line===n)&&b===58&&(d=!0,b=t.input.charCodeAt(++t.position),Ai(t,!0,e),N1(t,e,bC,!1,!0),x=t.result),m?R1(t,l,g,v,y,x,n,i,a):d?l.push(R1(t,null,g,v,y,x,n,i,a)):l.push(y),Ai(t,!0,e),b=t.input.charCodeAt(t.position),b===44?(r=!0,b=t.input.charCodeAt(++t.position)):r=!1}Gt(t,"unexpected end of the stream within a flow collection")}o(jtt,"readFlowCollection");function Ktt(t,e){var r,n,i=aF,a=!1,s=!1,l=e,u=0,h=!1,f,d;if(d=t.input.charCodeAt(t.position),d===124)n=!1;else if(d===62)n=!0;else return!1;for(t.kind="scalar",t.result="";d!==0;)if(d=t.input.charCodeAt(++t.position),d===43||d===45)aF===i?i=d===43?Mye:Btt:Gt(t,"repeat of a chomping mode identifier");else if((f=Utt(d))>=0)f===0?Gt(t,"bad explicit indentation width of a block scalar; it cannot be less than one"):s?Gt(t,"repeat of an indentation width identifier"):(l=e+f-1,s=!0);else break;if(rp(d)){do d=t.input.charCodeAt(++t.position);while(rp(d));if(d===35)do d=t.input.charCodeAt(++t.position);while(!qc(d)&&d!==0)}for(;d!==0;){for(fF(t),t.lineIndent=0,d=t.input.charCodeAt(t.position);(!s||t.lineIndentl&&(l=t.lineIndent),qc(d)){u++;continue}if(t.lineIndente)&&u!==0)Gt(t,"bad indentation of a sequence entry");else if(t.lineIndente)&&(v&&(s=t.line,l=t.lineStart,u=t.position),N1(t,e,wC,!0,i)&&(v?g=t.result:y=t.result),v||(R1(t,d,p,m,g,y,s,l,u),m=g=y=null),Ai(t,!0,-1),b=t.input.charCodeAt(t.position)),(t.line===a||t.lineIndent>e)&&b!==0)Gt(t,"bad indentation of a mapping entry");else if(t.lineIndente?u=1:t.lineIndent===e?u=0:t.lineIndente?u=1:t.lineIndent===e?u=0:t.lineIndent tag; it should be "scalar", not "'+t.kind+'"'),d=0,p=t.implicitTypes.length;d"),t.result!==null&&g.kind!==t.kind&&Gt(t,"unacceptable node kind for !<"+t.tag+'> tag; it should be "'+g.kind+'", not "'+t.kind+'"'),g.resolve(t.result,t.tag)?(t.result=g.construct(t.result,t.tag),t.anchor!==null&&(t.anchorMap[t.anchor]=t.result)):Gt(t,"cannot resolve a node with !<"+t.tag+"> explicit tag")}return t.listener!==null&&t.listener("close",t),t.tag!==null||t.anchor!==null||f}o(N1,"composeNode");function trt(t){var e=t.position,r,n,i,a=!1,s;for(t.version=null,t.checkLineBreaks=t.legacy,t.tagMap=Object.create(null),t.anchorMap=Object.create(null);(s=t.input.charCodeAt(t.position))!==0&&(Ai(t,!0,-1),s=t.input.charCodeAt(t.position),!(t.lineIndent>0||s!==37));){for(a=!0,s=t.input.charCodeAt(++t.position),r=t.position;s!==0&&!Vs(s);)s=t.input.charCodeAt(++t.position);for(n=t.input.slice(r,t.position),i=[],n.length<1&&Gt(t,"directive name must not be less than one character in length");s!==0;){for(;rp(s);)s=t.input.charCodeAt(++t.position);if(s===35){do s=t.input.charCodeAt(++t.position);while(s!==0&&!qc(s));break}if(qc(s))break;for(r=t.position;s!==0&&!Vs(s);)s=t.input.charCodeAt(++t.position);i.push(t.input.slice(r,t.position))}s!==0&&fF(t),Bf.call(Pye,n)?Pye[n](t,n,i):TC(t,'unknown document directive "'+n+'"')}if(Ai(t,!0,-1),t.lineIndent===0&&t.input.charCodeAt(t.position)===45&&t.input.charCodeAt(t.position+1)===45&&t.input.charCodeAt(t.position+2)===45?(t.position+=3,Ai(t,!0,-1)):a&&Gt(t,"directives end mark is expected"),N1(t,t.lineIndent-1,wC,!1,!0),Ai(t,!0,-1),t.checkLineBreaks&&ztt.test(t.input.slice(e,t.position))&&TC(t,"non-ASCII line breaks are interpreted as content"),t.documents.push(t.result),t.position===t.lineStart&&CC(t)){t.input.charCodeAt(t.position)===46&&(t.position+=3,Ai(t,!0,-1));return}if(t.position"u"&&(r=e,e=null);var n=sve(t,r);if(typeof e!="function")return n;for(var i=0,a=n.length;i=55296&&r<=56319&&e+1=56320&&n<=57343)?(r-55296)*1024+n-56320+65536:r}o(kb,"codePointAt");function mve(t){var e=/^\n* /;return e.test(t)}o(mve,"needIndentIndicator");var gve=1,cF=2,yve=3,vve=4,L1=5;function Rrt(t,e,r,n,i,a,s,l){var u,h=0,f=null,d=!1,p=!1,m=n!==-1,g=-1,y=Lrt(kb(t,0))&&Drt(kb(t,t.length-1));if(e||s)for(u=0;u=65536?u+=2:u++){if(h=kb(t,u),!Ab(h))return L1;y=y&&$ye(h,f,l),f=h}else{for(u=0;u=65536?u+=2:u++){if(h=kb(t,u),h===Cb)d=!0,m&&(p=p||u-g-1>n&&t[g+1]!==" ",g=u);else if(!Ab(h))return L1;y=y&&$ye(h,f,l),f=h}p=p||m&&u-g-1>n&&t[g+1]!==" "}return!d&&!p?y&&!s&&!i(t)?gve:a===Sb?L1:cF:r>9&&mve(t)?L1:s?a===Sb?L1:cF:p?vve:yve}o(Rrt,"chooseScalarStyle");function Nrt(t,e,r,n,i){t.dump=function(){if(e.length===0)return t.quotingType===Sb?'""':"''";if(!t.noCompatMode&&(Trt.indexOf(e)!==-1||krt.test(e)))return t.quotingType===Sb?'"'+e+'"':"'"+e+"'";var a=t.indent*Math.max(1,r),s=t.lineWidth===-1?-1:Math.max(Math.min(t.lineWidth,40),t.lineWidth-a),l=n||t.flowLevel>-1&&r>=t.flowLevel;function u(h){return _rt(t,h)}switch(o(u,"testAmbiguity"),Rrt(e,l,t.indent,s,u,t.quotingType,t.forceQuotes&&!n,i)){case gve:return e;case cF:return"'"+e.replace(/'/g,"''")+"'";case yve:return"|"+Vye(e,t.indent)+Uye(zye(e,a));case vve:return">"+Vye(e,t.indent)+Uye(zye(Mrt(e,s),a));case L1:return'"'+Irt(e)+'"';default:throw new $s("impossible error: invalid scalar style")}}()}o(Nrt,"writeScalar");function Vye(t,e){var r=mve(t)?String(e):"",n=t[t.length-1]===` +`,i=n&&(t[t.length-2]===` +`||t===` +`),a=i?"+":n?"":"-";return r+a+` +`}o(Vye,"blockHeader");function Uye(t){return t[t.length-1]===` +`?t.slice(0,-1):t}o(Uye,"dropEndingNewline");function Mrt(t,e){for(var r=/(\n+)([^\n]*)/g,n=function(){var h=t.indexOf(` +`);return h=h!==-1?h:t.length,r.lastIndex=h,Hye(t.slice(0,h),e)}(),i=t[0]===` +`||t[0]===" ",a,s;s=r.exec(t);){var l=s[1],u=s[2];a=u[0]===" ",n+=l+(!i&&!a&&u!==""?` +`:"")+Hye(u,e),i=a}return n}o(Mrt,"foldString");function Hye(t,e){if(t===""||t[0]===" ")return t;for(var r=/ [^ ]/g,n,i=0,a,s=0,l=0,u="";n=r.exec(t);)l=n.index,l-i>e&&(a=s>i?s:l,u+=` +`+t.slice(i,a),i=a+1),s=l;return u+=` +`,t.length-i>e&&s>i?u+=t.slice(i,s)+` +`+t.slice(s+1):u+=t.slice(i),u.slice(1)}o(Hye,"foldLine");function Irt(t){for(var e="",r=0,n,i=0;i=65536?i+=2:i++)r=kb(t,i),n=Ua[r],!n&&Ab(r)?(e+=t[i],r>=65536&&(e+=t[i+1])):e+=n||Crt(r);return e}o(Irt,"escapeString");function Ort(t,e,r){var n="",i=t.tag,a,s,l;for(a=0,s=r.length;a"u"&&rh(t,e,null,!1,!1))&&(n!==""&&(n+=","+(t.condenseFlow?"":" ")),n+=t.dump);t.tag=i,t.dump="["+n+"]"}o(Ort,"writeFlowSequence");function Yye(t,e,r,n){var i="",a=t.tag,s,l,u;for(s=0,l=r.length;s"u"&&rh(t,e+1,null,!0,!0,!1,!0))&&((!n||i!=="")&&(i+=lF(t,e)),t.dump&&Cb===t.dump.charCodeAt(0)?i+="-":i+="- ",i+=t.dump);t.tag=a,t.dump=i||"[]"}o(Yye,"writeBlockSequence");function Prt(t,e,r){var n="",i=t.tag,a=Object.keys(r),s,l,u,h,f;for(s=0,l=a.length;s1024&&(f+="? "),f+=t.dump+(t.condenseFlow?'"':"")+":"+(t.condenseFlow?"":" "),rh(t,e,h,!1,!1)&&(f+=t.dump,n+=f));t.tag=i,t.dump="{"+n+"}"}o(Prt,"writeFlowMapping");function Brt(t,e,r,n){var i="",a=t.tag,s=Object.keys(r),l,u,h,f,d,p;if(t.sortKeys===!0)s.sort();else if(typeof t.sortKeys=="function")s.sort(t.sortKeys);else if(t.sortKeys)throw new $s("sortKeys must be a boolean or a function");for(l=0,u=s.length;l1024,d&&(t.dump&&Cb===t.dump.charCodeAt(0)?p+="?":p+="? "),p+=t.dump,d&&(p+=lF(t,e)),rh(t,e+1,f,!0,d)&&(t.dump&&Cb===t.dump.charCodeAt(0)?p+=":":p+=": ",p+=t.dump,i+=p));t.tag=a,t.dump=i||"{}"}o(Brt,"writeBlockMapping");function Wye(t,e,r){var n,i,a,s,l,u;for(i=r?t.explicitTypes:t.implicitTypes,a=0,s=i.length;a tag resolver accepts not "'+u+'" style');t.dump=n}return!0}return!1}o(Wye,"detectType");function rh(t,e,r,n,i,a,s){t.tag=null,t.dump=r,Wye(t,r,!1)||Wye(t,r,!0);var l=lve.call(t.dump),u=n,h;n&&(n=t.flowLevel<0||t.flowLevel>e);var f=l==="[object Object]"||l==="[object Array]",d,p;if(f&&(d=t.duplicates.indexOf(r),p=d!==-1),(t.tag!==null&&t.tag!=="?"||p||t.indent!==2&&e>0)&&(i=!1),p&&t.usedDuplicates[d])t.dump="*ref_"+d;else{if(f&&p&&!t.usedDuplicates[d]&&(t.usedDuplicates[d]=!0),l==="[object Object]")n&&Object.keys(t.dump).length!==0?(Brt(t,e,t.dump,i),p&&(t.dump="&ref_"+d+t.dump)):(Prt(t,e,t.dump),p&&(t.dump="&ref_"+d+" "+t.dump));else if(l==="[object Array]")n&&t.dump.length!==0?(t.noArrayIndent&&!s&&e>0?Yye(t,e-1,t.dump,i):Yye(t,e,t.dump,i),p&&(t.dump="&ref_"+d+t.dump)):(Ort(t,e,t.dump),p&&(t.dump="&ref_"+d+" "+t.dump));else if(l==="[object String]")t.tag!=="?"&&Nrt(t,t.dump,e,a,u);else{if(l==="[object Undefined]")return!1;if(t.skipInvalid)return!1;throw new $s("unacceptable kind of an object to dump "+l)}t.tag!==null&&t.tag!=="?"&&(h=encodeURI(t.tag[0]==="!"?t.tag.slice(1):t.tag).replace(/!/g,"%21"),t.tag[0]==="!"?h="!"+h:h.slice(0,18)==="tag:yaml.org,2002:"?h="!!"+h.slice(18):h="!<"+h+">",t.dump=h+" "+t.dump)}return!0}o(rh,"writeNode");function Frt(t,e){var r=[],n=[],i,a;for(uF(t,r,n),i=0,a=n.length;it.replace(/\r\n?/g,` +`).replace(/<(\w+)([^>]*)>/g,(e,r,n)=>"<"+r+n.replace(/="([^"]*)"/g,"='$1'")+">"),"cleanupText"),Hrt=o(t=>{let{text:e,metadata:r}=wve(t),{displayMode:n,title:i,config:a={}}=r;return n&&(a.gantt||(a.gantt={}),a.gantt.displayMode=n),{title:i,config:a,text:e}},"processFrontmatter"),Yrt=o(t=>{let e=Lt.detectInit(t)??{},r=Lt.detectDirective(t,"wrap");return Array.isArray(r)?e.wrap=r.some(({type:n})=>n==="wrap"):r?.type==="wrap"&&(e.wrap=!0),{text:EX(t),directive:e}},"processDirectives");function gF(t){let e=Urt(t),r=Hrt(e),n=Yrt(r.text),i=Ts(r.config,n.directive);return t=Rye(n.text),{code:t,title:r.title,config:i}}o(gF,"preprocessDiagram");Z7();Kb();xr();function Tve(t){let e=new TextEncoder().encode(t),r=Array.from(e,n=>String.fromCodePoint(n)).join("");return btoa(r)}o(Tve,"toBase64");var Wrt=5e4,qrt="graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa",Xrt="sandbox",jrt="loose",Krt="http://www.w3.org/2000/svg",Qrt="http://www.w3.org/1999/xlink",Zrt="http://www.w3.org/1999/xhtml",Jrt="100%",ent="100%",tnt="border:0;margin:0;",rnt="margin:0",nnt="allow-top-navigation-by-user-activation allow-popups",int='The "iframe" tag is not supported by your browser.',ant=["foreignobject"],snt=["dominant-baseline"];function Ave(t){let e=gF(t);return Q1(),jz(e.config??{}),e}o(Ave,"processAndSetConfigs");async function ont(t,e){k1();try{let{code:r,config:n}=Ave(t);return{diagramType:(await _ve(r)).type,config:n}}catch(r){if(e?.suppressErrors)return!1;throw r}}o(ont,"parse");var kve=o((t,e,r=[])=>` +.${t} ${e} { ${r.join(" !important; ")} !important; }`,"cssImportantStyles"),lnt=o((t,e=new Map)=>{let r="";if(t.themeCSS!==void 0&&(r+=` +${t.themeCSS}`),t.fontFamily!==void 0&&(r+=` +:root { --mermaid-font-family: ${t.fontFamily}}`),t.altFontFamily!==void 0&&(r+=` +:root { --mermaid-alt-font-family: ${t.altFontFamily}}`),e instanceof Map){let s=t.htmlLabels??t.flowchart?.htmlLabels?["> *","span"]:["rect","polygon","ellipse","circle","path"];e.forEach(l=>{Qt(l.styles)||s.forEach(u=>{r+=kve(l.id,u,l.styles)}),Qt(l.textStyles)||(r+=kve(l.id,"tspan",(l?.textStyles||[]).map(u=>u.replace("color","fill"))))})}return r},"createCssStyles"),cnt=o((t,e,r,n)=>{let i=lnt(t,r),a=D$(e,i,t.themeVariables);return xC(Cye(`${n}{${a}}`),Sye)},"createUserStyles"),unt=o((t="",e,r)=>{let n=t;return!r&&!e&&(n=n.replace(/marker-end="url\([\d+./:=?A-Za-z-]*?#/g,'marker-end="url(#')),n=to(n),n=n.replace(/
    /g,"
    "),n},"cleanUpSvgCode"),hnt=o((t="",e)=>{let r=e?.viewBox?.baseVal?.height?e.viewBox.baseVal.height+"px":ent,n=Tve(`${t}`);return``},"putIntoIFrame"),Eve=o((t,e,r,n,i)=>{let a=t.append("div");a.attr("id",r),n&&a.attr("style",n);let s=a.append("svg").attr("id",e).attr("width","100%").attr("xmlns",Krt);return i&&s.attr("xmlns:xlink",i),s.append("g"),t},"appendDivSvgG");function Cve(t,e){return t.append("iframe").attr("id",e).attr("style","width: 100%; height: 100%;").attr("sandbox","")}o(Cve,"sandboxedIframe");var fnt=o((t,e,r,n)=>{t.getElementById(e)?.remove(),t.getElementById(r)?.remove(),t.getElementById(n)?.remove()},"removeExistingElements"),dnt=o(async function(t,e,r){k1();let n=Ave(e);e=n.code;let i=Or();V.debug(i),e.length>(i?.maxTextSize??Wrt)&&(e=qrt);let a="#"+t,s="i"+t,l="#"+s,u="d"+t,h="#"+u,f=o(()=>{let I=$e(p?l:h).node();I&&"remove"in I&&I.remove()},"removeTempElements"),d=$e("body"),p=i.securityLevel===Xrt,m=i.securityLevel===jrt,g=i.fontFamily;if(r!==void 0){if(r&&(r.innerHTML=""),p){let k=Cve($e(r),s);d=$e(k.nodes()[0].contentDocument.body),d.node().style.margin=0}else d=$e(r);Eve(d,t,u,`font-family: ${g}`,Qrt)}else{if(fnt(document,t,u,s),p){let k=Cve($e("body"),s);d=$e(k.nodes()[0].contentDocument.body),d.node().style.margin=0}else d=$e("body");Eve(d,t,u)}let y,v;try{y=await _1.fromText(e,{title:n.title})}catch(k){if(i.suppressErrorRendering)throw f(),k;y=await _1.fromText("error"),v=k}let x=d.select(h).node(),b=y.type,w=x.firstChild,S=w.firstChild,T=y.renderer.getClasses?.(e,y),E=cnt(i,b,T,a),_=document.createElement("style");_.innerHTML=E,w.insertBefore(_,S);try{await y.renderer.draw(e,t,fx,y)}catch(k){throw i.suppressErrorRendering?f():Ede.draw(e,t,fx),k}let A=d.select(`${h} svg`),L=y.db.getAccTitle?.(),M=y.db.getAccDescription?.();mnt(b,A,L,M),d.select(`[id="${t}"]`).selectAll("foreignobject > *").attr("xmlns",Zrt);let N=d.select(h).node().innerHTML;if(V.debug("config.arrowMarkerAbsolute",i.arrowMarkerAbsolute),N=unt(N,p,yr(i.arrowMarkerAbsolute)),p){let k=d.select(h+" svg").node();N=hnt(N,k)}else m||(N=Sve.default.sanitize(N,{ADD_TAGS:ant,ADD_ATTR:snt}));if(Dye(),v)throw v;return f(),{diagramType:b,svg:N,bindFunctions:y.db.bindFunctions}},"render");function pnt(t={}){let e=On({},t);e?.fontFamily&&!e.themeVariables?.fontFamily&&(e.themeVariables||(e.themeVariables={}),e.themeVariables.fontFamily=e.fontFamily),Wz(e),e?.theme&&e.theme in Co?e.themeVariables=Co[e.theme].getThemeVariables(e.themeVariables):e&&(e.themeVariables=Co.default.getThemeVariables(e.themeVariables));let r=typeof e=="object"?n7(e):i7();$1(r.logLevel),k1()}o(pnt,"initialize");var _ve=o((t,e={})=>{let{code:r}=gF(t);return _1.fromText(r,e)},"getDiagramFromText");function mnt(t,e,r,n){Aye(e,t),_ye(e,r,n,e.attr("id"))}o(mnt,"addA11yInfo");var Ff=Object.freeze({render:dnt,parse:ont,getDiagramFromText:_ve,initialize:pnt,getConfig:Or,setConfig:Zb,getSiteConfig:i7,updateSiteConfig:qz,reset:o(()=>{Q1()},"reset"),globalReset:o(()=>{Q1(uh)},"globalReset"),defaultConfig:uh});$1(Or().logLevel);Q1(Or());oT();xr();var gnt=o((t,e,r)=>{V.warn(t),r9(t)?(r&&r(t.str,t.hash),e.push({...t,message:t.str,error:t})):(r&&r(t),t instanceof Error&&e.push({str:t.message,message:t.message,hash:t.name,error:t}))},"handleError"),Lve=o(async function(t={querySelector:".mermaid"}){try{await ynt(t)}catch(e){if(r9(e)&&V.error(e.str),nh.parseError&&nh.parseError(e),!t.suppressErrors)throw V.error("Use the suppressErrors option to suppress these errors"),e}},"run"),ynt=o(async function({postRenderCallback:t,querySelector:e,nodes:r}={querySelector:".mermaid"}){let n=Ff.getConfig();V.debug(`${t?"":"No "}Callback function found`);let i;if(r)i=r;else if(e)i=document.querySelectorAll(e);else throw new Error("Nodes and querySelector are both undefined");V.debug(`Found ${i.length} diagrams`),n?.startOnLoad!==void 0&&(V.debug("Start On Load: "+n?.startOnLoad),Ff.updateSiteConfig({startOnLoad:n?.startOnLoad}));let a=new Lt.InitIDGenerator(n.deterministicIds,n.deterministicIDSeed),s,l=[];for(let u of Array.from(i)){V.info("Rendering diagram: "+u.id);if(u.getAttribute("data-processed"))continue;u.setAttribute("data-processed","true");let h=`mermaid-${a.next()}`;s=u.innerHTML,s=Gb(Lt.entityDecode(s)).trim().replace(//gi,"
    ");let f=Lt.detectInit(s);f&&V.debug("Detected early reinit: ",f);try{let{svg:d,bindFunctions:p}=await Mve(h,s,u);u.innerHTML=d,t&&await t(h),p&&p(u)}catch(d){gnt(d,l,nh.parseError)}}if(l.length>0)throw l[0]},"runThrowsErrors"),Dve=o(function(t){Ff.initialize(t)},"initialize"),vnt=o(async function(t,e,r){V.warn("mermaid.init is deprecated. Please use run instead."),t&&Dve(t);let n={postRenderCallback:r,querySelector:".mermaid"};typeof e=="string"?n.querySelector=e:e&&(e instanceof HTMLElement?n.nodes=[e]:n.nodes=e),await Lve(n)},"init"),xnt=o(async(t,{lazyLoad:e=!0}={})=>{k1(),Ub(...t),e===!1&&await lye()},"registerExternalDiagrams"),Rve=o(function(){if(nh.startOnLoad){let{startOnLoad:t}=Ff.getConfig();t&&nh.run().catch(e=>V.error("Mermaid failed to initialize",e))}},"contentLoaded");if(typeof document<"u"){window.addEventListener("load",Rve,!1)}var bnt=o(function(t){nh.parseError=t},"setParseErrorHandler"),SC=[],yF=!1,Nve=o(async()=>{if(!yF){for(yF=!0;SC.length>0;){let t=SC.shift();if(t)try{await t()}catch(e){V.error("Error executing queue",e)}}yF=!1}},"executeQueue"),wnt=o(async(t,e)=>new Promise((r,n)=>{let i=o(()=>new Promise((a,s)=>{Ff.parse(t,e).then(l=>{a(l),r(l)},l=>{V.error("Error parsing",l),nh.parseError?.(l),s(l),n(l)})}),"performCall");SC.push(i),Nve().catch(n)}),"parse"),Mve=o((t,e,r)=>new Promise((n,i)=>{let a=o(()=>new Promise((s,l)=>{Ff.render(t,e,r).then(u=>{s(u),n(u)},u=>{V.error("Error parsing",u),nh.parseError?.(u),l(u),i(u)})}),"performCall");SC.push(a),Nve().catch(i)}),"render"),nh={startOnLoad:!0,mermaidAPI:Ff,parse:wnt,render:Mve,init:vnt,run:Lve,registerExternalDiagrams:xnt,registerLayoutLoaders:gD,initialize:Dve,parseError:void 0,contentLoaded:Rve,setParseErrorHandler:bnt,detectType:lp,registerIconPacks:Fb},Tnt=nh;return $ve(knt);})(); +/*! Check if previously processed */ +/*! + * Wait for document loaded before starting the execution + */ +/*! Bundled license information: + +dompurify/dist/purify.js: + (*! @license DOMPurify 3.1.6 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.1.6/LICENSE *) + +lodash-es/lodash.js: + (** + * @license + * Lodash (Custom Build) + * Build: `lodash modularize exports="es" -o ./` + * Copyright OpenJS Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + *) + +cytoscape/dist/cytoscape.esm.mjs: + (*! + Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable + Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com) + Licensed under The MIT License (http://opensource.org/licenses/MIT) + *) + (*! + Event object based on jQuery events, MIT license + + https://jquery.org/license/ + https://tldrlegal.com/license/mit-license + https://github.com/jquery/jquery/blob/master/src/event.js + *) + (*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License *) + (*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License *) + +js-yaml/dist/js-yaml.mjs: + (*! js-yaml 4.1.0 https://github.com/nodeca/js-yaml @license MIT *) +*/ +globalThis.mermaid = globalThis.__esbuild_esm_mermaid.default; diff --git a/assets/external/valerio.nu/maps/geolocation.jpg b/assets/external/valerio.nu/maps/geolocation.jpg new file mode 100644 index 0000000000..c45f465ae1 Binary files /dev/null and b/assets/external/valerio.nu/maps/geolocation.jpg differ diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 0000000000..1cf13b9f9d Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.203fd0bc.min.js b/assets/javascripts/bundle.203fd0bc.min.js new file mode 100644 index 0000000000..716ce89624 --- /dev/null +++ b/assets/javascripts/bundle.203fd0bc.min.js @@ -0,0 +1,3 @@ +"use strict";(()=>{var Zi=Object.create;var _r=Object.defineProperty;var ea=Object.getOwnPropertyDescriptor;var ta=Object.getOwnPropertyNames,Gt=Object.getOwnPropertySymbols,ra=Object.getPrototypeOf,Ar=Object.prototype.hasOwnProperty,bo=Object.prototype.propertyIsEnumerable;var ho=(e,t,r)=>t in e?_r(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,R=(e,t)=>{for(var r in t||(t={}))Ar.call(t,r)&&ho(e,r,t[r]);if(Gt)for(var r of Gt(t))bo.call(t,r)&&ho(e,r,t[r]);return e};var vo=(e,t)=>{var r={};for(var o in e)Ar.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Gt)for(var o of Gt(e))t.indexOf(o)<0&&bo.call(e,o)&&(r[o]=e[o]);return r};var Cr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var oa=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of ta(t))!Ar.call(e,n)&&n!==r&&_r(e,n,{get:()=>t[n],enumerable:!(o=ea(t,n))||o.enumerable});return e};var Rt=(e,t,r)=>(r=e!=null?Zi(ra(e)):{},oa(t||!e||!e.__esModule?_r(r,"default",{value:e,enumerable:!0}):r,e));var go=(e,t,r)=>new Promise((o,n)=>{var i=c=>{try{a(r.next(c))}catch(p){n(p)}},s=c=>{try{a(r.throw(c))}catch(p){n(p)}},a=c=>c.done?o(c.value):Promise.resolve(c.value).then(i,s);a((r=r.apply(e,t)).next())});var xo=Cr((kr,yo)=>{(function(e,t){typeof kr=="object"&&typeof yo!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(kr,function(){"use strict";function e(r){var o=!0,n=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(k){return!!(k&&k!==document&&k.nodeName!=="HTML"&&k.nodeName!=="BODY"&&"classList"in k&&"contains"in k.classList)}function c(k){var ut=k.type,je=k.tagName;return!!(je==="INPUT"&&s[ut]&&!k.readOnly||je==="TEXTAREA"&&!k.readOnly||k.isContentEditable)}function p(k){k.classList.contains("focus-visible")||(k.classList.add("focus-visible"),k.setAttribute("data-focus-visible-added",""))}function l(k){k.hasAttribute("data-focus-visible-added")&&(k.classList.remove("focus-visible"),k.removeAttribute("data-focus-visible-added"))}function f(k){k.metaKey||k.altKey||k.ctrlKey||(a(r.activeElement)&&p(r.activeElement),o=!0)}function u(k){o=!1}function d(k){a(k.target)&&(o||c(k.target))&&p(k.target)}function v(k){a(k.target)&&(k.target.classList.contains("focus-visible")||k.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(k.target))}function S(k){document.visibilityState==="hidden"&&(n&&(o=!0),X())}function X(){document.addEventListener("mousemove",ee),document.addEventListener("mousedown",ee),document.addEventListener("mouseup",ee),document.addEventListener("pointermove",ee),document.addEventListener("pointerdown",ee),document.addEventListener("pointerup",ee),document.addEventListener("touchmove",ee),document.addEventListener("touchstart",ee),document.addEventListener("touchend",ee)}function re(){document.removeEventListener("mousemove",ee),document.removeEventListener("mousedown",ee),document.removeEventListener("mouseup",ee),document.removeEventListener("pointermove",ee),document.removeEventListener("pointerdown",ee),document.removeEventListener("pointerup",ee),document.removeEventListener("touchmove",ee),document.removeEventListener("touchstart",ee),document.removeEventListener("touchend",ee)}function ee(k){k.target.nodeName&&k.target.nodeName.toLowerCase()==="html"||(o=!1,re())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",S,!0),X(),r.addEventListener("focus",d,!0),r.addEventListener("blur",v,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var ro=Cr((Uy,Pn)=>{"use strict";var qa=/["'&<>]/;Pn.exports=Ka;function Ka(e){var t=""+e,r=qa.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i{(function(t,r){typeof zt=="object"&&typeof io=="object"?io.exports=r():typeof define=="function"&&define.amd?define([],r):typeof zt=="object"?zt.ClipboardJS=r():t.ClipboardJS=r()})(zt,function(){return function(){var e={686:function(o,n,i){"use strict";i.d(n,{default:function(){return Xi}});var s=i(279),a=i.n(s),c=i(370),p=i.n(c),l=i(817),f=i.n(l);function u(q){try{return document.execCommand(q)}catch(C){return!1}}var d=function(C){var _=f()(C);return u("cut"),_},v=d;function S(q){var C=document.documentElement.getAttribute("dir")==="rtl",_=document.createElement("textarea");_.style.fontSize="12pt",_.style.border="0",_.style.padding="0",_.style.margin="0",_.style.position="absolute",_.style[C?"right":"left"]="-9999px";var W=window.pageYOffset||document.documentElement.scrollTop;return _.style.top="".concat(W,"px"),_.setAttribute("readonly",""),_.value=q,_}var X=function(C,_){var W=S(C);_.container.appendChild(W);var N=f()(W);return u("copy"),W.remove(),N},re=function(C){var _=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},W="";return typeof C=="string"?W=X(C,_):C instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(C==null?void 0:C.type)?W=X(C.value,_):(W=f()(C),u("copy")),W},ee=re;function k(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?k=function(_){return typeof _}:k=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},k(q)}var ut=function(){var C=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},_=C.action,W=_===void 0?"copy":_,N=C.container,G=C.target,De=C.text;if(W!=="copy"&&W!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(G!==void 0)if(G&&k(G)==="object"&&G.nodeType===1){if(W==="copy"&&G.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(W==="cut"&&(G.hasAttribute("readonly")||G.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(De)return ee(De,{container:N});if(G)return W==="cut"?v(G):ee(G,{container:N})},je=ut;function P(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?P=function(_){return typeof _}:P=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},P(q)}function se(q,C){if(!(q instanceof C))throw new TypeError("Cannot call a class as a function")}function ce(q,C){for(var _=0;_0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof N.action=="function"?N.action:this.defaultAction,this.target=typeof N.target=="function"?N.target:this.defaultTarget,this.text=typeof N.text=="function"?N.text:this.defaultText,this.container=P(N.container)==="object"?N.container:document.body}},{key:"listenClick",value:function(N){var G=this;this.listener=p()(N,"click",function(De){return G.onClick(De)})}},{key:"onClick",value:function(N){var G=N.delegateTarget||N.currentTarget,De=this.action(G)||"copy",Bt=je({action:De,container:this.container,target:this.target(G),text:this.text(G)});this.emit(Bt?"success":"error",{action:De,text:Bt,trigger:G,clearSelection:function(){G&&G.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(N){return Mr("action",N)}},{key:"defaultTarget",value:function(N){var G=Mr("target",N);if(G)return document.querySelector(G)}},{key:"defaultText",value:function(N){return Mr("text",N)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(N){var G=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return ee(N,G)}},{key:"cut",value:function(N){return v(N)}},{key:"isSupported",value:function(){var N=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],G=typeof N=="string"?[N]:N,De=!!document.queryCommandSupported;return G.forEach(function(Bt){De=De&&!!document.queryCommandSupported(Bt)}),De}}]),_}(a()),Xi=Ji},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==n;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}o.exports=s},438:function(o,n,i){var s=i(828);function a(l,f,u,d,v){var S=p.apply(this,arguments);return l.addEventListener(u,S,v),{destroy:function(){l.removeEventListener(u,S,v)}}}function c(l,f,u,d,v){return typeof l.addEventListener=="function"?a.apply(null,arguments):typeof u=="function"?a.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(S){return a(S,f,u,d,v)}))}function p(l,f,u,d){return function(v){v.delegateTarget=s(v.target,f),v.delegateTarget&&d.call(l,v)}}o.exports=c},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}},370:function(o,n,i){var s=i(879),a=i(438);function c(u,d,v){if(!u&&!d&&!v)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(v))throw new TypeError("Third argument must be a Function");if(s.node(u))return p(u,d,v);if(s.nodeList(u))return l(u,d,v);if(s.string(u))return f(u,d,v);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,d,v){return u.addEventListener(d,v),{destroy:function(){u.removeEventListener(d,v)}}}function l(u,d,v){return Array.prototype.forEach.call(u,function(S){S.addEventListener(d,v)}),{destroy:function(){Array.prototype.forEach.call(u,function(S){S.removeEventListener(d,v)})}}}function f(u,d,v){return a(document.body,u,d,v)}o.exports=c},817:function(o){function n(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),s=c.toString()}return s}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function p(){c.off(i,p),s.apply(a,arguments)}return p._=s,this.on(i,p,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=a.length;for(c;c0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function K(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function B(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||c(d,S)})},v&&(n[d]=v(n[d])))}function c(d,v){try{p(o[d](v))}catch(S){u(i[0][3],S)}}function p(d){d.value instanceof dt?Promise.resolve(d.value.v).then(l,f):u(i[0][2],d)}function l(d){c("next",d)}function f(d){c("throw",d)}function u(d,v){d(v),i.shift(),i.length&&c(i[0][0],i[0][1])}}function To(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Oe=="function"?Oe(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),n(a,c,s.done,s.value)})}}function n(i,s,a,c){Promise.resolve(c).then(function(p){i({value:p,done:a})},s)}}function I(e){return typeof e=="function"}function yt(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var Xt=yt(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Ze(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var qe=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Oe(s),c=a.next();!c.done;c=a.next()){var p=c.value;p.remove(this)}}catch(S){t={error:S}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var l=this.initialTeardown;if(I(l))try{l()}catch(S){i=S instanceof Xt?S.errors:[S]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=Oe(f),d=u.next();!d.done;d=u.next()){var v=d.value;try{So(v)}catch(S){i=i!=null?i:[],S instanceof Xt?i=B(B([],K(i)),K(S.errors)):i.push(S)}}}catch(S){o={error:S}}finally{try{d&&!d.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new Xt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)So(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ze(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ze(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var $r=qe.EMPTY;function Zt(e){return e instanceof qe||e&&"closed"in e&&I(e.remove)&&I(e.add)&&I(e.unsubscribe)}function So(e){I(e)?e():e.unsubscribe()}var We={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var xt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,s=n.isStopped,a=n.observers;return i||s?$r:(this.currentObservers=null,a.push(r),new qe(function(){o.currentObservers=null,Ze(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,s=o.isStopped;n?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,o){return new Ho(r,o)},t}(F);var Ho=function(e){ie(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:$r},t}(T);var jr=function(e){ie(t,e);function t(r){var o=e.call(this)||this;return o._value=r,o}return Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(r){var o=e.prototype._subscribe.call(this,r);return!o.closed&&r.next(this._value),o},t.prototype.getValue=function(){var r=this,o=r.hasError,n=r.thrownError,i=r._value;if(o)throw n;return this._throwIfClosed(),i},t.prototype.next=function(r){e.prototype.next.call(this,this._value=r)},t}(T);var It={now:function(){return(It.delegate||Date).now()},delegate:void 0};var Ft=function(e){ie(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=It);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,s=o._infiniteTimeWindow,a=o._timestampProvider,c=o._windowTime;n||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,s=n._buffer,a=s.slice(),c=0;c0?e.prototype.schedule.call(this,r,o):(this.delay=o,this.state=r,this.scheduler.flush(this),this)},t.prototype.execute=function(r,o){return o>0||this.closed?e.prototype.execute.call(this,r,o):this._execute(r,o)},t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.flush(this),0)},t}(St);var Po=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t}(Ot);var Wr=new Po(Ro);var Io=function(e){ie(t,e);function t(r,o){var n=e.call(this,r,o)||this;return n.scheduler=r,n.work=o,n}return t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!==null&&n>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=Tt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var s=r.actions;o!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==o&&(Tt.cancelAnimationFrame(o),r._scheduled=void 0)},t}(St);var Fo=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o=this._scheduled;this._scheduled=void 0;var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t}(Ot);var ye=new Fo(Io);var y=new F(function(e){return e.complete()});function rr(e){return e&&I(e.schedule)}function Vr(e){return e[e.length-1]}function pt(e){return I(Vr(e))?e.pop():void 0}function Fe(e){return rr(Vr(e))?e.pop():void 0}function or(e,t){return typeof Vr(e)=="number"?e.pop():t}var Lt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function nr(e){return I(e==null?void 0:e.then)}function ir(e){return I(e[wt])}function ar(e){return Symbol.asyncIterator&&I(e==null?void 0:e[Symbol.asyncIterator])}function sr(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function fa(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var cr=fa();function pr(e){return I(e==null?void 0:e[cr])}function lr(e){return wo(this,arguments,function(){var r,o,n,i;return Jt(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,dt(r.read())];case 3:return o=s.sent(),n=o.value,i=o.done,i?[4,dt(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,dt(n)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function mr(e){return I(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(ir(e))return ua(e);if(Lt(e))return da(e);if(nr(e))return ha(e);if(ar(e))return jo(e);if(pr(e))return ba(e);if(mr(e))return va(e)}throw sr(e)}function ua(e){return new F(function(t){var r=e[wt]();if(I(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function da(e){return new F(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?g(function(n,i){return e(n,i,o)}):be,Ee(1),r?Qe(t):tn(function(){return new ur}))}}function Yr(e){return e<=0?function(){return y}:E(function(t,r){var o=[];t.subscribe(w(r,function(n){o.push(n),e=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new T}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(p){var l,f,u,d=0,v=!1,S=!1,X=function(){f==null||f.unsubscribe(),f=void 0},re=function(){X(),l=u=void 0,v=S=!1},ee=function(){var k=l;re(),k==null||k.unsubscribe()};return E(function(k,ut){d++,!S&&!v&&X();var je=u=u!=null?u:r();ut.add(function(){d--,d===0&&!S&&!v&&(f=Br(ee,c))}),je.subscribe(ut),!l&&d>0&&(l=new bt({next:function(P){return je.next(P)},error:function(P){S=!0,X(),f=Br(re,n,P),je.error(P)},complete:function(){v=!0,X(),f=Br(re,s),je.complete()}}),U(k).subscribe(l))})(p)}}function Br(e,t){for(var r=[],o=2;oe.next(document)),e}function M(e,t=document){return Array.from(t.querySelectorAll(e))}function j(e,t=document){let r=ue(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ue(e,t=document){return t.querySelector(e)||void 0}function Ne(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var Pa=L(h(document.body,"focusin"),h(document.body,"focusout")).pipe(Ae(1),Q(void 0),m(()=>Ne()||document.body),Z(1));function Ye(e){return Pa.pipe(m(t=>e.contains(t)),Y())}function it(e,t){return H(()=>L(h(e,"mouseenter").pipe(m(()=>!0)),h(e,"mouseleave").pipe(m(()=>!1))).pipe(t?Ut(r=>He(+!r*t)):be,Q(e.matches(":hover"))))}function sn(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)sn(e,r)}function x(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)sn(o,n);return o}function br(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function At(e){let t=x("script",{src:e});return H(()=>(document.head.appendChild(t),L(h(t,"load"),h(t,"error").pipe(b(()=>Nr(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),A(()=>document.head.removeChild(t)),Ee(1))))}var cn=new T,Ia=H(()=>typeof ResizeObserver=="undefined"?At("https://unpkg.com/resize-observer-polyfill"):$(void 0)).pipe(m(()=>new ResizeObserver(e=>e.forEach(t=>cn.next(t)))),b(e=>L(tt,$(e)).pipe(A(()=>e.disconnect()))),Z(1));function de(e){return{width:e.offsetWidth,height:e.offsetHeight}}function Le(e){let t=e;for(;t.clientWidth===0&&t.parentElement;)t=t.parentElement;return Ia.pipe(O(r=>r.observe(t)),b(r=>cn.pipe(g(o=>o.target===t),A(()=>r.unobserve(t)))),m(()=>de(e)),Q(de(e)))}function Ct(e){return{width:e.scrollWidth,height:e.scrollHeight}}function vr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}function pn(e){let t=[],r=e.parentElement;for(;r;)(e.clientWidth>r.clientWidth||e.clientHeight>r.clientHeight)&&t.push(r),r=(e=r).parentElement;return t.length===0&&t.push(document.documentElement),t}function Be(e){return{x:e.offsetLeft,y:e.offsetTop}}function ln(e){let t=e.getBoundingClientRect();return{x:t.x+window.scrollX,y:t.y+window.scrollY}}function mn(e){return L(h(window,"load"),h(window,"resize")).pipe($e(0,ye),m(()=>Be(e)),Q(Be(e)))}function gr(e){return{x:e.scrollLeft,y:e.scrollTop}}function Ge(e){return L(h(e,"scroll"),h(window,"scroll"),h(window,"resize")).pipe($e(0,ye),m(()=>gr(e)),Q(gr(e)))}var fn=new T,Fa=H(()=>$(new IntersectionObserver(e=>{for(let t of e)fn.next(t)},{threshold:0}))).pipe(b(e=>L(tt,$(e)).pipe(A(()=>e.disconnect()))),Z(1));function mt(e){return Fa.pipe(O(t=>t.observe(e)),b(t=>fn.pipe(g(({target:r})=>r===e),A(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function un(e,t=16){return Ge(e).pipe(m(({y:r})=>{let o=de(e),n=Ct(e);return r>=n.height-o.height-t}),Y())}var yr={drawer:j("[data-md-toggle=drawer]"),search:j("[data-md-toggle=search]")};function dn(e){return yr[e].checked}function at(e,t){yr[e].checked!==t&&yr[e].click()}function Je(e){let t=yr[e];return h(t,"change").pipe(m(()=>t.checked),Q(t.checked))}function ja(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ua(){return L(h(window,"compositionstart").pipe(m(()=>!0)),h(window,"compositionend").pipe(m(()=>!1))).pipe(Q(!1))}function hn(){let e=h(window,"keydown").pipe(g(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:dn("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),g(({mode:t,type:r})=>{if(t==="global"){let o=Ne();if(typeof o!="undefined")return!ja(o,r)}return!0}),le());return Ua().pipe(b(t=>t?y:e))}function we(){return new URL(location.href)}function st(e,t=!1){if(V("navigation.instant")&&!t){let r=x("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function bn(){return new T}function vn(){return location.hash.slice(1)}function gn(e){let t=x("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Zr(e){return L(h(window,"hashchange"),e).pipe(m(vn),Q(vn()),g(t=>t.length>0),Z(1))}function yn(e){return Zr(e).pipe(m(t=>ue(`[id="${t}"]`)),g(t=>typeof t!="undefined"))}function Wt(e){let t=matchMedia(e);return dr(r=>t.addListener(()=>r(t.matches))).pipe(Q(t.matches))}function xn(){let e=matchMedia("print");return L(h(window,"beforeprint").pipe(m(()=>!0)),h(window,"afterprint").pipe(m(()=>!1))).pipe(Q(e.matches))}function eo(e,t){return e.pipe(b(r=>r?t():y))}function to(e,t){return new F(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let s=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+s*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function ze(e,t){return to(e,t).pipe(b(r=>r.text()),m(r=>JSON.parse(r)),Z(1))}function xr(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),Z(1))}function En(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),Z(1))}function wn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function Tn(){return L(h(window,"scroll",{passive:!0}),h(window,"resize",{passive:!0})).pipe(m(wn),Q(wn()))}function Sn(){return{width:innerWidth,height:innerHeight}}function On(){return h(window,"resize",{passive:!0}).pipe(m(Sn),Q(Sn()))}function Ln(){return z([Tn(),On()]).pipe(m(([e,t])=>({offset:e,size:t})),Z(1))}function Er(e,{viewport$:t,header$:r}){let o=t.pipe(ne("size")),n=z([o,r]).pipe(m(()=>Be(e)));return z([r,t,n]).pipe(m(([{height:i},{offset:s,size:a},{x:c,y:p}])=>({offset:{x:s.x-c,y:s.y-p+i},size:a})))}function Da(e){return h(e,"message",t=>t.data)}function Wa(e){let t=new T;return t.subscribe(r=>e.postMessage(r)),t}function Mn(e,t=new Worker(e)){let r=Da(t),o=Wa(t),n=new T;n.subscribe(o);let i=o.pipe(oe(),ae(!0));return n.pipe(oe(),Ve(r.pipe(D(i))),le())}var Va=j("#__config"),kt=JSON.parse(Va.textContent);kt.base=`${new URL(kt.base,we())}`;function Te(){return kt}function V(e){return kt.features.includes(e)}function Me(e,t){return typeof t!="undefined"?kt.translations[e].replace("#",t.toString()):kt.translations[e]}function Ce(e,t=document){return j(`[data-md-component=${e}]`,t)}function me(e,t=document){return M(`[data-md-component=${e}]`,t)}function Na(e){let t=j(".md-typeset > :first-child",e);return h(t,"click",{once:!0}).pipe(m(()=>j(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function _n(e){if(!V("announce.dismiss")||!e.childElementCount)return y;if(!e.hidden){let t=j(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return H(()=>{let t=new T;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),Na(e).pipe(O(r=>t.next(r)),A(()=>t.complete()),m(r=>R({ref:e},r)))})}function za(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function An(e,t){let r=new T;return r.subscribe(({hidden:o})=>{e.hidden=o}),za(e,t).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>R({ref:e},o)))}function Vt(e,t){return t==="inline"?x("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"})):x("div",{class:"md-tooltip",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"}))}function wr(...e){return x("div",{class:"md-tooltip2",role:"dialog"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function Cn(...e){return x("div",{class:"md-tooltip2",role:"tooltip"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function kn(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return x("aside",{class:"md-annotation",tabIndex:0},Vt(t),x("a",{href:r,class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}else return x("aside",{class:"md-annotation",tabIndex:0},Vt(t),x("span",{class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}function Hn(e){return x("button",{class:"md-code__button",title:Me("clipboard.copy"),"data-clipboard-target":`#${e} > code`,"data-md-type":"copy"})}function $n(){return x("button",{class:"md-code__button",title:"Toggle line selection","data-md-type":"select"})}function Rn(){return x("nav",{class:"md-code__nav"})}var In=Rt(ro());function oo(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,x("del",null,(0,In.default)(p))," "],[]).slice(0,-1),i=Te(),s=new URL(e.location,i.base);V("search.highlight")&&s.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:a}=Te();return x("a",{href:`${s}`,class:"md-search-result__link",tabIndex:-1},x("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&x("div",{class:"md-search-result__icon md-icon"}),r>0&&x("h1",null,e.title),r<=0&&x("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&x("nav",{class:"md-tags"},e.tags.map(c=>{let p=a?c in a?`md-tag-icon md-tag--${a[c]}`:"md-tag-icon":"";return x("span",{class:`md-tag ${p}`},c)})),o>0&&n.length>0&&x("p",{class:"md-search-result__terms"},Me("search.result.term.missing"),": ",...n)))}function Fn(e){let t=e[0].score,r=[...e],o=Te(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),s=r.findIndex(l=>l.scoreoo(l,1)),...c.length?[x("details",{class:"md-search-result__more"},x("summary",{tabIndex:-1},x("div",null,c.length>0&&c.length===1?Me("search.result.more.one"):Me("search.result.more.other",c.length))),...c.map(l=>oo(l,1)))]:[]];return x("li",{class:"md-search-result__item"},p)}function jn(e){return x("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>x("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?br(r):r)))}function no(e){let t=`tabbed-control tabbed-control--${e}`;return x("div",{class:t,hidden:!0},x("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function Un(e){return x("div",{class:"md-typeset__scrollwrap"},x("div",{class:"md-typeset__table"},e))}function Qa(e){var o;let t=Te(),r=new URL(`../${e.version}/`,t.base);return x("li",{class:"md-version__item"},x("a",{href:`${r}`,class:"md-version__link"},e.title,((o=t.version)==null?void 0:o.alias)&&e.aliases.length>0&&x("span",{class:"md-version__alias"},e.aliases[0])))}function Dn(e,t){var o;let r=Te();return e=e.filter(n=>{var i;return!((i=n.properties)!=null&&i.hidden)}),x("div",{class:"md-version"},x("button",{class:"md-version__current","aria-label":Me("select.version")},t.title,((o=r.version)==null?void 0:o.alias)&&t.aliases.length>0&&x("span",{class:"md-version__alias"},t.aliases[0])),x("ul",{class:"md-version__list"},e.map(Qa)))}var Ya=0;function Ba(e,t=250){let r=z([Ye(e),it(e,t)]).pipe(m(([n,i])=>n||i),Y()),o=H(()=>pn(e)).pipe(J(Ge),gt(1),Re(r),m(()=>ln(e)));return r.pipe(Pe(n=>n),b(()=>z([r,o])),m(([n,i])=>({active:n,offset:i})),le())}function Nt(e,t,r=250){let{content$:o,viewport$:n}=t,i=`__tooltip2_${Ya++}`;return H(()=>{let s=new T,a=new jr(!1);s.pipe(oe(),ae(!1)).subscribe(a);let c=a.pipe(Ut(l=>He(+!l*250,Wr)),Y(),b(l=>l?o:y),O(l=>l.id=i),le());z([s.pipe(m(({active:l})=>l)),c.pipe(b(l=>it(l,250)),Q(!1))]).pipe(m(l=>l.some(f=>f))).subscribe(a);let p=a.pipe(g(l=>l),te(c,n),m(([l,f,{size:u}])=>{let d=e.getBoundingClientRect(),v=d.width/2;if(f.role==="tooltip")return{x:v,y:8+d.height};if(d.y>=u.height/2){let{height:S}=de(f);return{x:v,y:-16-S}}else return{x:v,y:16+d.height}}));return z([c,s,p]).subscribe(([l,{offset:f},u])=>{l.style.setProperty("--md-tooltip-host-x",`${f.x}px`),l.style.setProperty("--md-tooltip-host-y",`${f.y}px`),l.style.setProperty("--md-tooltip-x",`${u.x}px`),l.style.setProperty("--md-tooltip-y",`${u.y}px`),l.classList.toggle("md-tooltip2--top",u.y<0),l.classList.toggle("md-tooltip2--bottom",u.y>=0)}),a.pipe(g(l=>l),te(c,(l,f)=>f),g(l=>l.role==="tooltip")).subscribe(l=>{let f=de(j(":scope > *",l));l.style.setProperty("--md-tooltip-width",`${f.width}px`),l.style.setProperty("--md-tooltip-tail","0px")}),a.pipe(Y(),xe(ye),te(c)).subscribe(([l,f])=>{f.classList.toggle("md-tooltip2--active",l)}),z([a.pipe(g(l=>l)),c]).subscribe(([l,f])=>{f.role==="dialog"?(e.setAttribute("aria-controls",i),e.setAttribute("aria-haspopup","dialog")):e.setAttribute("aria-describedby",i)}),a.pipe(g(l=>!l)).subscribe(()=>{e.removeAttribute("aria-controls"),e.removeAttribute("aria-describedby"),e.removeAttribute("aria-haspopup")}),Ba(e,r).pipe(O(l=>s.next(l)),A(()=>s.complete()),m(l=>R({ref:e},l)))})}function Xe(e,{viewport$:t},r=document.body){return Nt(e,{content$:new F(o=>{let n=e.title,i=Cn(n);return o.next(i),e.removeAttribute("title"),r.append(i),()=>{i.remove(),e.setAttribute("title",n)}}),viewport$:t},0)}function Ga(e,t){let r=H(()=>z([mn(e),Ge(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:s,height:a}=de(e);return{x:o-i.x+s/2,y:n-i.y+a/2}}));return Ye(e).pipe(b(o=>r.pipe(m(n=>({active:o,offset:n})),Ee(+!o||1/0))))}function Wn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return H(()=>{let i=new T,s=i.pipe(oe(),ae(!0));return i.subscribe({next({offset:a}){e.style.setProperty("--md-tooltip-x",`${a.x}px`),e.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),mt(e).pipe(D(s)).subscribe(a=>{e.toggleAttribute("data-md-visible",a)}),L(i.pipe(g(({active:a})=>a)),i.pipe(Ae(250),g(({active:a})=>!a))).subscribe({next({active:a}){a?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe($e(16,ye)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(gt(125,ye),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?e.style.setProperty("--md-tooltip-0",`${-a}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),h(n,"click").pipe(D(s),g(a=>!(a.metaKey||a.ctrlKey))).subscribe(a=>{a.stopPropagation(),a.preventDefault()}),h(n,"mousedown").pipe(D(s),te(i)).subscribe(([a,{active:c}])=>{var p;if(a.button!==0||a.metaKey||a.ctrlKey)a.preventDefault();else if(c){a.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(p=Ne())==null||p.blur()}}),r.pipe(D(s),g(a=>a===o),nt(125)).subscribe(()=>e.focus()),Ga(e,t).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>R({ref:e},a)))})}function Ja(e){let t=Te();if(e.tagName!=="CODE")return[e];let r=[".c",".c1",".cm"];if(typeof t.annotate!="undefined"){let o=e.closest("[class|=language]");if(o)for(let n of Array.from(o.classList)){if(!n.startsWith("language-"))continue;let[,i]=n.split("-");i in t.annotate&&r.push(...t.annotate[i])}}return M(r.join(", "),e)}function Xa(e){let t=[];for(let r of Ja(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let s;for(;s=/(\(\d+\))(!)?/.exec(i.textContent);){let[,a,c]=s;if(typeof c=="undefined"){let p=i.splitText(s.index);i=p.splitText(a.length),t.push(p)}else{i.textContent=a,t.push(i);break}}}}return t}function Vn(e,t){t.append(...Array.from(e.childNodes))}function Tr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,s=new Map;for(let a of Xa(t)){let[,c]=a.textContent.match(/\((\d+)\)/);ue(`:scope > li:nth-child(${c})`,e)&&(s.set(c,kn(c,i)),a.replaceWith(s.get(c)))}return s.size===0?y:H(()=>{let a=new T,c=a.pipe(oe(),ae(!0)),p=[];for(let[l,f]of s)p.push([j(".md-typeset",f),j(`:scope > li:nth-child(${l})`,e)]);return o.pipe(D(c)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of p)l?Vn(f,u):Vn(u,f)}),L(...[...s].map(([,l])=>Wn(l,t,{target$:r}))).pipe(A(()=>a.complete()),le())})}function Nn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Nn(t)}}function zn(e,t){return H(()=>{let r=Nn(e);return typeof r!="undefined"?Tr(r,e,t):y})}var Kn=Rt(ao());var Za=0,qn=L(h(window,"keydown").pipe(m(()=>!0)),L(h(window,"keyup"),h(window,"contextmenu")).pipe(m(()=>!1))).pipe(Q(!1),Z(1));function Qn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Qn(t)}}function es(e){return Le(e).pipe(m(({width:t})=>({scrollable:Ct(e).width>t})),ne("scrollable"))}function Yn(e,t){let{matches:r}=matchMedia("(hover)"),o=H(()=>{let n=new T,i=n.pipe(Yr(1));n.subscribe(({scrollable:d})=>{d&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let s=[],a=e.closest("pre"),c=a.closest("[id]"),p=c?c.id:Za++;a.id=`__code_${p}`;let l=[],f=e.closest(".highlight");if(f instanceof HTMLElement){let d=Qn(f);if(typeof d!="undefined"&&(f.classList.contains("annotate")||V("content.code.annotate"))){let v=Tr(d,e,t);l.push(Le(f).pipe(D(i),m(({width:S,height:X})=>S&&X),Y(),b(S=>S?v:y)))}}let u=M(":scope > span[id]",e);if(u.length&&(e.classList.add("md-code__content"),e.closest(".select")||V("content.code.select")&&!e.closest(".no-select"))){let d=+u[0].id.split("-").pop(),v=$n();s.push(v),V("content.tooltips")&&l.push(Xe(v,{viewport$}));let S=h(v,"click").pipe(Dt(P=>!P,!1),O(()=>v.blur()),le());S.subscribe(P=>{v.classList.toggle("md-code__button--active",P)});let X=fe(u).pipe(J(P=>it(P).pipe(m(se=>[P,se]))));S.pipe(b(P=>P?X:y)).subscribe(([P,se])=>{let ce=ue(".hll.select",P);if(ce&&!se)ce.replaceWith(...Array.from(ce.childNodes));else if(!ce&&se){let he=document.createElement("span");he.className="hll select",he.append(...Array.from(P.childNodes).slice(1)),P.append(he)}});let re=fe(u).pipe(J(P=>h(P,"mousedown").pipe(O(se=>se.preventDefault()),m(()=>P)))),ee=S.pipe(b(P=>P?re:y),te(qn),m(([P,se])=>{var he;let ce=u.indexOf(P)+d;if(se===!1)return[ce,ce];{let Se=M(".hll",e).map(Ue=>u.indexOf(Ue.parentElement)+d);return(he=window.getSelection())==null||he.removeAllRanges(),[Math.min(ce,...Se),Math.max(ce,...Se)]}})),k=Zr(y).pipe(g(P=>P.startsWith(`__codelineno-${p}-`)));k.subscribe(P=>{let[,,se]=P.split("-"),ce=se.split(":").map(Se=>+Se-d+1);ce.length===1&&ce.push(ce[0]);for(let Se of M(".hll:not(.select)",e))Se.replaceWith(...Array.from(Se.childNodes));let he=u.slice(ce[0]-1,ce[1]);for(let Se of he){let Ue=document.createElement("span");Ue.className="hll",Ue.append(...Array.from(Se.childNodes).slice(1)),Se.append(Ue)}}),k.pipe(Ee(1),xe(pe)).subscribe(P=>{if(P.includes(":")){let se=document.getElementById(P.split(":")[0]);se&&setTimeout(()=>{let ce=se,he=-64;for(;ce!==document.body;)he+=ce.offsetTop,ce=ce.offsetParent;window.scrollTo({top:he})},1)}});let je=fe(M('a[href^="#__codelineno"]',f)).pipe(J(P=>h(P,"click").pipe(O(se=>se.preventDefault()),m(()=>P)))).pipe(D(i),te(qn),m(([P,se])=>{let he=+j(`[id="${P.hash.slice(1)}"]`).parentElement.id.split("-").pop();if(se===!1)return[he,he];{let Se=M(".hll",e).map(Ue=>+Ue.parentElement.id.split("-").pop());return[Math.min(he,...Se),Math.max(he,...Se)]}}));L(ee,je).subscribe(P=>{let se=`#__codelineno-${p}-`;P[0]===P[1]?se+=P[0]:se+=`${P[0]}:${P[1]}`,history.replaceState({},"",se),window.dispatchEvent(new HashChangeEvent("hashchange",{newURL:window.location.origin+window.location.pathname+se,oldURL:window.location.href}))})}if(Kn.default.isSupported()&&(e.closest(".copy")||V("content.code.copy")&&!e.closest(".no-copy"))){let d=Hn(a.id);s.push(d),V("content.tooltips")&&l.push(Xe(d,{viewport$}))}if(s.length){let d=Rn();d.append(...s),a.insertBefore(d,e)}return es(e).pipe(O(d=>n.next(d)),A(()=>n.complete()),m(d=>R({ref:e},d)),Ve(L(...l).pipe(D(i))))});return V("content.lazy")?mt(e).pipe(g(n=>n),Ee(1),b(()=>o)):o}function ts(e,{target$:t,print$:r}){let o=!0;return L(t.pipe(m(n=>n.closest("details:not([open])")),g(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(g(n=>n||!o),O(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Bn(e,t){return H(()=>{let r=new T;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),ts(e,t).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>R({ref:e},o)))})}var Gn=0;function rs(e){let t=document.createElement("h3");t.innerHTML=e.innerHTML;let r=[t],o=e.nextElementSibling;for(;o&&!(o instanceof HTMLHeadingElement);)r.push(o),o=o.nextElementSibling;return r}function os(e,t){for(let r of M("[href], [src]",e))for(let o of["href","src"]){let n=r.getAttribute(o);if(n&&!/^(?:[a-z]+:)?\/\//i.test(n)){r[o]=new URL(r.getAttribute(o),t).toString();break}}for(let r of M("[name^=__], [for]",e))for(let o of["id","for","name"]){let n=r.getAttribute(o);n&&r.setAttribute(o,`${n}$preview_${Gn}`)}return Gn++,$(e)}function Jn(e,t){let{sitemap$:r}=t;if(!(e instanceof HTMLAnchorElement))return y;if(!(V("navigation.instant.preview")||e.hasAttribute("data-preview")))return y;let o=z([Ye(e),it(e)]).pipe(m(([i,s])=>i||s),Y(),g(i=>i));return rt([r,o]).pipe(b(([i])=>{let s=new URL(e.href);return s.search=s.hash="",i.has(`${s}`)?$(s):y}),b(i=>xr(i).pipe(b(s=>os(s,i)))),b(i=>{let s=e.hash?`article [id="${e.hash.slice(1)}"]`:"article h1",a=ue(s,i);return typeof a=="undefined"?y:$(rs(a))})).pipe(b(i=>{let s=new F(a=>{let c=wr(...i);return a.next(c),document.body.append(c),()=>c.remove()});return Nt(e,R({content$:s},t))}))}var Xn=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel p,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel p{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color);stroke-width:.05rem}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs #classDiagram-compositionEnd,defs #classDiagram-compositionStart,defs #classDiagram-dependencyEnd,defs #classDiagram-dependencyStart,defs #classDiagram-extensionEnd,defs #classDiagram-extensionStart{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs #classDiagram-aggregationEnd,defs #classDiagram-aggregationStart{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}a .nodeLabel{text-decoration:underline}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}.attributeBoxEven,.attributeBoxOdd{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityBox{fill:var(--md-mermaid-label-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityLabel{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.relationshipLabelBox{fill:var(--md-mermaid-label-bg-color);fill-opacity:1;background-color:var(--md-mermaid-label-bg-color);opacity:1}.relationshipLabel{fill:var(--md-mermaid-label-fg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs #ONE_OR_MORE_END *,defs #ONE_OR_MORE_START *,defs #ONLY_ONE_END *,defs #ONLY_ONE_START *,defs #ZERO_OR_MORE_END *,defs #ZERO_OR_MORE_START *,defs #ZERO_OR_ONE_END *,defs #ZERO_OR_ONE_START *{stroke:var(--md-mermaid-edge-color)!important}defs #ZERO_OR_MORE_END circle,defs #ZERO_OR_MORE_START circle{fill:var(--md-mermaid-label-bg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var so,is=0;function as(){return typeof mermaid=="undefined"||mermaid instanceof Element?At("https://docs.photoprism.app/assets/external/unpkg.com/mermaid@11/dist/mermaid.min.js"):$(void 0)}function Zn(e){return e.classList.remove("mermaid"),so||(so=as().pipe(O(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Xn,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),Z(1))),so.subscribe(()=>go(this,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${is++}`,r=x("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),s=r.attachShadow({mode:"closed"});s.innerHTML=n,e.replaceWith(r),i==null||i(s)})),so.pipe(m(()=>({ref:e})))}var ei=x("table");function ti(e){return e.replaceWith(ei),ei.replaceWith(Un(e)),$({ref:e})}function ss(e){let t=e.find(r=>r.checked)||e[0];return L(...e.map(r=>h(r,"change").pipe(m(()=>j(`label[for="${r.id}"]`))))).pipe(Q(j(`label[for="${t.id}"]`)),m(r=>({active:r})))}function ri(e,{viewport$:t,target$:r}){let o=j(".tabbed-labels",e),n=M(":scope > input",e),i=no("prev");e.append(i);let s=no("next");return e.append(s),H(()=>{let a=new T,c=a.pipe(oe(),ae(!0));z([a,Le(e),mt(e)]).pipe(D(c),$e(1,ye)).subscribe({next([{active:p},l]){let f=Be(p),{width:u}=de(p);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let d=gr(o);(f.xd.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),z([Ge(o),Le(o)]).pipe(D(c)).subscribe(([p,l])=>{let f=Ct(o);i.hidden=p.x<16,s.hidden=p.x>f.width-l.width-16}),L(h(i,"click").pipe(m(()=>-1)),h(s,"click").pipe(m(()=>1))).pipe(D(c)).subscribe(p=>{let{width:l}=de(o);o.scrollBy({left:l*p,behavior:"smooth"})}),r.pipe(D(c),g(p=>n.includes(p))).subscribe(p=>p.click()),o.classList.add("tabbed-labels--linked");for(let p of n){let l=j(`label[for="${p.id}"]`);l.replaceChildren(x("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),h(l.firstElementChild,"click").pipe(D(c),g(f=>!(f.metaKey||f.ctrlKey)),O(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return V("content.tabs.link")&&a.pipe(Ie(1),te(t)).subscribe(([{active:p},{offset:l}])=>{let f=p.innerText.trim();if(p.hasAttribute("data-md-switching"))p.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let v of M("[data-tabs]"))for(let S of M(":scope > input",v)){let X=j(`label[for="${S.id}"]`);if(X!==p&&X.innerText.trim()===f){X.setAttribute("data-md-switching",""),S.click();break}}window.scrollTo({top:e.offsetTop-u});let d=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...d])])}}),a.pipe(D(c)).subscribe(()=>{for(let p of M("audio, video",e))p.pause()}),ss(n).pipe(O(p=>a.next(p)),A(()=>a.complete()),m(p=>R({ref:e},p)))}).pipe(et(pe))}function oi(e,t){let{viewport$:r,target$:o,print$:n}=t;return L(...M(".annotate:not(.highlight)",e).map(i=>zn(i,{target$:o,print$:n})),...M("pre:not(.mermaid) > code",e).map(i=>Yn(i,{target$:o,print$:n})),...M("a:not([title])",e).map(i=>Jn(i,t)),...M("pre.mermaid",e).map(i=>Zn(i)),...M("table:not([class])",e).map(i=>ti(i)),...M("details",e).map(i=>Bn(i,{target$:o,print$:n})),...M("[data-tabs]",e).map(i=>ri(i,{viewport$:r,target$:o})),...M("[title]",e).filter(()=>V("content.tooltips")).map(i=>Xe(i,{viewport$:r})),...M(".footnote-ref",e).filter(()=>V("content.footnote.tooltips")).map(i=>Nt(i,{content$:new F(s=>{let a=new URL(i.href).hash.slice(1),c=Array.from(document.getElementById(a).cloneNode(!0).children),p=wr(...c);return s.next(p),document.body.append(p),()=>p.remove()}),viewport$:r})))}function cs(e,{alert$:t}){return t.pipe(b(r=>L($(!0),$(!1).pipe(nt(2e3))).pipe(m(o=>({message:r,active:o})))))}function ni(e,t){let r=j(".md-typeset",e);return H(()=>{let o=new T;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),cs(e,t).pipe(O(n=>o.next(n)),A(()=>o.complete()),m(n=>R({ref:e},n)))})}var ps=0;function ls(e,t){document.body.append(e);let{width:r}=de(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=vr(t),n=typeof o!="undefined"?Ge(o):$({x:0,y:0}),i=L(Ye(t),it(t)).pipe(Y());return z([i,n]).pipe(m(([s,a])=>{let{x:c,y:p}=Be(t),l=de(t),f=t.closest("table");return f&&t.parentElement&&(c+=f.offsetLeft+t.parentElement.offsetLeft,p+=f.offsetTop+t.parentElement.offsetTop),{active:s,offset:{x:c-a.x+l.width/2-r/2,y:p-a.y+l.height+8}}}))}function ii(e){let t=e.title;if(!t.length)return y;let r=`__tooltip_${ps++}`,o=Vt(r,"inline"),n=j(".md-typeset",o);return n.innerHTML=t,H(()=>{let i=new T;return i.subscribe({next({offset:s}){o.style.setProperty("--md-tooltip-x",`${s.x}px`),o.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),L(i.pipe(g(({active:s})=>s)),i.pipe(Ae(250),g(({active:s})=>!s))).subscribe({next({active:s}){s?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe($e(16,ye)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(gt(125,ye),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?o.style.setProperty("--md-tooltip-0",`${-s}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),ls(o,e).pipe(O(s=>i.next(s)),A(()=>i.complete()),m(s=>R({ref:e},s)))}).pipe(et(pe))}function ms({viewport$:e}){if(!V("header.autohide"))return $(!1);let t=e.pipe(m(({offset:{y:n}})=>n),ot(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),Y()),o=Je("search");return z([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),Y(),b(n=>n?r:$(!1)),Q(!1))}function ai(e,t){return H(()=>z([Le(e),ms(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),Y((r,o)=>r.height===o.height&&r.hidden===o.hidden),Z(1))}function si(e,{header$:t,main$:r}){return H(()=>{let o=new T,n=o.pipe(oe(),ae(!0));o.pipe(ne("active"),Re(t)).subscribe(([{active:s},{hidden:a}])=>{e.classList.toggle("md-header--shadow",s&&!a),e.hidden=a});let i=fe(M("[title]",e)).pipe(g(()=>V("content.tooltips")),J(s=>ii(s)));return r.subscribe(o),t.pipe(D(n),m(s=>R({ref:e},s)),Ve(i.pipe(D(n))))})}function fs(e,{viewport$:t,header$:r}){return Er(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=de(e);return{active:o>=n}}),ne("active"))}function ci(e,t){return H(()=>{let r=new T;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=ue(".md-content h1");return typeof o=="undefined"?y:fs(o,t).pipe(O(n=>r.next(n)),A(()=>r.complete()),m(n=>R({ref:e},n)))})}function pi(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),Y()),n=o.pipe(b(()=>Le(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),ne("bottom"))));return z([o,n,t]).pipe(m(([i,{top:s,bottom:a},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,s-c,i)-Math.max(0,p+c-a)),{offset:s-i,height:p,active:s-i<=c})),Y((i,s)=>i.offset===s.offset&&i.height===s.height&&i.active===s.active))}function us(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return $(...e).pipe(J(o=>h(o,"change").pipe(m(()=>o))),Q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),Z(1))}function li(e){let t=M("input",e),r=x("meta",{name:"theme-color"});document.head.appendChild(r);let o=x("meta",{name:"color-scheme"});document.head.appendChild(o);let n=Wt("(prefers-color-scheme: light)");return H(()=>{let i=new T;return i.subscribe(s=>{if(document.body.setAttribute("data-md-color-switching",""),s.color.media==="(prefers-color-scheme)"){let a=matchMedia("(prefers-color-scheme: light)"),c=document.querySelector(a.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");s.color.scheme=c.getAttribute("data-md-color-scheme"),s.color.primary=c.getAttribute("data-md-color-primary"),s.color.accent=c.getAttribute("data-md-color-accent")}for(let[a,c]of Object.entries(s.color))document.body.setAttribute(`data-md-color-${a}`,c);for(let a=0;as.key==="Enter"),te(i,(s,a)=>a)).subscribe(({index:s})=>{s=(s+1)%t.length,t[s].click(),t[s].focus()}),i.pipe(m(()=>{let s=Ce("header"),a=window.getComputedStyle(s);return o.content=a.colorScheme,a.backgroundColor.match(/\d+/g).map(c=>(+c).toString(16).padStart(2,"0")).join("")})).subscribe(s=>r.content=`#${s}`),i.pipe(xe(pe)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),us(t).pipe(D(n.pipe(Ie(1))),vt(),O(s=>i.next(s)),A(()=>i.complete()),m(s=>R({ref:e},s)))})}function mi(e,{progress$:t}){return H(()=>{let r=new T;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(O(o=>r.next({value:o})),A(()=>r.complete()),m(o=>({ref:e,value:o})))})}function fi(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function ds(e,t){let r=new Map;for(let o of M("url",e)){let n=j("loc",o),i=[fi(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let s of M("[rel=alternate]",o)){let a=s.getAttribute("href");a!=null&&i.push(fi(new URL(a),t))}}return r}function Ht(e){return En(new URL("sitemap.xml",e)).pipe(m(t=>ds(t,new URL(e))),ve(()=>$(new Map)),le())}function ui({document$:e}){let t=new Map;e.pipe(b(()=>M("link[rel=alternate]")),m(r=>new URL(r.href)),g(r=>!t.has(r.toString())),J(r=>Ht(r).pipe(m(o=>[r,o]),ve(()=>y)))).subscribe(([r,o])=>{t.set(r.toString().replace(/\/$/,""),o)}),h(document.body,"click").pipe(g(r=>!r.metaKey&&!r.ctrlKey),b(r=>{if(r.target instanceof Element){let o=r.target.closest("a");if(o&&!o.target){let n=[...t].find(([f])=>o.href.startsWith(`${f}/`));if(typeof n=="undefined")return y;let[i,s]=n,a=we();if(a.href.startsWith(i))return y;let c=Te(),p=a.href.replace(c.base,"");p=`${i}/${p}`;let l=s.has(p.split("#")[0])?new URL(p,c.base):new URL(i);return r.preventDefault(),$(l)}}return y})).subscribe(r=>st(r,!0))}var co=Rt(ao());function hs(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function di({alert$:e}){co.default.isSupported()&&new F(t=>{new co.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||hs(j(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(O(t=>{t.trigger.focus()}),m(()=>Me("clipboard.copied"))).subscribe(e)}function hi(e,t){if(!(e.target instanceof Element))return y;let r=e.target.closest("a");if(r===null)return y;if(r.target||e.metaKey||e.ctrlKey)return y;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),$(r)):y}function bi(e){let t=new Map;for(let r of M(":scope > *",e.head))t.set(r.outerHTML,r);return t}function vi(e){for(let t of M("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return $(e)}function bs(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...V("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=ue(o),i=ue(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=bi(document);for(let[o,n]of bi(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Ce("container");return Ke(M("script",r)).pipe(b(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new F(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),y}),oe(),ae(document))}function gi({sitemap$:e,location$:t,viewport$:r,progress$:o}){if(location.protocol==="file:")return y;$(document).subscribe(vi);let n=h(document.body,"click").pipe(Re(e),b(([a,c])=>hi(a,c)),m(({href:a})=>new URL(a)),le()),i=h(window,"popstate").pipe(m(we),le());n.pipe(te(r)).subscribe(([a,{offset:c}])=>{history.replaceState(c,""),history.pushState(null,"",a)}),L(n,i).subscribe(t);let s=t.pipe(ne("pathname"),b(a=>xr(a,{progress$:o}).pipe(ve(()=>(st(a,!0),y)))),b(vi),b(bs),le());return L(s.pipe(te(t,(a,c)=>c)),s.pipe(b(()=>t),ne("pathname"),b(()=>t),ne("hash")),t.pipe(Y((a,c)=>a.pathname===c.pathname&&a.hash===c.hash),b(()=>n),O(()=>history.back()))).subscribe(a=>{var c,p;history.state!==null||!a.hash?window.scrollTo(0,(p=(c=history.state)==null?void 0:c.y)!=null?p:0):(history.scrollRestoration="auto",gn(a.hash),history.scrollRestoration="manual")}),t.subscribe(()=>{history.scrollRestoration="manual"}),h(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),r.pipe(ne("offset"),Ae(100)).subscribe(({offset:a})=>{history.replaceState(a,"")}),V("navigation.instant.prefetch")&&L(h(document.body,"mousemove"),h(document.body,"focusin")).pipe(Re(e),b(([a,c])=>hi(a,c)),Ae(25),Qr(({href:a})=>a),hr(a=>{let c=document.createElement("link");return c.rel="prefetch",c.href=a.toString(),document.head.appendChild(c),h(c,"load").pipe(m(()=>c),Ee(1))})).subscribe(a=>a.remove()),s}var yi=Rt(ro());function xi(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,s)=>`${i}${s}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return s=>(0,yi.default)(s).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function qt(e){return e.type===1}function Sr(e){return e.type===3}function Ei(e,t){let r=Mn(e);return L($(location.protocol!=="file:"),Je("search")).pipe(Pe(o=>o),b(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:V("search.suggest")}}})),r}function wi(e){var l;let{selectedVersionSitemap:t,selectedVersionBaseURL:r,currentLocation:o,currentBaseURL:n}=e,i=(l=po(n))==null?void 0:l.pathname;if(i===void 0)return;let s=ys(o.pathname,i);if(s===void 0)return;let a=Es(t.keys());if(!t.has(a))return;let c=po(s,a);if(!c||!t.has(c.href))return;let p=po(s,r);if(p)return p.hash=o.hash,p.search=o.search,p}function po(e,t){try{return new URL(e,t)}catch(r){return}}function ys(e,t){if(e.startsWith(t))return e.slice(t.length)}function xs(e,t){let r=Math.min(e.length,t.length),o;for(o=0;oy)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:s,aliases:a})=>s===i||a.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),b(n=>h(document.body,"click").pipe(g(i=>!i.metaKey&&!i.ctrlKey),te(o),b(([i,s])=>{if(i.target instanceof Element){let a=i.target.closest("a");if(a&&!a.target&&n.has(a.href)){let c=a.href;return!i.target.closest(".md-version")&&n.get(c)===s?y:(i.preventDefault(),$(new URL(c)))}}return y}),b(i=>Ht(i).pipe(m(s=>{var a;return(a=wi({selectedVersionSitemap:s,selectedVersionBaseURL:i,currentLocation:we(),currentBaseURL:t.base}))!=null?a:i})))))).subscribe(n=>st(n,!0)),z([r,o]).subscribe(([n,i])=>{j(".md-header__topic").appendChild(Dn(n,i))}),e.pipe(b(()=>o)).subscribe(n=>{var s;let i=__md_get("__outdated",sessionStorage);if(i===null){i=!0;let a=((s=t.version)==null?void 0:s.default)||"latest";Array.isArray(a)||(a=[a]);e:for(let c of a)for(let p of n.aliases.concat(n.version))if(new RegExp(c,"i").test(p)){i=!1;break e}__md_set("__outdated",i,sessionStorage)}if(i)for(let a of me("outdated"))a.hidden=!1})}function ws(e,{worker$:t}){let{searchParams:r}=we();r.has("q")&&(at("search",!0),e.value=r.get("q"),e.focus(),Je("search").pipe(Pe(i=>!i)).subscribe(()=>{let i=we();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=Ye(e),n=L(t.pipe(Pe(qt)),h(e,"keyup"),o).pipe(m(()=>e.value),Y());return z([n,o]).pipe(m(([i,s])=>({value:i,focus:s})),Z(1))}function Si(e,{worker$:t}){let r=new T,o=r.pipe(oe(),ae(!0));z([t.pipe(Pe(qt)),r],(i,s)=>s).pipe(ne("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(ne("focus")).subscribe(({focus:i})=>{i&&at("search",i)}),h(e.form,"reset").pipe(D(o)).subscribe(()=>e.focus());let n=j("header [for=__search]");return h(n,"click").subscribe(()=>e.focus()),ws(e,{worker$:t}).pipe(O(i=>r.next(i)),A(()=>r.complete()),m(i=>R({ref:e},i)),Z(1))}function Oi(e,{worker$:t,query$:r}){let o=new T,n=un(e.parentElement).pipe(g(Boolean)),i=e.parentElement,s=j(":scope > :first-child",e),a=j(":scope > :last-child",e);Je("search").subscribe(l=>a.setAttribute("role",l?"list":"presentation")),o.pipe(te(r),Gr(t.pipe(Pe(qt)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:s.textContent=f.length?Me("search.result.none"):Me("search.result.placeholder");break;case 1:s.textContent=Me("search.result.one");break;default:let u=br(l.length);s.textContent=Me("search.result.other",u)}});let c=o.pipe(O(()=>a.innerHTML=""),b(({items:l})=>L($(...l.slice(0,10)),$(...l.slice(10)).pipe(ot(4),Xr(n),b(([f])=>f)))),m(Fn),le());return c.subscribe(l=>a.appendChild(l)),c.pipe(J(l=>{let f=ue("details",l);return typeof f=="undefined"?y:h(f,"toggle").pipe(D(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(g(Sr),m(({data:l})=>l)).pipe(O(l=>o.next(l)),A(()=>o.complete()),m(l=>R({ref:e},l)))}function Ts(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=we();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function Li(e,t){let r=new T,o=r.pipe(oe(),ae(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),h(e,"click").pipe(D(o)).subscribe(n=>n.preventDefault()),Ts(e,t).pipe(O(n=>r.next(n)),A(()=>r.complete()),m(n=>R({ref:e},n)))}function Mi(e,{worker$:t,keyboard$:r}){let o=new T,n=Ce("search-query"),i=L(h(n,"keydown"),h(n,"focus")).pipe(xe(pe),m(()=>n.value),Y());return o.pipe(Re(i),m(([{suggest:a},c])=>{let p=c.split(/([\s-]+)/);if(a!=null&&a.length&&p[p.length-1]){let l=a[a.length-1];l.startsWith(p[p.length-1])&&(p[p.length-1]=l)}else p.length=0;return p})).subscribe(a=>e.innerHTML=a.join("").replace(/\s/g," ")),r.pipe(g(({mode:a})=>a==="search")).subscribe(a=>{switch(a.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(g(Sr),m(({data:a})=>a)).pipe(O(a=>o.next(a)),A(()=>o.complete()),m(()=>({ref:e})))}function _i(e,{index$:t,keyboard$:r}){let o=Te();try{let n=Ei(o.search,t),i=Ce("search-query",e),s=Ce("search-result",e);h(e,"click").pipe(g(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>at("search",!1)),r.pipe(g(({mode:c})=>c==="search")).subscribe(c=>{let p=Ne();switch(c.type){case"Enter":if(p===i){let l=new Map;for(let f of M(":first-child [href]",s)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,d])=>d-u);f.click()}c.claim()}break;case"Escape":case"Tab":at("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let l=[i,...M(":not(details) > [href], summary, details[open] [href]",s)],f=Math.max(0,(Math.max(0,l.indexOf(p))+l.length+(c.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}c.claim();break;default:i!==Ne()&&i.focus()}}),r.pipe(g(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let a=Si(i,{worker$:n});return L(a,Oi(s,{worker$:n,query$:a})).pipe(Ve(...me("search-share",e).map(c=>Li(c,{query$:a})),...me("search-suggest",e).map(c=>Mi(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,tt}}function Ai(e,{index$:t,location$:r}){return z([t,r.pipe(Q(we()),g(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>xi(o.config)(n.searchParams.get("h"))),m(o=>{var s;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let a=i.nextNode();a;a=i.nextNode())if((s=a.parentElement)!=null&&s.offsetHeight){let c=a.textContent,p=o(c);p.length>c.length&&n.set(a,p)}for(let[a,c]of n){let{childNodes:p}=x("span",null,c);a.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function Ss(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return z([r,t]).pipe(m(([{offset:i,height:s},{offset:{y:a}}])=>(s=s+Math.min(n,Math.max(0,a-i))-n,{height:s,locked:a>=i+n})),Y((i,s)=>i.height===s.height&&i.locked===s.locked))}function lo(e,o){var n=o,{header$:t}=n,r=vo(n,["header$"]);let i=j(".md-sidebar__scrollwrap",e),{y:s}=Be(i);return H(()=>{let a=new T,c=a.pipe(oe(),ae(!0)),p=a.pipe($e(0,ye));return p.pipe(te(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*s}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe(Pe()).subscribe(()=>{for(let l of M(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2})}}}),fe(M("label[tabindex]",e)).pipe(J(l=>h(l,"click").pipe(xe(pe),m(()=>l),D(c)))).subscribe(l=>{let f=j(`[id="${l.htmlFor}"]`);j(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),V("content.tooltips")&&fe(M("abbr[title]",e)).pipe(J(l=>Xe(l,{viewport$})),D(c)).subscribe(),Ss(e,r).pipe(O(l=>a.next(l)),A(()=>a.complete()),m(l=>R({ref:e},l)))})}function Ci(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return rt(ze(`${r}/releases/latest`).pipe(ve(()=>y),m(o=>({version:o.tag_name})),Qe({})),ze(r).pipe(ve(()=>y),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),Qe({}))).pipe(m(([o,n])=>R(R({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return ze(r).pipe(m(o=>({repositories:o.public_repos})),Qe({}))}}function ki(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return rt(ze(`${r}/releases/permalink/latest`).pipe(ve(()=>y),m(({tag_name:o})=>({version:o})),Qe({})),ze(r).pipe(ve(()=>y),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Qe({}))).pipe(m(([o,n])=>R(R({},o),n)))}function Hi(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return Ci(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return ki(r,o)}return y}var Os;function Ls(e){return Os||(Os=H(()=>{let t=__md_get("__source",sessionStorage);if(t)return $(t);if(me("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return y}return Hi(e.href).pipe(O(o=>__md_set("__source",o,sessionStorage)))}).pipe(ve(()=>y),g(t=>Object.keys(t).length>0),m(t=>({facts:t})),Z(1)))}function $i(e){let t=j(":scope > :last-child",e);return H(()=>{let r=new T;return r.subscribe(({facts:o})=>{t.appendChild(jn(o)),t.classList.add("md-source__repository--active")}),Ls(e).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>R({ref:e},o)))})}function Ms(e,{viewport$:t,header$:r}){return Le(document.body).pipe(b(()=>Er(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),ne("hidden"))}function Ri(e,t){return H(()=>{let r=new T;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(V("navigation.tabs.sticky")?$({hidden:!1}):Ms(e,t)).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>R({ref:e},o)))})}function _s(e,{viewport$:t,header$:r}){let o=new Map,n=M(".md-nav__link",e);for(let a of n){let c=decodeURIComponent(a.hash.substring(1)),p=ue(`[id="${c}"]`);typeof p!="undefined"&&o.set(a,p)}let i=r.pipe(ne("height"),m(({height:a})=>{let c=Ce("main"),p=j(":scope > :first-child",c);return a+.8*(p.offsetTop-c.offsetTop)}),le());return Le(document.body).pipe(ne("height"),b(a=>H(()=>{let c=[];return $([...o].reduce((p,[l,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let d=f.offsetParent;for(;d;d=d.offsetParent)u+=d.offsetTop;return p.set([...c=[...c,l]].reverse(),u)},new Map))}).pipe(m(c=>new Map([...c].sort(([,p],[,l])=>p-l))),Re(i),b(([c,p])=>t.pipe(Dt(([l,f],{offset:{y:u},size:d})=>{let v=u+d.height>=Math.floor(a.height);for(;f.length;){let[,S]=f[0];if(S-p=u&&!v)f=[l.pop(),...f];else break}return[l,f]},[[],[...c]]),Y((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([a,c])=>({prev:a.map(([p])=>p),next:c.map(([p])=>p)})),Q({prev:[],next:[]}),ot(2,1),m(([a,c])=>a.prev.length{let i=new T,s=i.pipe(oe(),ae(!0));if(i.subscribe(({prev:a,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[l]]of a.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",p===a.length-1)}),V("toc.follow")){let a=L(t.pipe(Ae(1),m(()=>{})),t.pipe(Ae(250),m(()=>"smooth")));i.pipe(g(({prev:c})=>c.length>0),Re(o.pipe(xe(pe))),te(a)).subscribe(([[{prev:c}],p])=>{let[l]=c[c.length-1];if(l.offsetHeight){let f=vr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2,behavior:p})}}})}return V("navigation.tracking")&&t.pipe(D(s),ne("offset"),Ae(250),Ie(1),D(n.pipe(Ie(1))),vt({delay:250}),te(i)).subscribe(([,{prev:a}])=>{let c=we(),p=a[a.length-1];if(p&&p.length){let[l]=p,{hash:f}=new URL(l.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),_s(e,{viewport$:t,header$:r}).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>R({ref:e},a)))})}function As(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:s}})=>s),ot(2,1),m(([s,a])=>s>a&&a>0),Y()),i=r.pipe(m(({active:s})=>s));return z([i,n]).pipe(m(([s,a])=>!(s&&a)),Y(),D(o.pipe(Ie(1))),ae(!0),vt({delay:250}),m(s=>({hidden:s})))}function Ii(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new T,s=i.pipe(oe(),ae(!0));return i.subscribe({next({hidden:a}){e.hidden=a,a?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(D(s),ne("height")).subscribe(({height:a})=>{e.style.top=`${a+16}px`}),h(e,"click").subscribe(a=>{a.preventDefault(),window.scrollTo({top:0})}),As(e,{viewport$:t,main$:o,target$:n}).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>R({ref:e},a)))}function Fi({document$:e,viewport$:t}){e.pipe(b(()=>M(".md-ellipsis")),J(r=>mt(r).pipe(D(e.pipe(Ie(1))),g(o=>o),m(()=>r),Ee(1))),g(r=>r.offsetWidth{let o=r.innerText,n=r.closest("a")||r;return n.title=o,V("content.tooltips")?Xe(n,{viewport$:t}).pipe(D(e.pipe(Ie(1))),A(()=>n.removeAttribute("title"))):y})).subscribe(),V("content.tooltips")&&e.pipe(b(()=>M(".md-status")),J(r=>Xe(r,{viewport$:t}))).subscribe()}function ji({document$:e,tablet$:t}){e.pipe(b(()=>M(".md-toggle--indeterminate")),O(r=>{r.indeterminate=!0,r.checked=!1}),J(r=>h(r,"change").pipe(Jr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),te(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function Cs(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function Ui({document$:e}){e.pipe(b(()=>M("[data-md-scrollfix]")),O(t=>t.removeAttribute("data-md-scrollfix")),g(Cs),J(t=>h(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function Di({viewport$:e,tablet$:t}){z([Je("search"),t]).pipe(m(([r,o])=>r&&!o),b(r=>$(r).pipe(nt(r?400:100))),te(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ks(){return location.protocol==="file:"?At(`${new URL("search/search_index.js",Or.base)}`).pipe(m(()=>__index),Z(1)):ze(new URL("search/search_index.json",Or.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var ct=an(),Qt=bn(),$t=yn(Qt),mo=hn(),ke=Ln(),Lr=Wt("(min-width: 960px)"),Vi=Wt("(min-width: 1220px)"),Ni=xn(),Or=Te(),zi=document.forms.namedItem("search")?ks():tt,fo=new T;di({alert$:fo});ui({document$:ct});var uo=new T,qi=Ht(Or.base);V("navigation.instant")&&gi({sitemap$:qi,location$:Qt,viewport$:ke,progress$:uo}).subscribe(ct);var Wi;((Wi=Or.version)==null?void 0:Wi.provider)==="mike"&&Ti({document$:ct});L(Qt,$t).pipe(nt(125)).subscribe(()=>{at("drawer",!1),at("search",!1)});mo.pipe(g(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=ue("link[rel=prev]");typeof t!="undefined"&&st(t);break;case"n":case".":let r=ue("link[rel=next]");typeof r!="undefined"&&st(r);break;case"Enter":let o=Ne();o instanceof HTMLLabelElement&&o.click()}});Fi({viewport$:ke,document$:ct});ji({document$:ct,tablet$:Lr});Ui({document$:ct});Di({viewport$:ke,tablet$:Lr});var ft=ai(Ce("header"),{viewport$:ke}),Kt=ct.pipe(m(()=>Ce("main")),b(e=>pi(e,{viewport$:ke,header$:ft})),Z(1)),Hs=L(...me("consent").map(e=>An(e,{target$:$t})),...me("dialog").map(e=>ni(e,{alert$:fo})),...me("palette").map(e=>li(e)),...me("progress").map(e=>mi(e,{progress$:uo})),...me("search").map(e=>_i(e,{index$:zi,keyboard$:mo})),...me("source").map(e=>$i(e))),$s=H(()=>L(...me("announce").map(e=>_n(e)),...me("content").map(e=>oi(e,{sitemap$:qi,viewport$:ke,target$:$t,print$:Ni})),...me("content").map(e=>V("search.highlight")?Ai(e,{index$:zi,location$:Qt}):y),...me("header").map(e=>si(e,{viewport$:ke,header$:ft,main$:Kt})),...me("header-title").map(e=>ci(e,{viewport$:ke,header$:ft})),...me("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?eo(Vi,()=>lo(e,{viewport$:ke,header$:ft,main$:Kt})):eo(Lr,()=>lo(e,{viewport$:ke,header$:ft,main$:Kt}))),...me("tabs").map(e=>Ri(e,{viewport$:ke,header$:ft})),...me("toc").map(e=>Pi(e,{viewport$:ke,header$:ft,main$:Kt,target$:$t})),...me("top").map(e=>Ii(e,{viewport$:ke,header$:ft,main$:Kt,target$:$t})))),Ki=ct.pipe(b(()=>$s),Ve(Hs),Z(1));Ki.subscribe();window.document$=ct;window.location$=Qt;window.target$=$t;window.keyboard$=mo;window.viewport$=ke;window.tablet$=Lr;window.screen$=Vi;window.print$=Ni;window.alert$=fo;window.progress$=uo;window.component$=Ki;})(); diff --git a/assets/javascripts/lunr/min/lunr.ar.min.js b/assets/javascripts/lunr/min/lunr.ar.min.js new file mode 100644 index 0000000000..9b06c26c1f --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.ar.min.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ar=function(){this.pipeline.reset(),this.pipeline.add(e.ar.trimmer,e.ar.stopWordFilter,e.ar.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ar.stemmer))},e.ar.wordCharacters="ء-ٛٱـ",e.ar.trimmer=e.trimmerSupport.generateTrimmer(e.ar.wordCharacters),e.Pipeline.registerFunction(e.ar.trimmer,"trimmer-ar"),e.ar.stemmer=function(){var e=this;return e.result=!1,e.preRemoved=!1,e.sufRemoved=!1,e.pre={pre1:"ف ك ب و س ل ن ا ي ت",pre2:"ال لل",pre3:"بال وال فال تال كال ولل",pre4:"فبال كبال وبال وكال"},e.suf={suf1:"ه ك ت ن ا ي",suf2:"نك نه ها وك يا اه ون ين تن تم نا وا ان كم كن ني نن ما هم هن تك ته ات يه",suf3:"تين كهم نيه نهم ونه وها يهم ونا ونك وني وهم تكم تنا تها تني تهم كما كها ناه نكم هنا تان يها",suf4:"كموه ناها ونني ونهم تكما تموه تكاه كماه ناكم ناهم نيها وننا"},e.patterns=JSON.parse('{"pt43":[{"pt":[{"c":"ا","l":1}]},{"pt":[{"c":"ا,ت,ن,ي","l":0}],"mPt":[{"c":"ف","l":0,"m":1},{"c":"ع","l":1,"m":2},{"c":"ل","l":2,"m":3}]},{"pt":[{"c":"و","l":2}],"mPt":[{"c":"ف","l":0,"m":0},{"c":"ع","l":1,"m":1},{"c":"ل","l":2,"m":3}]},{"pt":[{"c":"ا","l":2}]},{"pt":[{"c":"ي","l":2}],"mPt":[{"c":"ف","l":0,"m":0},{"c":"ع","l":1,"m":1},{"c":"ا","l":2},{"c":"ل","l":3,"m":3}]},{"pt":[{"c":"م","l":0}]}],"pt53":[{"pt":[{"c":"ت","l":0},{"c":"ا","l":2}]},{"pt":[{"c":"ا,ن,ت,ي","l":0},{"c":"ت","l":2}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ت","l":2},{"c":"ع","l":3,"m":3},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"ا","l":0},{"c":"ا","l":2}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ع","l":2,"m":3},{"c":"ل","l":3,"m":4},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"ا","l":0},{"c":"ا","l":3}],"mPt":[{"c":"ف","l":0,"m":1},{"c":"ع","l":1,"m":2},{"c":"ل","l":2,"m":4}]},{"pt":[{"c":"ا","l":3},{"c":"ن","l":4}]},{"pt":[{"c":"ت","l":0},{"c":"ي","l":3}]},{"pt":[{"c":"م","l":0},{"c":"و","l":3}]},{"pt":[{"c":"ا","l":1},{"c":"و","l":3}]},{"pt":[{"c":"و","l":1},{"c":"ا","l":2}]},{"pt":[{"c":"م","l":0},{"c":"ا","l":3}]},{"pt":[{"c":"م","l":0},{"c":"ي","l":3}]},{"pt":[{"c":"ا","l":2},{"c":"ن","l":3}]},{"pt":[{"c":"م","l":0},{"c":"ن","l":1}],"mPt":[{"c":"ا","l":0},{"c":"ن","l":1},{"c":"ف","l":2,"m":2},{"c":"ع","l":3,"m":3},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"م","l":0},{"c":"ت","l":2}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ت","l":2},{"c":"ع","l":3,"m":3},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"م","l":0},{"c":"ا","l":2}]},{"pt":[{"c":"م","l":1},{"c":"ا","l":3}]},{"pt":[{"c":"ي,ت,ا,ن","l":0},{"c":"ت","l":1}],"mPt":[{"c":"ف","l":0,"m":2},{"c":"ع","l":1,"m":3},{"c":"ا","l":2},{"c":"ل","l":3,"m":4}]},{"pt":[{"c":"ت,ي,ا,ن","l":0},{"c":"ت","l":2}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ت","l":2},{"c":"ع","l":3,"m":3},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"ا","l":2},{"c":"ي","l":3}]},{"pt":[{"c":"ا,ي,ت,ن","l":0},{"c":"ن","l":1}],"mPt":[{"c":"ا","l":0},{"c":"ن","l":1},{"c":"ف","l":2,"m":2},{"c":"ع","l":3,"m":3},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"ا","l":3},{"c":"ء","l":4}]}],"pt63":[{"pt":[{"c":"ا","l":0},{"c":"ت","l":2},{"c":"ا","l":4}]},{"pt":[{"c":"ا,ت,ن,ي","l":0},{"c":"س","l":1},{"c":"ت","l":2}],"mPt":[{"c":"ا","l":0},{"c":"س","l":1},{"c":"ت","l":2},{"c":"ف","l":3,"m":3},{"c":"ع","l":4,"m":4},{"c":"ا","l":5},{"c":"ل","l":6,"m":5}]},{"pt":[{"c":"ا,ن,ت,ي","l":0},{"c":"و","l":3}]},{"pt":[{"c":"م","l":0},{"c":"س","l":1},{"c":"ت","l":2}],"mPt":[{"c":"ا","l":0},{"c":"س","l":1},{"c":"ت","l":2},{"c":"ف","l":3,"m":3},{"c":"ع","l":4,"m":4},{"c":"ا","l":5},{"c":"ل","l":6,"m":5}]},{"pt":[{"c":"ي","l":1},{"c":"ي","l":3},{"c":"ا","l":4},{"c":"ء","l":5}]},{"pt":[{"c":"ا","l":0},{"c":"ن","l":1},{"c":"ا","l":4}]}],"pt54":[{"pt":[{"c":"ت","l":0}]},{"pt":[{"c":"ا,ي,ت,ن","l":0}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ع","l":2,"m":2},{"c":"ل","l":3,"m":3},{"c":"ر","l":4,"m":4},{"c":"ا","l":5},{"c":"ر","l":6,"m":4}]},{"pt":[{"c":"م","l":0}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ع","l":2,"m":2},{"c":"ل","l":3,"m":3},{"c":"ر","l":4,"m":4},{"c":"ا","l":5},{"c":"ر","l":6,"m":4}]},{"pt":[{"c":"ا","l":2}]},{"pt":[{"c":"ا","l":0},{"c":"ن","l":2}]}],"pt64":[{"pt":[{"c":"ا","l":0},{"c":"ا","l":4}]},{"pt":[{"c":"م","l":0},{"c":"ت","l":1}]}],"pt73":[{"pt":[{"c":"ا","l":0},{"c":"س","l":1},{"c":"ت","l":2},{"c":"ا","l":5}]}],"pt75":[{"pt":[{"c":"ا","l":0},{"c":"ا","l":5}]}]}'),e.execArray=["cleanWord","removeDiacritics","cleanAlef","removeStopWords","normalizeHamzaAndAlef","removeStartWaw","removePre432","removeEndTaa","wordCheck"],e.stem=function(){var r=0;for(e.result=!1,e.preRemoved=!1,e.sufRemoved=!1;r=0)return!0},e.normalizeHamzaAndAlef=function(){return e.word=e.word.replace("ؤ","ء"),e.word=e.word.replace("ئ","ء"),e.word=e.word.replace(/([\u0627])\1+/gi,"ا"),!1},e.removeEndTaa=function(){return!(e.word.length>2)||(e.word=e.word.replace(/[\u0627]$/,""),e.word=e.word.replace("ة",""),!1)},e.removeStartWaw=function(){return e.word.length>3&&"و"==e.word[0]&&"و"==e.word[1]&&(e.word=e.word.slice(1)),!1},e.removePre432=function(){var r=e.word;if(e.word.length>=7){var t=new RegExp("^("+e.pre.pre4.split(" ").join("|")+")");e.word=e.word.replace(t,"")}if(e.word==r&&e.word.length>=6){var c=new RegExp("^("+e.pre.pre3.split(" ").join("|")+")");e.word=e.word.replace(c,"")}if(e.word==r&&e.word.length>=5){var l=new RegExp("^("+e.pre.pre2.split(" ").join("|")+")");e.word=e.word.replace(l,"")}return r!=e.word&&(e.preRemoved=!0),!1},e.patternCheck=function(r){for(var t=0;t3){var t=new RegExp("^("+e.pre.pre1.split(" ").join("|")+")");e.word=e.word.replace(t,"")}return r!=e.word&&(e.preRemoved=!0),!1},e.removeSuf1=function(){var r=e.word;if(0==e.sufRemoved&&e.word.length>3){var t=new RegExp("("+e.suf.suf1.split(" ").join("|")+")$");e.word=e.word.replace(t,"")}return r!=e.word&&(e.sufRemoved=!0),!1},e.removeSuf432=function(){var r=e.word;if(e.word.length>=6){var t=new RegExp("("+e.suf.suf4.split(" ").join("|")+")$");e.word=e.word.replace(t,"")}if(e.word==r&&e.word.length>=5){var c=new RegExp("("+e.suf.suf3.split(" ").join("|")+")$");e.word=e.word.replace(c,"")}if(e.word==r&&e.word.length>=4){var l=new RegExp("("+e.suf.suf2.split(" ").join("|")+")$");e.word=e.word.replace(l,"")}return r!=e.word&&(e.sufRemoved=!0),!1},e.wordCheck=function(){for(var r=(e.word,[e.removeSuf432,e.removeSuf1,e.removePre1]),t=0,c=!1;e.word.length>=7&&!e.result&&t=f.limit)return;f.cursor++}for(;!f.out_grouping(w,97,248);){if(f.cursor>=f.limit)return;f.cursor++}d=f.cursor,d=d&&(r=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,e=f.find_among_b(c,32),f.limit_backward=r,e))switch(f.bra=f.cursor,e){case 1:f.slice_del();break;case 2:f.in_grouping_b(p,97,229)&&f.slice_del()}}function t(){var e,r=f.limit-f.cursor;f.cursor>=d&&(e=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,f.find_among_b(l,4)?(f.bra=f.cursor,f.limit_backward=e,f.cursor=f.limit-r,f.cursor>f.limit_backward&&(f.cursor--,f.bra=f.cursor,f.slice_del())):f.limit_backward=e)}function s(){var e,r,i,n=f.limit-f.cursor;if(f.ket=f.cursor,f.eq_s_b(2,"st")&&(f.bra=f.cursor,f.eq_s_b(2,"ig")&&f.slice_del()),f.cursor=f.limit-n,f.cursor>=d&&(r=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,e=f.find_among_b(m,5),f.limit_backward=r,e))switch(f.bra=f.cursor,e){case 1:f.slice_del(),i=f.limit-f.cursor,t(),f.cursor=f.limit-i;break;case 2:f.slice_from("løs")}}function o(){var e;f.cursor>=d&&(e=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,f.out_grouping_b(w,97,248)?(f.bra=f.cursor,u=f.slice_to(u),f.limit_backward=e,f.eq_v_b(u)&&f.slice_del()):f.limit_backward=e)}var a,d,u,c=[new r("hed",-1,1),new r("ethed",0,1),new r("ered",-1,1),new r("e",-1,1),new r("erede",3,1),new r("ende",3,1),new r("erende",5,1),new r("ene",3,1),new r("erne",3,1),new r("ere",3,1),new r("en",-1,1),new r("heden",10,1),new r("eren",10,1),new r("er",-1,1),new r("heder",13,1),new r("erer",13,1),new r("s",-1,2),new r("heds",16,1),new r("es",16,1),new r("endes",18,1),new r("erendes",19,1),new r("enes",18,1),new r("ernes",18,1),new r("eres",18,1),new r("ens",16,1),new r("hedens",24,1),new r("erens",24,1),new r("ers",16,1),new r("ets",16,1),new r("erets",28,1),new r("et",-1,1),new r("eret",30,1)],l=[new r("gd",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1)],m=[new r("ig",-1,1),new r("lig",0,1),new r("elig",1,1),new r("els",-1,1),new r("løst",-1,2)],w=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],p=[239,254,42,3,0,0,0,0,0,0,0,0,0,0,0,0,16],f=new i;this.setCurrent=function(e){f.setCurrent(e)},this.getCurrent=function(){return f.getCurrent()},this.stem=function(){var r=f.cursor;return e(),f.limit_backward=r,f.cursor=f.limit,n(),f.cursor=f.limit,t(),f.cursor=f.limit,s(),f.cursor=f.limit,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.da.stemmer,"stemmer-da"),e.da.stopWordFilter=e.generateStopWordFilter("ad af alle alt anden at blev blive bliver da de dem den denne der deres det dette dig din disse dog du efter eller en end er et for fra ham han hans har havde have hende hendes her hos hun hvad hvis hvor i ikke ind jeg jer jo kunne man mange med meget men mig min mine mit mod ned noget nogle nu når og også om op os over på selv sig sin sine sit skal skulle som sådan thi til ud under var vi vil ville vor være været".split(" ")),e.Pipeline.registerFunction(e.da.stopWordFilter,"stopWordFilter-da")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.de.min.js b/assets/javascripts/lunr/min/lunr.de.min.js new file mode 100644 index 0000000000..f3b5c108c9 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.de.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `German` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.de=function(){this.pipeline.reset(),this.pipeline.add(e.de.trimmer,e.de.stopWordFilter,e.de.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.de.stemmer))},e.de.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.de.trimmer=e.trimmerSupport.generateTrimmer(e.de.wordCharacters),e.Pipeline.registerFunction(e.de.trimmer,"trimmer-de"),e.de.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){function e(e,r,n){return!(!v.eq_s(1,e)||(v.ket=v.cursor,!v.in_grouping(p,97,252)))&&(v.slice_from(r),v.cursor=n,!0)}function i(){for(var r,n,i,s,t=v.cursor;;)if(r=v.cursor,v.bra=r,v.eq_s(1,"ß"))v.ket=v.cursor,v.slice_from("ss");else{if(r>=v.limit)break;v.cursor=r+1}for(v.cursor=t;;)for(n=v.cursor;;){if(i=v.cursor,v.in_grouping(p,97,252)){if(s=v.cursor,v.bra=s,e("u","U",i))break;if(v.cursor=s,e("y","Y",i))break}if(i>=v.limit)return void(v.cursor=n);v.cursor=i+1}}function s(){for(;!v.in_grouping(p,97,252);){if(v.cursor>=v.limit)return!0;v.cursor++}for(;!v.out_grouping(p,97,252);){if(v.cursor>=v.limit)return!0;v.cursor++}return!1}function t(){m=v.limit,l=m;var e=v.cursor+3;0<=e&&e<=v.limit&&(d=e,s()||(m=v.cursor,m=v.limit)return;v.cursor++}}}function c(){return m<=v.cursor}function u(){return l<=v.cursor}function a(){var e,r,n,i,s=v.limit-v.cursor;if(v.ket=v.cursor,(e=v.find_among_b(w,7))&&(v.bra=v.cursor,c()))switch(e){case 1:v.slice_del();break;case 2:v.slice_del(),v.ket=v.cursor,v.eq_s_b(1,"s")&&(v.bra=v.cursor,v.eq_s_b(3,"nis")&&v.slice_del());break;case 3:v.in_grouping_b(g,98,116)&&v.slice_del()}if(v.cursor=v.limit-s,v.ket=v.cursor,(e=v.find_among_b(f,4))&&(v.bra=v.cursor,c()))switch(e){case 1:v.slice_del();break;case 2:if(v.in_grouping_b(k,98,116)){var t=v.cursor-3;v.limit_backward<=t&&t<=v.limit&&(v.cursor=t,v.slice_del())}}if(v.cursor=v.limit-s,v.ket=v.cursor,(e=v.find_among_b(_,8))&&(v.bra=v.cursor,u()))switch(e){case 1:v.slice_del(),v.ket=v.cursor,v.eq_s_b(2,"ig")&&(v.bra=v.cursor,r=v.limit-v.cursor,v.eq_s_b(1,"e")||(v.cursor=v.limit-r,u()&&v.slice_del()));break;case 2:n=v.limit-v.cursor,v.eq_s_b(1,"e")||(v.cursor=v.limit-n,v.slice_del());break;case 3:if(v.slice_del(),v.ket=v.cursor,i=v.limit-v.cursor,!v.eq_s_b(2,"er")&&(v.cursor=v.limit-i,!v.eq_s_b(2,"en")))break;v.bra=v.cursor,c()&&v.slice_del();break;case 4:v.slice_del(),v.ket=v.cursor,e=v.find_among_b(b,2),e&&(v.bra=v.cursor,u()&&1==e&&v.slice_del())}}var d,l,m,h=[new r("",-1,6),new r("U",0,2),new r("Y",0,1),new r("ä",0,3),new r("ö",0,4),new r("ü",0,5)],w=[new r("e",-1,2),new r("em",-1,1),new r("en",-1,2),new r("ern",-1,1),new r("er",-1,1),new r("s",-1,3),new r("es",5,2)],f=[new r("en",-1,1),new r("er",-1,1),new r("st",-1,2),new r("est",2,1)],b=[new r("ig",-1,1),new r("lich",-1,1)],_=[new r("end",-1,1),new r("ig",-1,2),new r("ung",-1,1),new r("lich",-1,3),new r("isch",-1,2),new r("ik",-1,2),new r("heit",-1,3),new r("keit",-1,4)],p=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32,8],g=[117,30,5],k=[117,30,4],v=new n;this.setCurrent=function(e){v.setCurrent(e)},this.getCurrent=function(){return v.getCurrent()},this.stem=function(){var e=v.cursor;return i(),v.cursor=e,t(),v.limit_backward=e,v.cursor=v.limit,a(),v.cursor=v.limit_backward,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.de.stemmer,"stemmer-de"),e.de.stopWordFilter=e.generateStopWordFilter("aber alle allem allen aller alles als also am an ander andere anderem anderen anderer anderes anderm andern anderr anders auch auf aus bei bin bis bist da damit dann das dasselbe dazu daß dein deine deinem deinen deiner deines dem demselben den denn denselben der derer derselbe derselben des desselben dessen dich die dies diese dieselbe dieselben diesem diesen dieser dieses dir doch dort du durch ein eine einem einen einer eines einig einige einigem einigen einiger einiges einmal er es etwas euch euer eure eurem euren eurer eures für gegen gewesen hab habe haben hat hatte hatten hier hin hinter ich ihm ihn ihnen ihr ihre ihrem ihren ihrer ihres im in indem ins ist jede jedem jeden jeder jedes jene jenem jenen jener jenes jetzt kann kein keine keinem keinen keiner keines können könnte machen man manche manchem manchen mancher manches mein meine meinem meinen meiner meines mich mir mit muss musste nach nicht nichts noch nun nur ob oder ohne sehr sein seine seinem seinen seiner seines selbst sich sie sind so solche solchem solchen solcher solches soll sollte sondern sonst um und uns unse unsem unsen unser unses unter viel vom von vor war waren warst was weg weil weiter welche welchem welchen welcher welches wenn werde werden wie wieder will wir wird wirst wo wollen wollte während würde würden zu zum zur zwar zwischen über".split(" ")),e.Pipeline.registerFunction(e.de.stopWordFilter,"stopWordFilter-de")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.du.min.js b/assets/javascripts/lunr/min/lunr.du.min.js new file mode 100644 index 0000000000..49a0f3f0ac --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.du.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Dutch` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");console.warn('[Lunr Languages] Please use the "nl" instead of the "du". The "nl" code is the standard code for Dutch language, and "du" will be removed in the next major versions.'),e.du=function(){this.pipeline.reset(),this.pipeline.add(e.du.trimmer,e.du.stopWordFilter,e.du.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.du.stemmer))},e.du.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.du.trimmer=e.trimmerSupport.generateTrimmer(e.du.wordCharacters),e.Pipeline.registerFunction(e.du.trimmer,"trimmer-du"),e.du.stemmer=function(){var r=e.stemmerSupport.Among,i=e.stemmerSupport.SnowballProgram,n=new function(){function e(){for(var e,r,i,o=C.cursor;;){if(C.bra=C.cursor,e=C.find_among(b,11))switch(C.ket=C.cursor,e){case 1:C.slice_from("a");continue;case 2:C.slice_from("e");continue;case 3:C.slice_from("i");continue;case 4:C.slice_from("o");continue;case 5:C.slice_from("u");continue;case 6:if(C.cursor>=C.limit)break;C.cursor++;continue}break}for(C.cursor=o,C.bra=o,C.eq_s(1,"y")?(C.ket=C.cursor,C.slice_from("Y")):C.cursor=o;;)if(r=C.cursor,C.in_grouping(q,97,232)){if(i=C.cursor,C.bra=i,C.eq_s(1,"i"))C.ket=C.cursor,C.in_grouping(q,97,232)&&(C.slice_from("I"),C.cursor=r);else if(C.cursor=i,C.eq_s(1,"y"))C.ket=C.cursor,C.slice_from("Y"),C.cursor=r;else if(n(r))break}else if(n(r))break}function n(e){return C.cursor=e,e>=C.limit||(C.cursor++,!1)}function o(){_=C.limit,f=_,t()||(_=C.cursor,_<3&&(_=3),t()||(f=C.cursor))}function t(){for(;!C.in_grouping(q,97,232);){if(C.cursor>=C.limit)return!0;C.cursor++}for(;!C.out_grouping(q,97,232);){if(C.cursor>=C.limit)return!0;C.cursor++}return!1}function s(){for(var e;;)if(C.bra=C.cursor,e=C.find_among(p,3))switch(C.ket=C.cursor,e){case 1:C.slice_from("y");break;case 2:C.slice_from("i");break;case 3:if(C.cursor>=C.limit)return;C.cursor++}}function u(){return _<=C.cursor}function c(){return f<=C.cursor}function a(){var e=C.limit-C.cursor;C.find_among_b(g,3)&&(C.cursor=C.limit-e,C.ket=C.cursor,C.cursor>C.limit_backward&&(C.cursor--,C.bra=C.cursor,C.slice_del()))}function l(){var e;w=!1,C.ket=C.cursor,C.eq_s_b(1,"e")&&(C.bra=C.cursor,u()&&(e=C.limit-C.cursor,C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-e,C.slice_del(),w=!0,a())))}function m(){var e;u()&&(e=C.limit-C.cursor,C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-e,C.eq_s_b(3,"gem")||(C.cursor=C.limit-e,C.slice_del(),a())))}function d(){var e,r,i,n,o,t,s=C.limit-C.cursor;if(C.ket=C.cursor,e=C.find_among_b(h,5))switch(C.bra=C.cursor,e){case 1:u()&&C.slice_from("heid");break;case 2:m();break;case 3:u()&&C.out_grouping_b(z,97,232)&&C.slice_del()}if(C.cursor=C.limit-s,l(),C.cursor=C.limit-s,C.ket=C.cursor,C.eq_s_b(4,"heid")&&(C.bra=C.cursor,c()&&(r=C.limit-C.cursor,C.eq_s_b(1,"c")||(C.cursor=C.limit-r,C.slice_del(),C.ket=C.cursor,C.eq_s_b(2,"en")&&(C.bra=C.cursor,m())))),C.cursor=C.limit-s,C.ket=C.cursor,e=C.find_among_b(k,6))switch(C.bra=C.cursor,e){case 1:if(c()){if(C.slice_del(),i=C.limit-C.cursor,C.ket=C.cursor,C.eq_s_b(2,"ig")&&(C.bra=C.cursor,c()&&(n=C.limit-C.cursor,!C.eq_s_b(1,"e")))){C.cursor=C.limit-n,C.slice_del();break}C.cursor=C.limit-i,a()}break;case 2:c()&&(o=C.limit-C.cursor,C.eq_s_b(1,"e")||(C.cursor=C.limit-o,C.slice_del()));break;case 3:c()&&(C.slice_del(),l());break;case 4:c()&&C.slice_del();break;case 5:c()&&w&&C.slice_del()}C.cursor=C.limit-s,C.out_grouping_b(j,73,232)&&(t=C.limit-C.cursor,C.find_among_b(v,4)&&C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-t,C.ket=C.cursor,C.cursor>C.limit_backward&&(C.cursor--,C.bra=C.cursor,C.slice_del())))}var f,_,w,b=[new r("",-1,6),new r("á",0,1),new r("ä",0,1),new r("é",0,2),new r("ë",0,2),new r("í",0,3),new r("ï",0,3),new r("ó",0,4),new r("ö",0,4),new r("ú",0,5),new r("ü",0,5)],p=[new r("",-1,3),new r("I",0,2),new r("Y",0,1)],g=[new r("dd",-1,-1),new r("kk",-1,-1),new r("tt",-1,-1)],h=[new r("ene",-1,2),new r("se",-1,3),new r("en",-1,2),new r("heden",2,1),new r("s",-1,3)],k=[new r("end",-1,1),new r("ig",-1,2),new r("ing",-1,1),new r("lijk",-1,3),new r("baar",-1,4),new r("bar",-1,5)],v=[new r("aa",-1,-1),new r("ee",-1,-1),new r("oo",-1,-1),new r("uu",-1,-1)],q=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],j=[1,0,0,17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],z=[17,67,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],C=new i;this.setCurrent=function(e){C.setCurrent(e)},this.getCurrent=function(){return C.getCurrent()},this.stem=function(){var r=C.cursor;return e(),C.cursor=r,o(),C.limit_backward=r,C.cursor=C.limit,d(),C.cursor=C.limit_backward,s(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.du.stemmer,"stemmer-du"),e.du.stopWordFilter=e.generateStopWordFilter(" aan al alles als altijd andere ben bij daar dan dat de der deze die dit doch doen door dus een eens en er ge geen geweest haar had heb hebben heeft hem het hier hij hoe hun iemand iets ik in is ja je kan kon kunnen maar me meer men met mij mijn moet na naar niet niets nog nu of om omdat onder ons ook op over reeds te tegen toch toen tot u uit uw van veel voor want waren was wat werd wezen wie wil worden wordt zal ze zelf zich zij zijn zo zonder zou".split(" ")),e.Pipeline.registerFunction(e.du.stopWordFilter,"stopWordFilter-du")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.el.min.js b/assets/javascripts/lunr/min/lunr.el.min.js new file mode 100644 index 0000000000..ace017bd65 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.el.min.js @@ -0,0 +1 @@ +!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.el=function(){this.pipeline.reset(),void 0===this.searchPipeline&&this.pipeline.add(e.el.trimmer,e.el.normilizer),this.pipeline.add(e.el.stopWordFilter,e.el.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.el.stemmer))},e.el.wordCharacters="A-Za-zΑαΒβΓγΔδΕεΖζΗηΘθΙιΚκΛλΜμΝνΞξΟοΠπΡρΣσςΤτΥυΦφΧχΨψΩωΆάΈέΉήΊίΌόΎύΏώΪΐΫΰΐΰ",e.el.trimmer=e.trimmerSupport.generateTrimmer(e.el.wordCharacters),e.Pipeline.registerFunction(e.el.trimmer,"trimmer-el"),e.el.stemmer=function(){function e(e){return s.test(e)}function t(e){return/[ΑΕΗΙΟΥΩ]$/.test(e)}function r(e){return/[ΑΕΗΙΟΩ]$/.test(e)}function n(n){var s=n;if(n.length<3)return s;if(!e(n))return s;if(i.indexOf(n)>=0)return s;var u=new RegExp("(.*)("+Object.keys(l).join("|")+")$"),o=u.exec(s);return null!==o&&(s=o[1]+l[o[2]]),null!==(o=/^(.+?)(ΑΔΕΣ|ΑΔΩΝ)$/.exec(s))&&(s=o[1],/(ΟΚ|ΜΑΜ|ΜΑΝ|ΜΠΑΜΠ|ΠΑΤΕΡ|ΓΙΑΓΙ|ΝΤΑΝΤ|ΚΥΡ|ΘΕΙ|ΠΕΘΕΡ|ΜΟΥΣΑΜ|ΚΑΠΛΑΜ|ΠΑΡ|ΨΑΡ|ΤΖΟΥΡ|ΤΑΜΠΟΥΡ|ΓΑΛΑΤ|ΦΑΦΛΑΤ)$/.test(o[1])||(s+="ΑΔ")),null!==(o=/^(.+?)(ΕΔΕΣ|ΕΔΩΝ)$/.exec(s))&&(s=o[1],/(ΟΠ|ΙΠ|ΕΜΠ|ΥΠ|ΓΗΠ|ΔΑΠ|ΚΡΑΣΠ|ΜΙΛ)$/.test(o[1])&&(s+="ΕΔ")),null!==(o=/^(.+?)(ΟΥΔΕΣ|ΟΥΔΩΝ)$/.exec(s))&&(s=o[1],/(ΑΡΚ|ΚΑΛΙΑΚ|ΠΕΤΑΛ|ΛΙΧ|ΠΛΕΞ|ΣΚ|Σ|ΦΛ|ΦΡ|ΒΕΛ|ΛΟΥΛ|ΧΝ|ΣΠ|ΤΡΑΓ|ΦΕ)$/.test(o[1])&&(s+="ΟΥΔ")),null!==(o=/^(.+?)(ΕΩΣ|ΕΩΝ|ΕΑΣ|ΕΑ)$/.exec(s))&&(s=o[1],/^(Θ|Δ|ΕΛ|ΓΑΛ|Ν|Π|ΙΔ|ΠΑΡ|ΣΤΕΡ|ΟΡΦ|ΑΝΔΡ|ΑΝΤΡ)$/.test(o[1])&&(s+="Ε")),null!==(o=/^(.+?)(ΕΙΟ|ΕΙΟΣ|ΕΙΟΙ|ΕΙΑ|ΕΙΑΣ|ΕΙΕΣ|ΕΙΟΥ|ΕΙΟΥΣ|ΕΙΩΝ)$/.exec(s))&&o[1].length>4&&(s=o[1]),null!==(o=/^(.+?)(ΙΟΥΣ|ΙΑΣ|ΙΕΣ|ΙΟΣ|ΙΟΥ|ΙΟΙ|ΙΩΝ|ΙΟΝ|ΙΑ|ΙΟ)$/.exec(s))&&(s=o[1],(t(s)||s.length<2||/^(ΑΓ|ΑΓΓΕΛ|ΑΓΡ|ΑΕΡ|ΑΘΛ|ΑΚΟΥΣ|ΑΞ|ΑΣ|Β|ΒΙΒΛ|ΒΥΤ|Γ|ΓΙΑΓ|ΓΩΝ|Δ|ΔΑΝ|ΔΗΛ|ΔΗΜ|ΔΟΚΙΜ|ΕΛ|ΖΑΧΑΡ|ΗΛ|ΗΠ|ΙΔ|ΙΣΚ|ΙΣΤ|ΙΟΝ|ΙΩΝ|ΚΙΜΩΛ|ΚΟΛΟΝ|ΚΟΡ|ΚΤΗΡ|ΚΥΡ|ΛΑΓ|ΛΟΓ|ΜΑΓ|ΜΠΑΝ|ΜΠΡ|ΝΑΥΤ|ΝΟΤ|ΟΠΑΛ|ΟΞ|ΟΡ|ΟΣ|ΠΑΝΑΓ|ΠΑΤΡ|ΠΗΛ|ΠΗΝ|ΠΛΑΙΣ|ΠΟΝΤ|ΡΑΔ|ΡΟΔ|ΣΚ|ΣΚΟΡΠ|ΣΟΥΝ|ΣΠΑΝ|ΣΤΑΔ|ΣΥΡ|ΤΗΛ|ΤΙΜ|ΤΟΚ|ΤΟΠ|ΤΡΟΧ|ΦΙΛ|ΦΩΤ|Χ|ΧΙΛ|ΧΡΩΜ|ΧΩΡ)$/.test(o[1]))&&(s+="Ι"),/^(ΠΑΛ)$/.test(o[1])&&(s+="ΑΙ")),null!==(o=/^(.+?)(ΙΚΟΣ|ΙΚΟΝ|ΙΚΕΙΣ|ΙΚΟΙ|ΙΚΕΣ|ΙΚΟΥΣ|ΙΚΗ|ΙΚΗΣ|ΙΚΟ|ΙΚΑ|ΙΚΟΥ|ΙΚΩΝ|ΙΚΩΣ)$/.exec(s))&&(s=o[1],(t(s)||/^(ΑΔ|ΑΛ|ΑΜΑΝ|ΑΜΕΡ|ΑΜΜΟΧΑΛ|ΑΝΗΘ|ΑΝΤΙΔ|ΑΠΛ|ΑΤΤ|ΑΦΡ|ΒΑΣ|ΒΡΩΜ|ΓΕΝ|ΓΕΡ|Δ|ΔΙΚΑΝ|ΔΥΤ|ΕΙΔ|ΕΝΔ|ΕΞΩΔ|ΗΘ|ΘΕΤ|ΚΑΛΛΙΝ|ΚΑΛΠ|ΚΑΤΑΔ|ΚΟΥΖΙΝ|ΚΡ|ΚΩΔ|ΛΟΓ|Μ|ΜΕΡ|ΜΟΝΑΔ|ΜΟΥΛ|ΜΟΥΣ|ΜΠΑΓΙΑΤ|ΜΠΑΝ|ΜΠΟΛ|ΜΠΟΣ|ΜΥΣΤ|Ν|ΝΙΤ|ΞΙΚ|ΟΠΤ|ΠΑΝ|ΠΕΤΣ|ΠΙΚΑΝΤ|ΠΙΤΣ|ΠΛΑΣΤ|ΠΛΙΑΤΣ|ΠΟΝΤ|ΠΟΣΤΕΛΝ|ΠΡΩΤΟΔ|ΣΕΡΤ|ΣΗΜΑΝΤ|ΣΤΑΤ|ΣΥΝΑΔ|ΣΥΝΟΜΗΛ|ΤΕΛ|ΤΕΧΝ|ΤΡΟΠ|ΤΣΑΜ|ΥΠΟΔ|Φ|ΦΙΛΟΝ|ΦΥΛΟΔ|ΦΥΣ|ΧΑΣ)$/.test(o[1])||/(ΦΟΙΝ)$/.test(o[1]))&&(s+="ΙΚ")),"ΑΓΑΜΕ"===s&&(s="ΑΓΑΜ"),null!==(o=/^(.+?)(ΑΓΑΜΕ|ΗΣΑΜΕ|ΟΥΣΑΜΕ|ΗΚΑΜΕ|ΗΘΗΚΑΜΕ)$/.exec(s))&&(s=o[1]),null!==(o=/^(.+?)(ΑΜΕ)$/.exec(s))&&(s=o[1],/^(ΑΝΑΠ|ΑΠΟΘ|ΑΠΟΚ|ΑΠΟΣΤ|ΒΟΥΒ|ΞΕΘ|ΟΥΛ|ΠΕΘ|ΠΙΚΡ|ΠΟΤ|ΣΙΧ|Χ)$/.test(o[1])&&(s+="ΑΜ")),null!==(o=/^(.+?)(ΑΓΑΝΕ|ΗΣΑΝΕ|ΟΥΣΑΝΕ|ΙΟΝΤΑΝΕ|ΙΟΤΑΝΕ|ΙΟΥΝΤΑΝΕ|ΟΝΤΑΝΕ|ΟΤΑΝΕ|ΟΥΝΤΑΝΕ|ΗΚΑΝΕ|ΗΘΗΚΑΝΕ)$/.exec(s))&&(s=o[1],/^(ΤΡ|ΤΣ)$/.test(o[1])&&(s+="ΑΓΑΝ")),null!==(o=/^(.+?)(ΑΝΕ)$/.exec(s))&&(s=o[1],(r(s)||/^(ΒΕΤΕΡ|ΒΟΥΛΚ|ΒΡΑΧΜ|Γ|ΔΡΑΔΟΥΜ|Θ|ΚΑΛΠΟΥΖ|ΚΑΣΤΕΛ|ΚΟΡΜΟΡ|ΛΑΟΠΛ|ΜΩΑΜΕΘ|Μ|ΜΟΥΣΟΥΛΜΑΝ|ΟΥΛ|Π|ΠΕΛΕΚ|ΠΛ|ΠΟΛΙΣ|ΠΟΡΤΟΛ|ΣΑΡΑΚΑΤΣ|ΣΟΥΛΤ|ΤΣΑΡΛΑΤ|ΟΡΦ|ΤΣΙΓΓ|ΤΣΟΠ|ΦΩΤΟΣΤΕΦ|Χ|ΨΥΧΟΠΛ|ΑΓ|ΟΡΦ|ΓΑΛ|ΓΕΡ|ΔΕΚ|ΔΙΠΛ|ΑΜΕΡΙΚΑΝ|ΟΥΡ|ΠΙΘ|ΠΟΥΡΙΤ|Σ|ΖΩΝΤ|ΙΚ|ΚΑΣΤ|ΚΟΠ|ΛΙΧ|ΛΟΥΘΗΡ|ΜΑΙΝΤ|ΜΕΛ|ΣΙΓ|ΣΠ|ΣΤΕΓ|ΤΡΑΓ|ΤΣΑΓ|Φ|ΕΡ|ΑΔΑΠ|ΑΘΙΓΓ|ΑΜΗΧ|ΑΝΙΚ|ΑΝΟΡΓ|ΑΠΗΓ|ΑΠΙΘ|ΑΤΣΙΓΓ|ΒΑΣ|ΒΑΣΚ|ΒΑΘΥΓΑΛ|ΒΙΟΜΗΧ|ΒΡΑΧΥΚ|ΔΙΑΤ|ΔΙΑΦ|ΕΝΟΡΓ|ΘΥΣ|ΚΑΠΝΟΒΙΟΜΗΧ|ΚΑΤΑΓΑΛ|ΚΛΙΒ|ΚΟΙΛΑΡΦ|ΛΙΒ|ΜΕΓΛΟΒΙΟΜΗΧ|ΜΙΚΡΟΒΙΟΜΗΧ|ΝΤΑΒ|ΞΗΡΟΚΛΙΒ|ΟΛΙΓΟΔΑΜ|ΟΛΟΓΑΛ|ΠΕΝΤΑΡΦ|ΠΕΡΗΦ|ΠΕΡΙΤΡ|ΠΛΑΤ|ΠΟΛΥΔΑΠ|ΠΟΛΥΜΗΧ|ΣΤΕΦ|ΤΑΒ|ΤΕΤ|ΥΠΕΡΗΦ|ΥΠΟΚΟΠ|ΧΑΜΗΛΟΔΑΠ|ΨΗΛΟΤΑΒ)$/.test(o[1]))&&(s+="ΑΝ")),null!==(o=/^(.+?)(ΗΣΕΤΕ)$/.exec(s))&&(s=o[1]),null!==(o=/^(.+?)(ΕΤΕ)$/.exec(s))&&(s=o[1],(r(s)||/(ΟΔ|ΑΙΡ|ΦΟΡ|ΤΑΘ|ΔΙΑΘ|ΣΧ|ΕΝΔ|ΕΥΡ|ΤΙΘ|ΥΠΕΡΘ|ΡΑΘ|ΕΝΘ|ΡΟΘ|ΣΘ|ΠΥΡ|ΑΙΝ|ΣΥΝΔ|ΣΥΝ|ΣΥΝΘ|ΧΩΡ|ΠΟΝ|ΒΡ|ΚΑΘ|ΕΥΘ|ΕΚΘ|ΝΕΤ|ΡΟΝ|ΑΡΚ|ΒΑΡ|ΒΟΛ|ΩΦΕΛ)$/.test(o[1])||/^(ΑΒΑΡ|ΒΕΝ|ΕΝΑΡ|ΑΒΡ|ΑΔ|ΑΘ|ΑΝ|ΑΠΛ|ΒΑΡΟΝ|ΝΤΡ|ΣΚ|ΚΟΠ|ΜΠΟΡ|ΝΙΦ|ΠΑΓ|ΠΑΡΑΚΑΛ|ΣΕΡΠ|ΣΚΕΛ|ΣΥΡΦ|ΤΟΚ|Υ|Δ|ΕΜ|ΘΑΡΡ|Θ)$/.test(o[1]))&&(s+="ΕΤ")),null!==(o=/^(.+?)(ΟΝΤΑΣ|ΩΝΤΑΣ)$/.exec(s))&&(s=o[1],/^ΑΡΧ$/.test(o[1])&&(s+="ΟΝΤ"),/ΚΡΕ$/.test(o[1])&&(s+="ΩΝΤ")),null!==(o=/^(.+?)(ΟΜΑΣΤΕ|ΙΟΜΑΣΤΕ)$/.exec(s))&&(s=o[1],/^ΟΝ$/.test(o[1])&&(s+="ΟΜΑΣΤ")),null!==(o=/^(.+?)(ΙΕΣΤΕ)$/.exec(s))&&(s=o[1],/^(Π|ΑΠ|ΣΥΜΠ|ΑΣΥΜΠ|ΑΚΑΤΑΠ|ΑΜΕΤΑΜΦ)$/.test(o[1])&&(s+="ΙΕΣΤ")),null!==(o=/^(.+?)(ΕΣΤΕ)$/.exec(s))&&(s=o[1],/^(ΑΛ|ΑΡ|ΕΚΤΕΛ|Ζ|Μ|Ξ|ΠΑΡΑΚΑΛ|ΠΡΟ|ΝΙΣ)$/.test(o[1])&&(s+="ΕΣΤ")),null!==(o=/^(.+?)(ΗΘΗΚΑ|ΗΘΗΚΕΣ|ΗΘΗΚΕ)$/.exec(s))&&(s=o[1]),null!==(o=/^(.+?)(ΗΚΑ|ΗΚΕΣ|ΗΚΕ)$/.exec(s))&&(s=o[1],(/(ΣΚΩΛ|ΣΚΟΥΛ|ΝΑΡΘ|ΣΦ|ΟΘ|ΠΙΘ)$/.test(o[1])||/^(ΔΙΑΘ|Θ|ΠΑΡΑΚΑΤΑΘ|ΠΡΟΣΘ|ΣΥΝΘ)$/.test(o[1]))&&(s+="ΗΚ")),null!==(o=/^(.+?)(ΟΥΣΑ|ΟΥΣΕΣ|ΟΥΣΕ)$/.exec(s))&&(s=o[1],(t(s)||/^(ΦΑΡΜΑΚ|ΧΑΔ|ΑΓΚ|ΑΝΑΡΡ|ΒΡΟΜ|ΕΚΛΙΠ|ΛΑΜΠΙΔ|ΛΕΧ|Μ|ΠΑΤ|Ρ|Λ|ΜΕΔ|ΜΕΣΑΖ|ΥΠΟΤΕΙΝ|ΑΜ|ΑΙΘ|ΑΝΗΚ|ΔΕΣΠΟΖ|ΕΝΔΙΑΦΕΡ)$/.test(o[1])||/(ΠΟΔΑΡ|ΒΛΕΠ|ΠΑΝΤΑΧ|ΦΡΥΔ|ΜΑΝΤΙΛ|ΜΑΛΛ|ΚΥΜΑΤ|ΛΑΧ|ΛΗΓ|ΦΑΓ|ΟΜ|ΠΡΩΤ)$/.test(o[1]))&&(s+="ΟΥΣ")),null!==(o=/^(.+?)(ΑΓΑ|ΑΓΕΣ|ΑΓΕ)$/.exec(s))&&(s=o[1],(/^(ΑΒΑΣΤ|ΠΟΛΥΦ|ΑΔΗΦ|ΠΑΜΦ|Ρ|ΑΣΠ|ΑΦ|ΑΜΑΛ|ΑΜΑΛΛΙ|ΑΝΥΣΤ|ΑΠΕΡ|ΑΣΠΑΡ|ΑΧΑΡ|ΔΕΡΒΕΝ|ΔΡΟΣΟΠ|ΞΕΦ|ΝΕΟΠ|ΝΟΜΟΤ|ΟΛΟΠ|ΟΜΟΤ|ΠΡΟΣΤ|ΠΡΟΣΩΠΟΠ|ΣΥΜΠ|ΣΥΝΤ|Τ|ΥΠΟΤ|ΧΑΡ|ΑΕΙΠ|ΑΙΜΟΣΤ|ΑΝΥΠ|ΑΠΟΤ|ΑΡΤΙΠ|ΔΙΑΤ|ΕΝ|ΕΠΙΤ|ΚΡΟΚΑΛΟΠ|ΣΙΔΗΡΟΠ|Λ|ΝΑΥ|ΟΥΛΑΜ|ΟΥΡ|Π|ΤΡ|Μ)$/.test(o[1])||/(ΟΦ|ΠΕΛ|ΧΟΡΤ|ΛΛ|ΣΦ|ΡΠ|ΦΡ|ΠΡ|ΛΟΧ|ΣΜΗΝ)$/.test(o[1])&&!/^(ΨΟΦ|ΝΑΥΛΟΧ)$/.test(o[1])||/(ΚΟΛΛ)$/.test(o[1]))&&(s+="ΑΓ")),null!==(o=/^(.+?)(ΗΣΕ|ΗΣΟΥ|ΗΣΑ)$/.exec(s))&&(s=o[1],/^(Ν|ΧΕΡΣΟΝ|ΔΩΔΕΚΑΝ|ΕΡΗΜΟΝ|ΜΕΓΑΛΟΝ|ΕΠΤΑΝ|Ι)$/.test(o[1])&&(s+="ΗΣ")),null!==(o=/^(.+?)(ΗΣΤΕ)$/.exec(s))&&(s=o[1],/^(ΑΣΒ|ΣΒ|ΑΧΡ|ΧΡ|ΑΠΛ|ΑΕΙΜΝ|ΔΥΣΧΡ|ΕΥΧΡ|ΚΟΙΝΟΧΡ|ΠΑΛΙΜΨ)$/.test(o[1])&&(s+="ΗΣΤ")),null!==(o=/^(.+?)(ΟΥΝΕ|ΗΣΟΥΝΕ|ΗΘΟΥΝΕ)$/.exec(s))&&(s=o[1],/^(Ν|Ρ|ΣΠΙ|ΣΤΡΑΒΟΜΟΥΤΣ|ΚΑΚΟΜΟΥΤΣ|ΕΞΩΝ)$/.test(o[1])&&(s+="ΟΥΝ")),null!==(o=/^(.+?)(ΟΥΜΕ|ΗΣΟΥΜΕ|ΗΘΟΥΜΕ)$/.exec(s))&&(s=o[1],/^(ΠΑΡΑΣΟΥΣ|Φ|Χ|ΩΡΙΟΠΛ|ΑΖ|ΑΛΛΟΣΟΥΣ|ΑΣΟΥΣ)$/.test(o[1])&&(s+="ΟΥΜ")),null!=(o=/^(.+?)(ΜΑΤΟΙ|ΜΑΤΟΥΣ|ΜΑΤΟ|ΜΑΤΑ|ΜΑΤΩΣ|ΜΑΤΩΝ|ΜΑΤΟΣ|ΜΑΤΕΣ|ΜΑΤΗ|ΜΑΤΗΣ|ΜΑΤΟΥ)$/.exec(s))&&(s=o[1]+"Μ",/^(ΓΡΑΜ)$/.test(o[1])?s+="Α":/^(ΓΕ|ΣΤΑ)$/.test(o[1])&&(s+="ΑΤ")),null!==(o=/^(.+?)(ΟΥΑ)$/.exec(s))&&(s=o[1]+"ΟΥ"),n.length===s.length&&null!==(o=/^(.+?)(Α|ΑΓΑΤΕ|ΑΓΑΝ|ΑΕΙ|ΑΜΑΙ|ΑΝ|ΑΣ|ΑΣΑΙ|ΑΤΑΙ|ΑΩ|Ε|ΕΙ|ΕΙΣ|ΕΙΤΕ|ΕΣΑΙ|ΕΣ|ΕΤΑΙ|Ι|ΙΕΜΑΙ|ΙΕΜΑΣΤΕ|ΙΕΤΑΙ|ΙΕΣΑΙ|ΙΕΣΑΣΤΕ|ΙΟΜΑΣΤΑΝ|ΙΟΜΟΥΝ|ΙΟΜΟΥΝΑ|ΙΟΝΤΑΝ|ΙΟΝΤΟΥΣΑΝ|ΙΟΣΑΣΤΑΝ|ΙΟΣΑΣΤΕ|ΙΟΣΟΥΝ|ΙΟΣΟΥΝΑ|ΙΟΤΑΝ|ΙΟΥΜΑ|ΙΟΥΜΑΣΤΕ|ΙΟΥΝΤΑΙ|ΙΟΥΝΤΑΝ|Η|ΗΔΕΣ|ΗΔΩΝ|ΗΘΕΙ|ΗΘΕΙΣ|ΗΘΕΙΤΕ|ΗΘΗΚΑΤΕ|ΗΘΗΚΑΝ|ΗΘΟΥΝ|ΗΘΩ|ΗΚΑΤΕ|ΗΚΑΝ|ΗΣ|ΗΣΑΝ|ΗΣΑΤΕ|ΗΣΕΙ|ΗΣΕΣ|ΗΣΟΥΝ|ΗΣΩ|Ο|ΟΙ|ΟΜΑΙ|ΟΜΑΣΤΑΝ|ΟΜΟΥΝ|ΟΜΟΥΝΑ|ΟΝΤΑΙ|ΟΝΤΑΝ|ΟΝΤΟΥΣΑΝ|ΟΣ|ΟΣΑΣΤΑΝ|ΟΣΑΣΤΕ|ΟΣΟΥΝ|ΟΣΟΥΝΑ|ΟΤΑΝ|ΟΥ|ΟΥΜΑΙ|ΟΥΜΑΣΤΕ|ΟΥΝ|ΟΥΝΤΑΙ|ΟΥΝΤΑΝ|ΟΥΣ|ΟΥΣΑΝ|ΟΥΣΑΤΕ|Υ||ΥΑ|ΥΣ|Ω|ΩΝ|ΟΙΣ)$/.exec(s))&&(s=o[1]),null!=(o=/^(.+?)(ΕΣΤΕΡ|ΕΣΤΑΤ|ΟΤΕΡ|ΟΤΑΤ|ΥΤΕΡ|ΥΤΑΤ|ΩΤΕΡ|ΩΤΑΤ)$/.exec(s))&&(/^(ΕΞ|ΕΣ|ΑΝ|ΚΑΤ|Κ|ΠΡ)$/.test(o[1])||(s=o[1]),/^(ΚΑ|Μ|ΕΛΕ|ΛΕ|ΔΕ)$/.test(o[1])&&(s+="ΥΤ")),s}var l={"ΦΑΓΙΑ":"ΦΑ","ΦΑΓΙΟΥ":"ΦΑ","ΦΑΓΙΩΝ":"ΦΑ","ΣΚΑΓΙΑ":"ΣΚΑ","ΣΚΑΓΙΟΥ":"ΣΚΑ","ΣΚΑΓΙΩΝ":"ΣΚΑ","ΣΟΓΙΟΥ":"ΣΟ","ΣΟΓΙΑ":"ΣΟ","ΣΟΓΙΩΝ":"ΣΟ","ΤΑΤΟΓΙΑ":"ΤΑΤΟ","ΤΑΤΟΓΙΟΥ":"ΤΑΤΟ","ΤΑΤΟΓΙΩΝ":"ΤΑΤΟ","ΚΡΕΑΣ":"ΚΡΕ","ΚΡΕΑΤΟΣ":"ΚΡΕ","ΚΡΕΑΤΑ":"ΚΡΕ","ΚΡΕΑΤΩΝ":"ΚΡΕ","ΠΕΡΑΣ":"ΠΕΡ","ΠΕΡΑΤΟΣ":"ΠΕΡ","ΠΕΡΑΤΑ":"ΠΕΡ","ΠΕΡΑΤΩΝ":"ΠΕΡ","ΤΕΡΑΣ":"ΤΕΡ","ΤΕΡΑΤΟΣ":"ΤΕΡ","ΤΕΡΑΤΑ":"ΤΕΡ","ΤΕΡΑΤΩΝ":"ΤΕΡ","ΦΩΣ":"ΦΩ","ΦΩΤΟΣ":"ΦΩ","ΦΩΤΑ":"ΦΩ","ΦΩΤΩΝ":"ΦΩ","ΚΑΘΕΣΤΩΣ":"ΚΑΘΕΣΤ","ΚΑΘΕΣΤΩΤΟΣ":"ΚΑΘΕΣΤ","ΚΑΘΕΣΤΩΤΑ":"ΚΑΘΕΣΤ","ΚΑΘΕΣΤΩΤΩΝ":"ΚΑΘΕΣΤ","ΓΕΓΟΝΟΣ":"ΓΕΓΟΝ","ΓΕΓΟΝΟΤΟΣ":"ΓΕΓΟΝ","ΓΕΓΟΝΟΤΑ":"ΓΕΓΟΝ","ΓΕΓΟΝΟΤΩΝ":"ΓΕΓΟΝ","ΕΥΑ":"ΕΥ"},i=["ΑΚΡΙΒΩΣ","ΑΛΑ","ΑΛΛΑ","ΑΛΛΙΩΣ","ΑΛΛΟΤΕ","ΑΜΑ","ΑΝΩ","ΑΝΑ","ΑΝΑΜΕΣΑ","ΑΝΑΜΕΤΑΞΥ","ΑΝΕΥ","ΑΝΤΙ","ΑΝΤΙΠΕΡΑ","ΑΝΤΙΟ","ΑΞΑΦΝΑ","ΑΠΟ","ΑΠΟΨΕ","ΑΡΑ","ΑΡΑΓΕ","ΑΥΡΙΟ","ΑΦΟΙ","ΑΦΟΥ","ΑΦΟΤΟΥ","ΒΡΕ","ΓΕΙΑ","ΓΙΑ","ΓΙΑΤΙ","ΓΡΑΜΜΑ","ΔΕΗ","ΔΕΝ","ΔΗΛΑΔΗ","ΔΙΧΩΣ","ΔΥΟ","ΕΑΝ","ΕΓΩ","ΕΔΩ","ΕΔΑ","ΕΙΘΕ","ΕΙΜΑΙ","ΕΙΜΑΣΤΕ","ΕΙΣΑΙ","ΕΙΣΑΣΤΕ","ΕΙΝΑΙ","ΕΙΣΤΕ","ΕΙΤΕ","ΕΚΕΙ","ΕΚΟ","ΕΛΑ","ΕΜΑΣ","ΕΜΕΙΣ","ΕΝΤΕΛΩΣ","ΕΝΤΟΣ","ΕΝΤΩΜΕΤΑΞΥ","ΕΝΩ","ΕΞΙ","ΕΞΙΣΟΥ","ΕΞΗΣ","ΕΞΩ","ΕΟΚ","ΕΠΑΝΩ","ΕΠΕΙΔΗ","ΕΠΕΙΤΑ","ΕΠΙ","ΕΠΙΣΗΣ","ΕΠΟΜΕΝΩΣ","ΕΠΤΑ","ΕΣΑΣ","ΕΣΕΙΣ","ΕΣΤΩ","ΕΣΥ","ΕΣΩ","ΕΤΣΙ","ΕΥΓΕ","ΕΦΕ","ΕΦΕΞΗΣ","ΕΧΤΕΣ","ΕΩΣ","ΗΔΗ","ΗΜΙ","ΗΠΑ","ΗΤΟΙ","ΘΕΣ","ΙΔΙΩΣ","ΙΔΗ","ΙΚΑ","ΙΣΩΣ","ΚΑΘΕ","ΚΑΘΕΤΙ","ΚΑΘΟΛΟΥ","ΚΑΘΩΣ","ΚΑΙ","ΚΑΝ","ΚΑΠΟΤΕ","ΚΑΠΟΥ","ΚΑΤΑ","ΚΑΤΙ","ΚΑΤΟΠΙΝ","ΚΑΤΩ","ΚΕΙ","ΚΙΧ","ΚΚΕ","ΚΟΛΑΝ","ΚΥΡΙΩΣ","ΚΩΣ","ΜΑΚΑΡΙ","ΜΑΛΙΣΤΑ","ΜΑΛΛΟΝ","ΜΑΙ","ΜΑΟ","ΜΑΟΥΣ","ΜΑΣ","ΜΕΘΑΥΡΙΟ","ΜΕΣ","ΜΕΣΑ","ΜΕΤΑ","ΜΕΤΑΞΥ","ΜΕΧΡΙ","ΜΗΔΕ","ΜΗΝ","ΜΗΠΩΣ","ΜΗΤΕ","ΜΙΑ","ΜΙΑΣ","ΜΙΣ","ΜΜΕ","ΜΟΛΟΝΟΤΙ","ΜΟΥ","ΜΠΑ","ΜΠΑΣ","ΜΠΟΥΦΑΝ","ΜΠΡΟΣ","ΝΑΙ","ΝΕΣ","ΝΤΑ","ΝΤΕ","ΞΑΝΑ","ΟΗΕ","ΟΚΤΩ","ΟΜΩΣ","ΟΝΕ","ΟΠΑ","ΟΠΟΥ","ΟΠΩΣ","ΟΣΟ","ΟΤΑΝ","ΟΤΕ","ΟΤΙ","ΟΥΤΕ","ΟΧΙ","ΠΑΛΙ","ΠΑΝ","ΠΑΝΟ","ΠΑΝΤΟΤΕ","ΠΑΝΤΟΥ","ΠΑΝΤΩΣ","ΠΑΝΩ","ΠΑΡΑ","ΠΕΡΑ","ΠΕΡΙ","ΠΕΡΙΠΟΥ","ΠΙΑ","ΠΙΟ","ΠΙΣΩ","ΠΛΑΙ","ΠΛΕΟΝ","ΠΛΗΝ","ΠΟΤΕ","ΠΟΥ","ΠΡΟ","ΠΡΟΣ","ΠΡΟΧΤΕΣ","ΠΡΟΧΘΕΣ","ΡΟΔΙ","ΠΩΣ","ΣΑΙ","ΣΑΣ","ΣΑΝ","ΣΕΙΣ","ΣΙΑ","ΣΚΙ","ΣΟΙ","ΣΟΥ","ΣΡΙ","ΣΥΝ","ΣΥΝΑΜΑ","ΣΧΕΔΟΝ","ΤΑΔΕ","ΤΑΞΙ","ΤΑΧΑ","ΤΕΙ","ΤΗΝ","ΤΗΣ","ΤΙΠΟΤΑ","ΤΙΠΟΤΕ","ΤΙΣ","ΤΟΝ","ΤΟΤΕ","ΤΟΥ","ΤΟΥΣ","ΤΣΑ","ΤΣΕ","ΤΣΙ","ΤΣΟΥ","ΤΩΝ","ΥΠΟ","ΥΠΟΨΗ","ΥΠΟΨΙΝ","ΥΣΤΕΡΑ","ΦΕΤΟΣ","ΦΙΣ","ΦΠΑ","ΧΑΦ","ΧΘΕΣ","ΧΤΕΣ","ΧΩΡΙΣ","ΩΣ","ΩΣΑΝ","ΩΣΟΤΟΥ","ΩΣΠΟΥ","ΩΣΤΕ","ΩΣΤΟΣΟ"],s=new RegExp("^[ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ]+$");return function(e){return"function"==typeof e.update?e.update(function(e){return n(e.toUpperCase()).toLowerCase()}):n(e.toUpperCase()).toLowerCase()}}(),e.Pipeline.registerFunction(e.el.stemmer,"stemmer-el"),e.el.stopWordFilter=e.generateStopWordFilter("αλλα αν αντι απο αυτα αυτεσ αυτη αυτο αυτοι αυτοσ αυτουσ αυτων για δε δεν εαν ειμαι ειμαστε ειναι εισαι ειστε εκεινα εκεινεσ εκεινη εκεινο εκεινοι εκεινοσ εκεινουσ εκεινων ενω επι η θα ισωσ κ και κατα κι μα με μετα μη μην να ο οι ομωσ οπωσ οσο οτι παρα ποια ποιεσ ποιο ποιοι ποιοσ ποιουσ ποιων που προσ πωσ σε στη στην στο στον τα την τησ το τον τοτε του των ωσ".split(" ")),e.Pipeline.registerFunction(e.el.stopWordFilter,"stopWordFilter-el"),e.el.normilizer=function(){var e={"Ά":"Α","ά":"α","Έ":"Ε","έ":"ε","Ή":"Η","ή":"η","Ί":"Ι","ί":"ι","Ό":"Ο","ο":"ο","Ύ":"Υ","ύ":"υ","Ώ":"Ω","ώ":"ω","Ϊ":"Ι","ϊ":"ι","Ϋ":"Υ","ϋ":"υ","ΐ":"ι","ΰ":"υ"};return function(t){if("function"==typeof t.update)return t.update(function(t){for(var r="",n=0;n=A.limit)return!0;A.cursor++}return!1}return!0}function n(){if(A.in_grouping(x,97,252)){var s=A.cursor;if(e()){if(A.cursor=s,!A.in_grouping(x,97,252))return!0;for(;!A.out_grouping(x,97,252);){if(A.cursor>=A.limit)return!0;A.cursor++}}return!1}return!0}function i(){var s,r=A.cursor;if(n()){if(A.cursor=r,!A.out_grouping(x,97,252))return;if(s=A.cursor,e()){if(A.cursor=s,!A.in_grouping(x,97,252)||A.cursor>=A.limit)return;A.cursor++}}g=A.cursor}function a(){for(;!A.in_grouping(x,97,252);){if(A.cursor>=A.limit)return!1;A.cursor++}for(;!A.out_grouping(x,97,252);){if(A.cursor>=A.limit)return!1;A.cursor++}return!0}function t(){var e=A.cursor;g=A.limit,p=g,v=g,i(),A.cursor=e,a()&&(p=A.cursor,a()&&(v=A.cursor))}function o(){for(var e;;){if(A.bra=A.cursor,e=A.find_among(k,6))switch(A.ket=A.cursor,e){case 1:A.slice_from("a");continue;case 2:A.slice_from("e");continue;case 3:A.slice_from("i");continue;case 4:A.slice_from("o");continue;case 5:A.slice_from("u");continue;case 6:if(A.cursor>=A.limit)break;A.cursor++;continue}break}}function u(){return g<=A.cursor}function w(){return p<=A.cursor}function c(){return v<=A.cursor}function m(){var e;if(A.ket=A.cursor,A.find_among_b(y,13)&&(A.bra=A.cursor,(e=A.find_among_b(q,11))&&u()))switch(e){case 1:A.bra=A.cursor,A.slice_from("iendo");break;case 2:A.bra=A.cursor,A.slice_from("ando");break;case 3:A.bra=A.cursor,A.slice_from("ar");break;case 4:A.bra=A.cursor,A.slice_from("er");break;case 5:A.bra=A.cursor,A.slice_from("ir");break;case 6:A.slice_del();break;case 7:A.eq_s_b(1,"u")&&A.slice_del()}}function l(e,s){if(!c())return!0;A.slice_del(),A.ket=A.cursor;var r=A.find_among_b(e,s);return r&&(A.bra=A.cursor,1==r&&c()&&A.slice_del()),!1}function d(e){return!c()||(A.slice_del(),A.ket=A.cursor,A.eq_s_b(2,e)&&(A.bra=A.cursor,c()&&A.slice_del()),!1)}function b(){var e;if(A.ket=A.cursor,e=A.find_among_b(S,46)){switch(A.bra=A.cursor,e){case 1:if(!c())return!1;A.slice_del();break;case 2:if(d("ic"))return!1;break;case 3:if(!c())return!1;A.slice_from("log");break;case 4:if(!c())return!1;A.slice_from("u");break;case 5:if(!c())return!1;A.slice_from("ente");break;case 6:if(!w())return!1;A.slice_del(),A.ket=A.cursor,e=A.find_among_b(C,4),e&&(A.bra=A.cursor,c()&&(A.slice_del(),1==e&&(A.ket=A.cursor,A.eq_s_b(2,"at")&&(A.bra=A.cursor,c()&&A.slice_del()))));break;case 7:if(l(P,3))return!1;break;case 8:if(l(F,3))return!1;break;case 9:if(d("at"))return!1}return!0}return!1}function f(){var e,s;if(A.cursor>=g&&(s=A.limit_backward,A.limit_backward=g,A.ket=A.cursor,e=A.find_among_b(W,12),A.limit_backward=s,e)){if(A.bra=A.cursor,1==e){if(!A.eq_s_b(1,"u"))return!1;A.slice_del()}return!0}return!1}function _(){var e,s,r,n;if(A.cursor>=g&&(s=A.limit_backward,A.limit_backward=g,A.ket=A.cursor,e=A.find_among_b(L,96),A.limit_backward=s,e))switch(A.bra=A.cursor,e){case 1:r=A.limit-A.cursor,A.eq_s_b(1,"u")?(n=A.limit-A.cursor,A.eq_s_b(1,"g")?A.cursor=A.limit-n:A.cursor=A.limit-r):A.cursor=A.limit-r,A.bra=A.cursor;case 2:A.slice_del()}}function h(){var e,s;if(A.ket=A.cursor,e=A.find_among_b(z,8))switch(A.bra=A.cursor,e){case 1:u()&&A.slice_del();break;case 2:u()&&(A.slice_del(),A.ket=A.cursor,A.eq_s_b(1,"u")&&(A.bra=A.cursor,s=A.limit-A.cursor,A.eq_s_b(1,"g")&&(A.cursor=A.limit-s,u()&&A.slice_del())))}}var v,p,g,k=[new s("",-1,6),new s("á",0,1),new s("é",0,2),new s("í",0,3),new s("ó",0,4),new s("ú",0,5)],y=[new s("la",-1,-1),new s("sela",0,-1),new s("le",-1,-1),new s("me",-1,-1),new s("se",-1,-1),new s("lo",-1,-1),new s("selo",5,-1),new s("las",-1,-1),new s("selas",7,-1),new s("les",-1,-1),new s("los",-1,-1),new s("selos",10,-1),new s("nos",-1,-1)],q=[new s("ando",-1,6),new s("iendo",-1,6),new s("yendo",-1,7),new s("ándo",-1,2),new s("iéndo",-1,1),new s("ar",-1,6),new s("er",-1,6),new s("ir",-1,6),new s("ár",-1,3),new s("ér",-1,4),new s("ír",-1,5)],C=[new s("ic",-1,-1),new s("ad",-1,-1),new s("os",-1,-1),new s("iv",-1,1)],P=[new s("able",-1,1),new s("ible",-1,1),new s("ante",-1,1)],F=[new s("ic",-1,1),new s("abil",-1,1),new s("iv",-1,1)],S=[new s("ica",-1,1),new s("ancia",-1,2),new s("encia",-1,5),new s("adora",-1,2),new s("osa",-1,1),new s("ista",-1,1),new s("iva",-1,9),new s("anza",-1,1),new s("logía",-1,3),new s("idad",-1,8),new s("able",-1,1),new s("ible",-1,1),new s("ante",-1,2),new s("mente",-1,7),new s("amente",13,6),new s("ación",-1,2),new s("ución",-1,4),new s("ico",-1,1),new s("ismo",-1,1),new s("oso",-1,1),new s("amiento",-1,1),new s("imiento",-1,1),new s("ivo",-1,9),new s("ador",-1,2),new s("icas",-1,1),new s("ancias",-1,2),new s("encias",-1,5),new s("adoras",-1,2),new s("osas",-1,1),new s("istas",-1,1),new s("ivas",-1,9),new s("anzas",-1,1),new s("logías",-1,3),new s("idades",-1,8),new s("ables",-1,1),new s("ibles",-1,1),new s("aciones",-1,2),new s("uciones",-1,4),new s("adores",-1,2),new s("antes",-1,2),new s("icos",-1,1),new s("ismos",-1,1),new s("osos",-1,1),new s("amientos",-1,1),new s("imientos",-1,1),new s("ivos",-1,9)],W=[new s("ya",-1,1),new s("ye",-1,1),new s("yan",-1,1),new s("yen",-1,1),new s("yeron",-1,1),new s("yendo",-1,1),new s("yo",-1,1),new s("yas",-1,1),new s("yes",-1,1),new s("yais",-1,1),new s("yamos",-1,1),new s("yó",-1,1)],L=[new s("aba",-1,2),new s("ada",-1,2),new s("ida",-1,2),new s("ara",-1,2),new s("iera",-1,2),new s("ía",-1,2),new s("aría",5,2),new s("ería",5,2),new s("iría",5,2),new s("ad",-1,2),new s("ed",-1,2),new s("id",-1,2),new s("ase",-1,2),new s("iese",-1,2),new s("aste",-1,2),new s("iste",-1,2),new s("an",-1,2),new s("aban",16,2),new s("aran",16,2),new s("ieran",16,2),new s("ían",16,2),new s("arían",20,2),new s("erían",20,2),new s("irían",20,2),new s("en",-1,1),new s("asen",24,2),new s("iesen",24,2),new s("aron",-1,2),new s("ieron",-1,2),new s("arán",-1,2),new s("erán",-1,2),new s("irán",-1,2),new s("ado",-1,2),new s("ido",-1,2),new s("ando",-1,2),new s("iendo",-1,2),new s("ar",-1,2),new s("er",-1,2),new s("ir",-1,2),new s("as",-1,2),new s("abas",39,2),new s("adas",39,2),new s("idas",39,2),new s("aras",39,2),new s("ieras",39,2),new s("ías",39,2),new s("arías",45,2),new s("erías",45,2),new s("irías",45,2),new s("es",-1,1),new s("ases",49,2),new s("ieses",49,2),new s("abais",-1,2),new s("arais",-1,2),new s("ierais",-1,2),new s("íais",-1,2),new s("aríais",55,2),new s("eríais",55,2),new s("iríais",55,2),new s("aseis",-1,2),new s("ieseis",-1,2),new s("asteis",-1,2),new s("isteis",-1,2),new s("áis",-1,2),new s("éis",-1,1),new s("aréis",64,2),new s("eréis",64,2),new s("iréis",64,2),new s("ados",-1,2),new s("idos",-1,2),new s("amos",-1,2),new s("ábamos",70,2),new s("áramos",70,2),new s("iéramos",70,2),new s("íamos",70,2),new s("aríamos",74,2),new s("eríamos",74,2),new s("iríamos",74,2),new s("emos",-1,1),new s("aremos",78,2),new s("eremos",78,2),new s("iremos",78,2),new s("ásemos",78,2),new s("iésemos",78,2),new s("imos",-1,2),new s("arás",-1,2),new s("erás",-1,2),new s("irás",-1,2),new s("ís",-1,2),new s("ará",-1,2),new s("erá",-1,2),new s("irá",-1,2),new s("aré",-1,2),new s("eré",-1,2),new s("iré",-1,2),new s("ió",-1,2)],z=[new s("a",-1,1),new s("e",-1,2),new s("o",-1,1),new s("os",-1,1),new s("á",-1,1),new s("é",-1,2),new s("í",-1,1),new s("ó",-1,1)],x=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,4,10],A=new r;this.setCurrent=function(e){A.setCurrent(e)},this.getCurrent=function(){return A.getCurrent()},this.stem=function(){var e=A.cursor;return t(),A.limit_backward=e,A.cursor=A.limit,m(),A.cursor=A.limit,b()||(A.cursor=A.limit,f()||(A.cursor=A.limit,_())),A.cursor=A.limit,h(),A.cursor=A.limit_backward,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.es.stemmer,"stemmer-es"),e.es.stopWordFilter=e.generateStopWordFilter("a al algo algunas algunos ante antes como con contra cual cuando de del desde donde durante e el ella ellas ellos en entre era erais eran eras eres es esa esas ese eso esos esta estaba estabais estaban estabas estad estada estadas estado estados estamos estando estar estaremos estará estarán estarás estaré estaréis estaría estaríais estaríamos estarían estarías estas este estemos esto estos estoy estuve estuviera estuvierais estuvieran estuvieras estuvieron estuviese estuvieseis estuviesen estuvieses estuvimos estuviste estuvisteis estuviéramos estuviésemos estuvo está estábamos estáis están estás esté estéis estén estés fue fuera fuerais fueran fueras fueron fuese fueseis fuesen fueses fui fuimos fuiste fuisteis fuéramos fuésemos ha habida habidas habido habidos habiendo habremos habrá habrán habrás habré habréis habría habríais habríamos habrían habrías habéis había habíais habíamos habían habías han has hasta hay haya hayamos hayan hayas hayáis he hemos hube hubiera hubierais hubieran hubieras hubieron hubiese hubieseis hubiesen hubieses hubimos hubiste hubisteis hubiéramos hubiésemos hubo la las le les lo los me mi mis mucho muchos muy más mí mía mías mío míos nada ni no nos nosotras nosotros nuestra nuestras nuestro nuestros o os otra otras otro otros para pero poco por porque que quien quienes qué se sea seamos sean seas seremos será serán serás seré seréis sería seríais seríamos serían serías seáis sido siendo sin sobre sois somos son soy su sus suya suyas suyo suyos sí también tanto te tendremos tendrá tendrán tendrás tendré tendréis tendría tendríais tendríamos tendrían tendrías tened tenemos tenga tengamos tengan tengas tengo tengáis tenida tenidas tenido tenidos teniendo tenéis tenía teníais teníamos tenían tenías ti tiene tienen tienes todo todos tu tus tuve tuviera tuvierais tuvieran tuvieras tuvieron tuviese tuvieseis tuviesen tuvieses tuvimos tuviste tuvisteis tuviéramos tuviésemos tuvo tuya tuyas tuyo tuyos tú un una uno unos vosotras vosotros vuestra vuestras vuestro vuestros y ya yo él éramos".split(" ")),e.Pipeline.registerFunction(e.es.stopWordFilter,"stopWordFilter-es")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.fi.min.js b/assets/javascripts/lunr/min/lunr.fi.min.js new file mode 100644 index 0000000000..29f5dfcea8 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.fi.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Finnish` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(i,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e():e()(i.lunr)}(this,function(){return function(i){if(void 0===i)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===i.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");i.fi=function(){this.pipeline.reset(),this.pipeline.add(i.fi.trimmer,i.fi.stopWordFilter,i.fi.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(i.fi.stemmer))},i.fi.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",i.fi.trimmer=i.trimmerSupport.generateTrimmer(i.fi.wordCharacters),i.Pipeline.registerFunction(i.fi.trimmer,"trimmer-fi"),i.fi.stemmer=function(){var e=i.stemmerSupport.Among,r=i.stemmerSupport.SnowballProgram,n=new function(){function i(){f=A.limit,d=f,n()||(f=A.cursor,n()||(d=A.cursor))}function n(){for(var i;;){if(i=A.cursor,A.in_grouping(W,97,246))break;if(A.cursor=i,i>=A.limit)return!0;A.cursor++}for(A.cursor=i;!A.out_grouping(W,97,246);){if(A.cursor>=A.limit)return!0;A.cursor++}return!1}function t(){return d<=A.cursor}function s(){var i,e;if(A.cursor>=f)if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,i=A.find_among_b(h,10)){switch(A.bra=A.cursor,A.limit_backward=e,i){case 1:if(!A.in_grouping_b(x,97,246))return;break;case 2:if(!t())return}A.slice_del()}else A.limit_backward=e}function o(){var i,e,r;if(A.cursor>=f)if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,i=A.find_among_b(v,9))switch(A.bra=A.cursor,A.limit_backward=e,i){case 1:r=A.limit-A.cursor,A.eq_s_b(1,"k")||(A.cursor=A.limit-r,A.slice_del());break;case 2:A.slice_del(),A.ket=A.cursor,A.eq_s_b(3,"kse")&&(A.bra=A.cursor,A.slice_from("ksi"));break;case 3:A.slice_del();break;case 4:A.find_among_b(p,6)&&A.slice_del();break;case 5:A.find_among_b(g,6)&&A.slice_del();break;case 6:A.find_among_b(j,2)&&A.slice_del()}else A.limit_backward=e}function l(){return A.find_among_b(q,7)}function a(){return A.eq_s_b(1,"i")&&A.in_grouping_b(L,97,246)}function u(){var i,e,r;if(A.cursor>=f)if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,i=A.find_among_b(C,30)){switch(A.bra=A.cursor,A.limit_backward=e,i){case 1:if(!A.eq_s_b(1,"a"))return;break;case 2:case 9:if(!A.eq_s_b(1,"e"))return;break;case 3:if(!A.eq_s_b(1,"i"))return;break;case 4:if(!A.eq_s_b(1,"o"))return;break;case 5:if(!A.eq_s_b(1,"ä"))return;break;case 6:if(!A.eq_s_b(1,"ö"))return;break;case 7:if(r=A.limit-A.cursor,!l()&&(A.cursor=A.limit-r,!A.eq_s_b(2,"ie"))){A.cursor=A.limit-r;break}if(A.cursor=A.limit-r,A.cursor<=A.limit_backward){A.cursor=A.limit-r;break}A.cursor--,A.bra=A.cursor;break;case 8:if(!A.in_grouping_b(W,97,246)||!A.out_grouping_b(W,97,246))return}A.slice_del(),k=!0}else A.limit_backward=e}function c(){var i,e,r;if(A.cursor>=d)if(e=A.limit_backward,A.limit_backward=d,A.ket=A.cursor,i=A.find_among_b(P,14)){if(A.bra=A.cursor,A.limit_backward=e,1==i){if(r=A.limit-A.cursor,A.eq_s_b(2,"po"))return;A.cursor=A.limit-r}A.slice_del()}else A.limit_backward=e}function m(){var i;A.cursor>=f&&(i=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,A.find_among_b(F,2)?(A.bra=A.cursor,A.limit_backward=i,A.slice_del()):A.limit_backward=i)}function w(){var i,e,r,n,t,s;if(A.cursor>=f){if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,A.eq_s_b(1,"t")&&(A.bra=A.cursor,r=A.limit-A.cursor,A.in_grouping_b(W,97,246)&&(A.cursor=A.limit-r,A.slice_del(),A.limit_backward=e,n=A.limit-A.cursor,A.cursor>=d&&(A.cursor=d,t=A.limit_backward,A.limit_backward=A.cursor,A.cursor=A.limit-n,A.ket=A.cursor,i=A.find_among_b(S,2))))){if(A.bra=A.cursor,A.limit_backward=t,1==i){if(s=A.limit-A.cursor,A.eq_s_b(2,"po"))return;A.cursor=A.limit-s}return void A.slice_del()}A.limit_backward=e}}function _(){var i,e,r,n;if(A.cursor>=f){for(i=A.limit_backward,A.limit_backward=f,e=A.limit-A.cursor,l()&&(A.cursor=A.limit-e,A.ket=A.cursor,A.cursor>A.limit_backward&&(A.cursor--,A.bra=A.cursor,A.slice_del())),A.cursor=A.limit-e,A.ket=A.cursor,A.in_grouping_b(y,97,228)&&(A.bra=A.cursor,A.out_grouping_b(W,97,246)&&A.slice_del()),A.cursor=A.limit-e,A.ket=A.cursor,A.eq_s_b(1,"j")&&(A.bra=A.cursor,r=A.limit-A.cursor,A.eq_s_b(1,"o")?A.slice_del():(A.cursor=A.limit-r,A.eq_s_b(1,"u")&&A.slice_del())),A.cursor=A.limit-e,A.ket=A.cursor,A.eq_s_b(1,"o")&&(A.bra=A.cursor,A.eq_s_b(1,"j")&&A.slice_del()),A.cursor=A.limit-e,A.limit_backward=i;;){if(n=A.limit-A.cursor,A.out_grouping_b(W,97,246)){A.cursor=A.limit-n;break}if(A.cursor=A.limit-n,A.cursor<=A.limit_backward)return;A.cursor--}A.ket=A.cursor,A.cursor>A.limit_backward&&(A.cursor--,A.bra=A.cursor,b=A.slice_to(),A.eq_v_b(b)&&A.slice_del())}}var k,b,d,f,h=[new e("pa",-1,1),new e("sti",-1,2),new e("kaan",-1,1),new e("han",-1,1),new e("kin",-1,1),new e("hän",-1,1),new e("kään",-1,1),new e("ko",-1,1),new e("pä",-1,1),new e("kö",-1,1)],p=[new e("lla",-1,-1),new e("na",-1,-1),new e("ssa",-1,-1),new e("ta",-1,-1),new e("lta",3,-1),new e("sta",3,-1)],g=[new e("llä",-1,-1),new e("nä",-1,-1),new e("ssä",-1,-1),new e("tä",-1,-1),new e("ltä",3,-1),new e("stä",3,-1)],j=[new e("lle",-1,-1),new e("ine",-1,-1)],v=[new e("nsa",-1,3),new e("mme",-1,3),new e("nne",-1,3),new e("ni",-1,2),new e("si",-1,1),new e("an",-1,4),new e("en",-1,6),new e("än",-1,5),new e("nsä",-1,3)],q=[new e("aa",-1,-1),new e("ee",-1,-1),new e("ii",-1,-1),new e("oo",-1,-1),new e("uu",-1,-1),new e("ää",-1,-1),new e("öö",-1,-1)],C=[new e("a",-1,8),new e("lla",0,-1),new e("na",0,-1),new e("ssa",0,-1),new e("ta",0,-1),new e("lta",4,-1),new e("sta",4,-1),new e("tta",4,9),new e("lle",-1,-1),new e("ine",-1,-1),new e("ksi",-1,-1),new e("n",-1,7),new e("han",11,1),new e("den",11,-1,a),new e("seen",11,-1,l),new e("hen",11,2),new e("tten",11,-1,a),new e("hin",11,3),new e("siin",11,-1,a),new e("hon",11,4),new e("hän",11,5),new e("hön",11,6),new e("ä",-1,8),new e("llä",22,-1),new e("nä",22,-1),new e("ssä",22,-1),new e("tä",22,-1),new e("ltä",26,-1),new e("stä",26,-1),new e("ttä",26,9)],P=[new e("eja",-1,-1),new e("mma",-1,1),new e("imma",1,-1),new e("mpa",-1,1),new e("impa",3,-1),new e("mmi",-1,1),new e("immi",5,-1),new e("mpi",-1,1),new e("impi",7,-1),new e("ejä",-1,-1),new e("mmä",-1,1),new e("immä",10,-1),new e("mpä",-1,1),new e("impä",12,-1)],F=[new e("i",-1,-1),new e("j",-1,-1)],S=[new e("mma",-1,1),new e("imma",0,-1)],y=[17,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8],W=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],L=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],x=[17,97,24,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],A=new r;this.setCurrent=function(i){A.setCurrent(i)},this.getCurrent=function(){return A.getCurrent()},this.stem=function(){var e=A.cursor;return i(),k=!1,A.limit_backward=e,A.cursor=A.limit,s(),A.cursor=A.limit,o(),A.cursor=A.limit,u(),A.cursor=A.limit,c(),A.cursor=A.limit,k?(m(),A.cursor=A.limit):(A.cursor=A.limit,w(),A.cursor=A.limit),_(),!0}};return function(i){return"function"==typeof i.update?i.update(function(i){return n.setCurrent(i),n.stem(),n.getCurrent()}):(n.setCurrent(i),n.stem(),n.getCurrent())}}(),i.Pipeline.registerFunction(i.fi.stemmer,"stemmer-fi"),i.fi.stopWordFilter=i.generateStopWordFilter("ei eivät emme en et ette että he heidän heidät heihin heille heillä heiltä heissä heistä heitä hän häneen hänelle hänellä häneltä hänen hänessä hänestä hänet häntä itse ja johon joiden joihin joiksi joilla joille joilta joina joissa joista joita joka joksi jolla jolle jolta jona jonka jos jossa josta jota jotka kanssa keiden keihin keiksi keille keillä keiltä keinä keissä keistä keitä keneen keneksi kenelle kenellä keneltä kenen kenenä kenessä kenestä kenet ketkä ketkä ketä koska kuin kuka kun me meidän meidät meihin meille meillä meiltä meissä meistä meitä mihin miksi mikä mille millä miltä minkä minkä minua minulla minulle minulta minun minussa minusta minut minuun minä minä missä mistä mitkä mitä mukaan mutta ne niiden niihin niiksi niille niillä niiltä niin niin niinä niissä niistä niitä noiden noihin noiksi noilla noille noilta noin noina noissa noista noita nuo nyt näiden näihin näiksi näille näillä näiltä näinä näissä näistä näitä nämä ole olemme olen olet olette oli olimme olin olisi olisimme olisin olisit olisitte olisivat olit olitte olivat olla olleet ollut on ovat poikki se sekä sen siihen siinä siitä siksi sille sillä sillä siltä sinua sinulla sinulle sinulta sinun sinussa sinusta sinut sinuun sinä sinä sitä tai te teidän teidät teihin teille teillä teiltä teissä teistä teitä tuo tuohon tuoksi tuolla tuolle tuolta tuon tuona tuossa tuosta tuota tähän täksi tälle tällä tältä tämä tämän tänä tässä tästä tätä vaan vai vaikka yli".split(" ")),i.Pipeline.registerFunction(i.fi.stopWordFilter,"stopWordFilter-fi")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.fr.min.js b/assets/javascripts/lunr/min/lunr.fr.min.js new file mode 100644 index 0000000000..68cd0094ae --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.fr.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `French` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.fr=function(){this.pipeline.reset(),this.pipeline.add(e.fr.trimmer,e.fr.stopWordFilter,e.fr.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.fr.stemmer))},e.fr.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.fr.trimmer=e.trimmerSupport.generateTrimmer(e.fr.wordCharacters),e.Pipeline.registerFunction(e.fr.trimmer,"trimmer-fr"),e.fr.stemmer=function(){var r=e.stemmerSupport.Among,s=e.stemmerSupport.SnowballProgram,i=new function(){function e(e,r,s){return!(!W.eq_s(1,e)||(W.ket=W.cursor,!W.in_grouping(F,97,251)))&&(W.slice_from(r),W.cursor=s,!0)}function i(e,r,s){return!!W.eq_s(1,e)&&(W.ket=W.cursor,W.slice_from(r),W.cursor=s,!0)}function n(){for(var r,s;;){if(r=W.cursor,W.in_grouping(F,97,251)){if(W.bra=W.cursor,s=W.cursor,e("u","U",r))continue;if(W.cursor=s,e("i","I",r))continue;if(W.cursor=s,i("y","Y",r))continue}if(W.cursor=r,W.bra=r,!e("y","Y",r)){if(W.cursor=r,W.eq_s(1,"q")&&(W.bra=W.cursor,i("u","U",r)))continue;if(W.cursor=r,r>=W.limit)return;W.cursor++}}}function t(){for(;!W.in_grouping(F,97,251);){if(W.cursor>=W.limit)return!0;W.cursor++}for(;!W.out_grouping(F,97,251);){if(W.cursor>=W.limit)return!0;W.cursor++}return!1}function u(){var e=W.cursor;if(q=W.limit,g=q,p=q,W.in_grouping(F,97,251)&&W.in_grouping(F,97,251)&&W.cursor=W.limit){W.cursor=q;break}W.cursor++}while(!W.in_grouping(F,97,251))}q=W.cursor,W.cursor=e,t()||(g=W.cursor,t()||(p=W.cursor))}function o(){for(var e,r;;){if(r=W.cursor,W.bra=r,!(e=W.find_among(h,4)))break;switch(W.ket=W.cursor,e){case 1:W.slice_from("i");break;case 2:W.slice_from("u");break;case 3:W.slice_from("y");break;case 4:if(W.cursor>=W.limit)return;W.cursor++}}}function c(){return q<=W.cursor}function a(){return g<=W.cursor}function l(){return p<=W.cursor}function w(){var e,r;if(W.ket=W.cursor,e=W.find_among_b(C,43)){switch(W.bra=W.cursor,e){case 1:if(!l())return!1;W.slice_del();break;case 2:if(!l())return!1;W.slice_del(),W.ket=W.cursor,W.eq_s_b(2,"ic")&&(W.bra=W.cursor,l()?W.slice_del():W.slice_from("iqU"));break;case 3:if(!l())return!1;W.slice_from("log");break;case 4:if(!l())return!1;W.slice_from("u");break;case 5:if(!l())return!1;W.slice_from("ent");break;case 6:if(!c())return!1;if(W.slice_del(),W.ket=W.cursor,e=W.find_among_b(z,6))switch(W.bra=W.cursor,e){case 1:l()&&(W.slice_del(),W.ket=W.cursor,W.eq_s_b(2,"at")&&(W.bra=W.cursor,l()&&W.slice_del()));break;case 2:l()?W.slice_del():a()&&W.slice_from("eux");break;case 3:l()&&W.slice_del();break;case 4:c()&&W.slice_from("i")}break;case 7:if(!l())return!1;if(W.slice_del(),W.ket=W.cursor,e=W.find_among_b(y,3))switch(W.bra=W.cursor,e){case 1:l()?W.slice_del():W.slice_from("abl");break;case 2:l()?W.slice_del():W.slice_from("iqU");break;case 3:l()&&W.slice_del()}break;case 8:if(!l())return!1;if(W.slice_del(),W.ket=W.cursor,W.eq_s_b(2,"at")&&(W.bra=W.cursor,l()&&(W.slice_del(),W.ket=W.cursor,W.eq_s_b(2,"ic")))){W.bra=W.cursor,l()?W.slice_del():W.slice_from("iqU");break}break;case 9:W.slice_from("eau");break;case 10:if(!a())return!1;W.slice_from("al");break;case 11:if(l())W.slice_del();else{if(!a())return!1;W.slice_from("eux")}break;case 12:if(!a()||!W.out_grouping_b(F,97,251))return!1;W.slice_del();break;case 13:return c()&&W.slice_from("ant"),!1;case 14:return c()&&W.slice_from("ent"),!1;case 15:return r=W.limit-W.cursor,W.in_grouping_b(F,97,251)&&c()&&(W.cursor=W.limit-r,W.slice_del()),!1}return!0}return!1}function f(){var e,r;if(W.cursor=q){if(s=W.limit_backward,W.limit_backward=q,W.ket=W.cursor,e=W.find_among_b(P,7))switch(W.bra=W.cursor,e){case 1:if(l()){if(i=W.limit-W.cursor,!W.eq_s_b(1,"s")&&(W.cursor=W.limit-i,!W.eq_s_b(1,"t")))break;W.slice_del()}break;case 2:W.slice_from("i");break;case 3:W.slice_del();break;case 4:W.eq_s_b(2,"gu")&&W.slice_del()}W.limit_backward=s}}function b(){var e=W.limit-W.cursor;W.find_among_b(U,5)&&(W.cursor=W.limit-e,W.ket=W.cursor,W.cursor>W.limit_backward&&(W.cursor--,W.bra=W.cursor,W.slice_del()))}function d(){for(var e,r=1;W.out_grouping_b(F,97,251);)r--;if(r<=0){if(W.ket=W.cursor,e=W.limit-W.cursor,!W.eq_s_b(1,"é")&&(W.cursor=W.limit-e,!W.eq_s_b(1,"è")))return;W.bra=W.cursor,W.slice_from("e")}}function k(){if(!w()&&(W.cursor=W.limit,!f()&&(W.cursor=W.limit,!m())))return W.cursor=W.limit,void _();W.cursor=W.limit,W.ket=W.cursor,W.eq_s_b(1,"Y")?(W.bra=W.cursor,W.slice_from("i")):(W.cursor=W.limit,W.eq_s_b(1,"ç")&&(W.bra=W.cursor,W.slice_from("c")))}var p,g,q,v=[new r("col",-1,-1),new r("par",-1,-1),new r("tap",-1,-1)],h=[new r("",-1,4),new r("I",0,1),new r("U",0,2),new r("Y",0,3)],z=[new r("iqU",-1,3),new r("abl",-1,3),new r("Ièr",-1,4),new r("ièr",-1,4),new r("eus",-1,2),new r("iv",-1,1)],y=[new r("ic",-1,2),new r("abil",-1,1),new r("iv",-1,3)],C=[new r("iqUe",-1,1),new r("atrice",-1,2),new r("ance",-1,1),new r("ence",-1,5),new r("logie",-1,3),new r("able",-1,1),new r("isme",-1,1),new r("euse",-1,11),new r("iste",-1,1),new r("ive",-1,8),new r("if",-1,8),new r("usion",-1,4),new r("ation",-1,2),new r("ution",-1,4),new r("ateur",-1,2),new r("iqUes",-1,1),new r("atrices",-1,2),new r("ances",-1,1),new r("ences",-1,5),new r("logies",-1,3),new r("ables",-1,1),new r("ismes",-1,1),new r("euses",-1,11),new r("istes",-1,1),new r("ives",-1,8),new r("ifs",-1,8),new r("usions",-1,4),new r("ations",-1,2),new r("utions",-1,4),new r("ateurs",-1,2),new r("ments",-1,15),new r("ements",30,6),new r("issements",31,12),new r("ités",-1,7),new r("ment",-1,15),new r("ement",34,6),new r("issement",35,12),new r("amment",34,13),new r("emment",34,14),new r("aux",-1,10),new r("eaux",39,9),new r("eux",-1,1),new r("ité",-1,7)],x=[new r("ira",-1,1),new r("ie",-1,1),new r("isse",-1,1),new r("issante",-1,1),new r("i",-1,1),new r("irai",4,1),new r("ir",-1,1),new r("iras",-1,1),new r("ies",-1,1),new r("îmes",-1,1),new r("isses",-1,1),new r("issantes",-1,1),new r("îtes",-1,1),new r("is",-1,1),new r("irais",13,1),new r("issais",13,1),new r("irions",-1,1),new r("issions",-1,1),new r("irons",-1,1),new r("issons",-1,1),new r("issants",-1,1),new r("it",-1,1),new r("irait",21,1),new r("issait",21,1),new r("issant",-1,1),new r("iraIent",-1,1),new r("issaIent",-1,1),new r("irent",-1,1),new r("issent",-1,1),new r("iront",-1,1),new r("ît",-1,1),new r("iriez",-1,1),new r("issiez",-1,1),new r("irez",-1,1),new r("issez",-1,1)],I=[new r("a",-1,3),new r("era",0,2),new r("asse",-1,3),new r("ante",-1,3),new r("ée",-1,2),new r("ai",-1,3),new r("erai",5,2),new r("er",-1,2),new r("as",-1,3),new r("eras",8,2),new r("âmes",-1,3),new r("asses",-1,3),new r("antes",-1,3),new r("âtes",-1,3),new r("ées",-1,2),new r("ais",-1,3),new r("erais",15,2),new r("ions",-1,1),new r("erions",17,2),new r("assions",17,3),new r("erons",-1,2),new r("ants",-1,3),new r("és",-1,2),new r("ait",-1,3),new r("erait",23,2),new r("ant",-1,3),new r("aIent",-1,3),new r("eraIent",26,2),new r("èrent",-1,2),new r("assent",-1,3),new r("eront",-1,2),new r("ât",-1,3),new r("ez",-1,2),new r("iez",32,2),new r("eriez",33,2),new r("assiez",33,3),new r("erez",32,2),new r("é",-1,2)],P=[new r("e",-1,3),new r("Ière",0,2),new r("ière",0,2),new r("ion",-1,1),new r("Ier",-1,2),new r("ier",-1,2),new r("ë",-1,4)],U=[new r("ell",-1,-1),new r("eill",-1,-1),new r("enn",-1,-1),new r("onn",-1,-1),new r("ett",-1,-1)],F=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,128,130,103,8,5],S=[1,65,20,0,0,0,0,0,0,0,0,0,0,0,0,0,128],W=new s;this.setCurrent=function(e){W.setCurrent(e)},this.getCurrent=function(){return W.getCurrent()},this.stem=function(){var e=W.cursor;return n(),W.cursor=e,u(),W.limit_backward=e,W.cursor=W.limit,k(),W.cursor=W.limit,b(),W.cursor=W.limit,d(),W.cursor=W.limit_backward,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.fr.stemmer,"stemmer-fr"),e.fr.stopWordFilter=e.generateStopWordFilter("ai aie aient aies ait as au aura aurai auraient aurais aurait auras aurez auriez aurions aurons auront aux avaient avais avait avec avez aviez avions avons ayant ayez ayons c ce ceci celà ces cet cette d dans de des du elle en es est et eu eue eues eurent eus eusse eussent eusses eussiez eussions eut eux eûmes eût eûtes furent fus fusse fussent fusses fussiez fussions fut fûmes fût fûtes ici il ils j je l la le les leur leurs lui m ma mais me mes moi mon même n ne nos notre nous on ont ou par pas pour qu que quel quelle quelles quels qui s sa sans se sera serai seraient serais serait seras serez seriez serions serons seront ses soi soient sois soit sommes son sont soyez soyons suis sur t ta te tes toi ton tu un une vos votre vous y à étaient étais était étant étiez étions été étée étées étés êtes".split(" ")),e.Pipeline.registerFunction(e.fr.stopWordFilter,"stopWordFilter-fr")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.he.min.js b/assets/javascripts/lunr/min/lunr.he.min.js new file mode 100644 index 0000000000..b863d3eae0 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.he.min.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.he=function(){this.pipeline.reset(),this.pipeline.add(e.he.trimmer,e.he.stopWordFilter,e.he.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.he.stemmer))},e.he.wordCharacters="֑-״א-תa-zA-Za-zA-Z0-90-9",e.he.trimmer=e.trimmerSupport.generateTrimmer(e.he.wordCharacters),e.Pipeline.registerFunction(e.he.trimmer,"trimmer-he"),e.he.stemmer=function(){var e=this;return e.result=!1,e.preRemoved=!1,e.sufRemoved=!1,e.pre={pre1:"ה ו י ת",pre2:"ב כ ל מ ש כש",pre3:"הב הכ הל המ הש בש לכ",pre4:"וב וכ ול ומ וש",pre5:"מה שה כל",pre6:"מב מכ מל ממ מש",pre7:"בה בו בי בת כה כו כי כת לה לו לי לת",pre8:"ובה ובו ובי ובת וכה וכו וכי וכת ולה ולו ולי ולת"},e.suf={suf1:"ך כ ם ן נ",suf2:"ים ות וך וכ ום ון ונ הם הן יכ יך ינ ים",suf3:"תי תך תכ תם תן תנ",suf4:"ותי ותך ותכ ותם ותן ותנ",suf5:"נו כם כן הם הן",suf6:"ונו וכם וכן והם והן",suf7:"תכם תכן תנו תהם תהן",suf8:"הוא היא הם הן אני אתה את אנו אתם אתן",suf9:"ני נו כי כו כם כן תי תך תכ תם תן",suf10:"י ך כ ם ן נ ת"},e.patterns=JSON.parse('{"hebrewPatterns": [{"pt1": [{"c": "ה", "l": 0}]}, {"pt2": [{"c": "ו", "l": 0}]}, {"pt3": [{"c": "י", "l": 0}]}, {"pt4": [{"c": "ת", "l": 0}]}, {"pt5": [{"c": "מ", "l": 0}]}, {"pt6": [{"c": "ל", "l": 0}]}, {"pt7": [{"c": "ב", "l": 0}]}, {"pt8": [{"c": "כ", "l": 0}]}, {"pt9": [{"c": "ש", "l": 0}]}, {"pt10": [{"c": "כש", "l": 0}]}, {"pt11": [{"c": "בה", "l": 0}]}, {"pt12": [{"c": "וב", "l": 0}]}, {"pt13": [{"c": "וכ", "l": 0}]}, {"pt14": [{"c": "ול", "l": 0}]}, {"pt15": [{"c": "ומ", "l": 0}]}, {"pt16": [{"c": "וש", "l": 0}]}, {"pt17": [{"c": "הב", "l": 0}]}, {"pt18": [{"c": "הכ", "l": 0}]}, {"pt19": [{"c": "הל", "l": 0}]}, {"pt20": [{"c": "המ", "l": 0}]}, {"pt21": [{"c": "הש", "l": 0}]}, {"pt22": [{"c": "מה", "l": 0}]}, {"pt23": [{"c": "שה", "l": 0}]}, {"pt24": [{"c": "כל", "l": 0}]}]}'),e.execArray=["cleanWord","removeDiacritics","removeStopWords","normalizeHebrewCharacters"],e.stem=function(){var r=0;for(e.result=!1,e.preRemoved=!1,e.sufRemoved=!1;r=0)return!0},e.normalizeHebrewCharacters=function(){return e.word=e.word.replace("ך","כ"),e.word=e.word.replace("ם","מ"),e.word=e.word.replace("ן","נ"),e.word=e.word.replace("ף","פ"),e.word=e.word.replace("ץ","צ"),!1},function(r){return"function"==typeof r.update?r.update(function(r){return e.setCurrent(r),e.stem(),e.getCurrent()}):(e.setCurrent(r),e.stem(),e.getCurrent())}}(),e.Pipeline.registerFunction(e.he.stemmer,"stemmer-he"),e.he.stopWordFilter=e.generateStopWordFilter("אבל או אולי אותו אותי אותך אותם אותן אותנו אז אחר אחרות אחרי אחריכן אחרים אחרת אי איזה איך אין איפה אל אלה אלו אם אנחנו אני אף אפשר את אתה אתכם אתכן אתם אתן באיזה באיזו בגלל בין בלבד בעבור בעזרת בכל בכן בלי במידה במקום שבו ברוב בשביל בשעה ש בתוך גם דרך הוא היא היה היי היכן היתה היתי הם הן הנה הסיבה שבגללה הרי ואילו ואת זאת זה זות יהיה יוכל יוכלו יותר מדי יכול יכולה יכולות יכולים יכל יכלה יכלו יש כאן כאשר כולם כולן כזה כי כיצד כך כל כלל כמו כן כפי כש לא לאו לאיזותך לאן לבין לה להיות להם להן לו לזה לזות לי לך לכם לכן למה למעלה למעלה מ למטה למטה מ למעט למקום שבו למרות לנו לעבר לעיכן לפיכך לפני מאד מאחורי מאיזו סיבה מאין מאיפה מבלי מבעד מדוע מה מהיכן מול מחוץ מי מידע מכאן מכל מכן מלבד מן מנין מסוגל מעט מעטים מעל מצד מקום בו מתחת מתי נגד נגר נו עד עז על עלי עליו עליה עליהם עליך עלינו עם עצמה עצמהם עצמהן עצמו עצמי עצמם עצמן עצמנו פה רק שוב של שלה שלהם שלהן שלו שלי שלך שלכה שלכם שלכן שלנו שם תהיה תחת".split(" ")),e.Pipeline.registerFunction(e.he.stopWordFilter,"stopWordFilter-he")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.hi.min.js b/assets/javascripts/lunr/min/lunr.hi.min.js new file mode 100644 index 0000000000..7dbc41402c --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.hi.min.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.hi=function(){this.pipeline.reset(),this.pipeline.add(e.hi.trimmer,e.hi.stopWordFilter,e.hi.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.hi.stemmer))},e.hi.wordCharacters="ऀ-ःऄ-एऐ-टठ-यर-िी-ॏॐ-य़ॠ-९॰-ॿa-zA-Za-zA-Z0-90-9",e.hi.trimmer=e.trimmerSupport.generateTrimmer(e.hi.wordCharacters),e.Pipeline.registerFunction(e.hi.trimmer,"trimmer-hi"),e.hi.stopWordFilter=e.generateStopWordFilter("अत अपना अपनी अपने अभी अंदर आदि आप इत्यादि इन इनका इन्हीं इन्हें इन्हों इस इसका इसकी इसके इसमें इसी इसे उन उनका उनकी उनके उनको उन्हीं उन्हें उन्हों उस उसके उसी उसे एक एवं एस ऐसे और कई कर करता करते करना करने करें कहते कहा का काफ़ी कि कितना किन्हें किन्हों किया किर किस किसी किसे की कुछ कुल के को कोई कौन कौनसा गया घर जब जहाँ जा जितना जिन जिन्हें जिन्हों जिस जिसे जीधर जैसा जैसे जो तक तब तरह तिन तिन्हें तिन्हों तिस तिसे तो था थी थे दबारा दिया दुसरा दूसरे दो द्वारा न नके नहीं ना निहायत नीचे ने पर पहले पूरा पे फिर बनी बही बहुत बाद बाला बिलकुल भी भीतर मगर मानो मे में यदि यह यहाँ यही या यिह ये रखें रहा रहे ऱ्वासा लिए लिये लेकिन व वग़ैरह वर्ग वह वहाँ वहीं वाले वुह वे वो सकता सकते सबसे सभी साथ साबुत साभ सारा से सो संग ही हुआ हुई हुए है हैं हो होता होती होते होना होने".split(" ")),e.hi.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var r=e.wordcut;r.init(),e.hi.tokenizer=function(i){if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(r){return isLunr2?new e.Token(r.toLowerCase()):r.toLowerCase()});var t=i.toString().toLowerCase().replace(/^\s+/,"");return r.cut(t).split("|")},e.Pipeline.registerFunction(e.hi.stemmer,"stemmer-hi"),e.Pipeline.registerFunction(e.hi.stopWordFilter,"stopWordFilter-hi")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.hu.min.js b/assets/javascripts/lunr/min/lunr.hu.min.js new file mode 100644 index 0000000000..ed9d909f73 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.hu.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Hungarian` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.hu=function(){this.pipeline.reset(),this.pipeline.add(e.hu.trimmer,e.hu.stopWordFilter,e.hu.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.hu.stemmer))},e.hu.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.hu.trimmer=e.trimmerSupport.generateTrimmer(e.hu.wordCharacters),e.Pipeline.registerFunction(e.hu.trimmer,"trimmer-hu"),e.hu.stemmer=function(){var n=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,i=new function(){function e(){var e,n=L.cursor;if(d=L.limit,L.in_grouping(W,97,252))for(;;){if(e=L.cursor,L.out_grouping(W,97,252))return L.cursor=e,L.find_among(g,8)||(L.cursor=e,e=L.limit)return void(d=e);L.cursor++}if(L.cursor=n,L.out_grouping(W,97,252)){for(;!L.in_grouping(W,97,252);){if(L.cursor>=L.limit)return;L.cursor++}d=L.cursor}}function i(){return d<=L.cursor}function a(){var e;if(L.ket=L.cursor,(e=L.find_among_b(h,2))&&(L.bra=L.cursor,i()))switch(e){case 1:L.slice_from("a");break;case 2:L.slice_from("e")}}function t(){var e=L.limit-L.cursor;return!!L.find_among_b(p,23)&&(L.cursor=L.limit-e,!0)}function s(){if(L.cursor>L.limit_backward){L.cursor--,L.ket=L.cursor;var e=L.cursor-1;L.limit_backward<=e&&e<=L.limit&&(L.cursor=e,L.bra=e,L.slice_del())}}function c(){var e;if(L.ket=L.cursor,(e=L.find_among_b(_,2))&&(L.bra=L.cursor,i())){if((1==e||2==e)&&!t())return;L.slice_del(),s()}}function o(){L.ket=L.cursor,L.find_among_b(v,44)&&(L.bra=L.cursor,i()&&(L.slice_del(),a()))}function w(){var e;if(L.ket=L.cursor,(e=L.find_among_b(z,3))&&(L.bra=L.cursor,i()))switch(e){case 1:L.slice_from("e");break;case 2:case 3:L.slice_from("a")}}function l(){var e;if(L.ket=L.cursor,(e=L.find_among_b(y,6))&&(L.bra=L.cursor,i()))switch(e){case 1:case 2:L.slice_del();break;case 3:L.slice_from("a");break;case 4:L.slice_from("e")}}function u(){var e;if(L.ket=L.cursor,(e=L.find_among_b(j,2))&&(L.bra=L.cursor,i())){if((1==e||2==e)&&!t())return;L.slice_del(),s()}}function m(){var e;if(L.ket=L.cursor,(e=L.find_among_b(C,7))&&(L.bra=L.cursor,i()))switch(e){case 1:L.slice_from("a");break;case 2:L.slice_from("e");break;case 3:case 4:case 5:case 6:case 7:L.slice_del()}}function k(){var e;if(L.ket=L.cursor,(e=L.find_among_b(P,12))&&(L.bra=L.cursor,i()))switch(e){case 1:case 4:case 7:case 9:L.slice_del();break;case 2:case 5:case 8:L.slice_from("e");break;case 3:case 6:L.slice_from("a")}}function f(){var e;if(L.ket=L.cursor,(e=L.find_among_b(F,31))&&(L.bra=L.cursor,i()))switch(e){case 1:case 4:case 7:case 8:case 9:case 12:case 13:case 16:case 17:case 18:L.slice_del();break;case 2:case 5:case 10:case 14:case 19:L.slice_from("a");break;case 3:case 6:case 11:case 15:case 20:L.slice_from("e")}}function b(){var e;if(L.ket=L.cursor,(e=L.find_among_b(S,42))&&(L.bra=L.cursor,i()))switch(e){case 1:case 4:case 5:case 6:case 9:case 10:case 11:case 14:case 15:case 16:case 17:case 20:case 21:case 24:case 25:case 26:case 29:L.slice_del();break;case 2:case 7:case 12:case 18:case 22:case 27:L.slice_from("a");break;case 3:case 8:case 13:case 19:case 23:case 28:L.slice_from("e")}}var d,g=[new n("cs",-1,-1),new n("dzs",-1,-1),new n("gy",-1,-1),new n("ly",-1,-1),new n("ny",-1,-1),new n("sz",-1,-1),new n("ty",-1,-1),new n("zs",-1,-1)],h=[new n("á",-1,1),new n("é",-1,2)],p=[new n("bb",-1,-1),new n("cc",-1,-1),new n("dd",-1,-1),new n("ff",-1,-1),new n("gg",-1,-1),new n("jj",-1,-1),new n("kk",-1,-1),new n("ll",-1,-1),new n("mm",-1,-1),new n("nn",-1,-1),new n("pp",-1,-1),new n("rr",-1,-1),new n("ccs",-1,-1),new n("ss",-1,-1),new n("zzs",-1,-1),new n("tt",-1,-1),new n("vv",-1,-1),new n("ggy",-1,-1),new n("lly",-1,-1),new n("nny",-1,-1),new n("tty",-1,-1),new n("ssz",-1,-1),new n("zz",-1,-1)],_=[new n("al",-1,1),new n("el",-1,2)],v=[new n("ba",-1,-1),new n("ra",-1,-1),new n("be",-1,-1),new n("re",-1,-1),new n("ig",-1,-1),new n("nak",-1,-1),new n("nek",-1,-1),new n("val",-1,-1),new n("vel",-1,-1),new n("ul",-1,-1),new n("nál",-1,-1),new n("nél",-1,-1),new n("ból",-1,-1),new n("ról",-1,-1),new n("tól",-1,-1),new n("bõl",-1,-1),new n("rõl",-1,-1),new n("tõl",-1,-1),new n("ül",-1,-1),new n("n",-1,-1),new n("an",19,-1),new n("ban",20,-1),new n("en",19,-1),new n("ben",22,-1),new n("képpen",22,-1),new n("on",19,-1),new n("ön",19,-1),new n("képp",-1,-1),new n("kor",-1,-1),new n("t",-1,-1),new n("at",29,-1),new n("et",29,-1),new n("ként",29,-1),new n("anként",32,-1),new n("enként",32,-1),new n("onként",32,-1),new n("ot",29,-1),new n("ért",29,-1),new n("öt",29,-1),new n("hez",-1,-1),new n("hoz",-1,-1),new n("höz",-1,-1),new n("vá",-1,-1),new n("vé",-1,-1)],z=[new n("án",-1,2),new n("én",-1,1),new n("ánként",-1,3)],y=[new n("stul",-1,2),new n("astul",0,1),new n("ástul",0,3),new n("stül",-1,2),new n("estül",3,1),new n("éstül",3,4)],j=[new n("á",-1,1),new n("é",-1,2)],C=[new n("k",-1,7),new n("ak",0,4),new n("ek",0,6),new n("ok",0,5),new n("ák",0,1),new n("ék",0,2),new n("ök",0,3)],P=[new n("éi",-1,7),new n("áéi",0,6),new n("ééi",0,5),new n("é",-1,9),new n("ké",3,4),new n("aké",4,1),new n("eké",4,1),new n("oké",4,1),new n("áké",4,3),new n("éké",4,2),new n("öké",4,1),new n("éé",3,8)],F=[new n("a",-1,18),new n("ja",0,17),new n("d",-1,16),new n("ad",2,13),new n("ed",2,13),new n("od",2,13),new n("ád",2,14),new n("éd",2,15),new n("öd",2,13),new n("e",-1,18),new n("je",9,17),new n("nk",-1,4),new n("unk",11,1),new n("ánk",11,2),new n("énk",11,3),new n("ünk",11,1),new n("uk",-1,8),new n("juk",16,7),new n("ájuk",17,5),new n("ük",-1,8),new n("jük",19,7),new n("éjük",20,6),new n("m",-1,12),new n("am",22,9),new n("em",22,9),new n("om",22,9),new n("ám",22,10),new n("ém",22,11),new n("o",-1,18),new n("á",-1,19),new n("é",-1,20)],S=[new n("id",-1,10),new n("aid",0,9),new n("jaid",1,6),new n("eid",0,9),new n("jeid",3,6),new n("áid",0,7),new n("éid",0,8),new n("i",-1,15),new n("ai",7,14),new n("jai",8,11),new n("ei",7,14),new n("jei",10,11),new n("ái",7,12),new n("éi",7,13),new n("itek",-1,24),new n("eitek",14,21),new n("jeitek",15,20),new n("éitek",14,23),new n("ik",-1,29),new n("aik",18,26),new n("jaik",19,25),new n("eik",18,26),new n("jeik",21,25),new n("áik",18,27),new n("éik",18,28),new n("ink",-1,20),new n("aink",25,17),new n("jaink",26,16),new n("eink",25,17),new n("jeink",28,16),new n("áink",25,18),new n("éink",25,19),new n("aitok",-1,21),new n("jaitok",32,20),new n("áitok",-1,22),new n("im",-1,5),new n("aim",35,4),new n("jaim",36,1),new n("eim",35,4),new n("jeim",38,1),new n("áim",35,2),new n("éim",35,3)],W=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,52,14],L=new r;this.setCurrent=function(e){L.setCurrent(e)},this.getCurrent=function(){return L.getCurrent()},this.stem=function(){var n=L.cursor;return e(),L.limit_backward=n,L.cursor=L.limit,c(),L.cursor=L.limit,o(),L.cursor=L.limit,w(),L.cursor=L.limit,l(),L.cursor=L.limit,u(),L.cursor=L.limit,k(),L.cursor=L.limit,f(),L.cursor=L.limit,b(),L.cursor=L.limit,m(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.hu.stemmer,"stemmer-hu"),e.hu.stopWordFilter=e.generateStopWordFilter("a abban ahhoz ahogy ahol aki akik akkor alatt amely amelyek amelyekben amelyeket amelyet amelynek ami amikor amit amolyan amíg annak arra arról az azok azon azonban azt aztán azután azzal azért be belül benne bár cikk cikkek cikkeket csak de e ebben eddig egy egyes egyetlen egyik egyre egyéb egész ehhez ekkor el ellen elsõ elég elõ elõször elõtt emilyen ennek erre ez ezek ezen ezt ezzel ezért fel felé hanem hiszen hogy hogyan igen ill ill. illetve ilyen ilyenkor ismét ison itt jobban jó jól kell kellett keressünk keresztül ki kívül között közül legalább legyen lehet lehetett lenne lenni lesz lett maga magát majd majd meg mellett mely melyek mert mi mikor milyen minden mindenki mindent mindig mint mintha mit mivel miért most már más másik még míg nagy nagyobb nagyon ne nekem neki nem nincs néha néhány nélkül olyan ott pedig persze rá s saját sem semmi sok sokat sokkal szemben szerint szinte számára talán tehát teljes tovább továbbá több ugyanis utolsó után utána vagy vagyis vagyok valaki valami valamint való van vannak vele vissza viszont volna volt voltak voltam voltunk által általában át én éppen és így õ õk õket össze úgy új újabb újra".split(" ")),e.Pipeline.registerFunction(e.hu.stopWordFilter,"stopWordFilter-hu")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.hy.min.js b/assets/javascripts/lunr/min/lunr.hy.min.js new file mode 100644 index 0000000000..b37f792985 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.hy.min.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.hy=function(){this.pipeline.reset(),this.pipeline.add(e.hy.trimmer,e.hy.stopWordFilter)},e.hy.wordCharacters="[A-Za-z԰-֏ff-ﭏ]",e.hy.trimmer=e.trimmerSupport.generateTrimmer(e.hy.wordCharacters),e.Pipeline.registerFunction(e.hy.trimmer,"trimmer-hy"),e.hy.stopWordFilter=e.generateStopWordFilter("դու և եք էիր էիք հետո նաև նրանք որը վրա է որ պիտի են այս մեջ ն իր ու ի այդ որոնք այն կամ էր մի ես համար այլ իսկ էին ենք հետ ին թ էինք մենք նրա նա դուք եմ էի ըստ որպես ում".split(" ")),e.Pipeline.registerFunction(e.hy.stopWordFilter,"stopWordFilter-hy"),e.hy.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}(),e.Pipeline.registerFunction(e.hy.stemmer,"stemmer-hy")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.it.min.js b/assets/javascripts/lunr/min/lunr.it.min.js new file mode 100644 index 0000000000..344b6a3c0c --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.it.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Italian` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.it=function(){this.pipeline.reset(),this.pipeline.add(e.it.trimmer,e.it.stopWordFilter,e.it.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.it.stemmer))},e.it.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.it.trimmer=e.trimmerSupport.generateTrimmer(e.it.wordCharacters),e.Pipeline.registerFunction(e.it.trimmer,"trimmer-it"),e.it.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){function e(e,r,n){return!(!x.eq_s(1,e)||(x.ket=x.cursor,!x.in_grouping(L,97,249)))&&(x.slice_from(r),x.cursor=n,!0)}function i(){for(var r,n,i,o,t=x.cursor;;){if(x.bra=x.cursor,r=x.find_among(h,7))switch(x.ket=x.cursor,r){case 1:x.slice_from("à");continue;case 2:x.slice_from("è");continue;case 3:x.slice_from("ì");continue;case 4:x.slice_from("ò");continue;case 5:x.slice_from("ù");continue;case 6:x.slice_from("qU");continue;case 7:if(x.cursor>=x.limit)break;x.cursor++;continue}break}for(x.cursor=t;;)for(n=x.cursor;;){if(i=x.cursor,x.in_grouping(L,97,249)){if(x.bra=x.cursor,o=x.cursor,e("u","U",i))break;if(x.cursor=o,e("i","I",i))break}if(x.cursor=i,x.cursor>=x.limit)return void(x.cursor=n);x.cursor++}}function o(e){if(x.cursor=e,!x.in_grouping(L,97,249))return!1;for(;!x.out_grouping(L,97,249);){if(x.cursor>=x.limit)return!1;x.cursor++}return!0}function t(){if(x.in_grouping(L,97,249)){var e=x.cursor;if(x.out_grouping(L,97,249)){for(;!x.in_grouping(L,97,249);){if(x.cursor>=x.limit)return o(e);x.cursor++}return!0}return o(e)}return!1}function s(){var e,r=x.cursor;if(!t()){if(x.cursor=r,!x.out_grouping(L,97,249))return;if(e=x.cursor,x.out_grouping(L,97,249)){for(;!x.in_grouping(L,97,249);){if(x.cursor>=x.limit)return x.cursor=e,void(x.in_grouping(L,97,249)&&x.cursor=x.limit)return;x.cursor++}k=x.cursor}function a(){for(;!x.in_grouping(L,97,249);){if(x.cursor>=x.limit)return!1;x.cursor++}for(;!x.out_grouping(L,97,249);){if(x.cursor>=x.limit)return!1;x.cursor++}return!0}function u(){var e=x.cursor;k=x.limit,p=k,g=k,s(),x.cursor=e,a()&&(p=x.cursor,a()&&(g=x.cursor))}function c(){for(var e;;){if(x.bra=x.cursor,!(e=x.find_among(q,3)))break;switch(x.ket=x.cursor,e){case 1:x.slice_from("i");break;case 2:x.slice_from("u");break;case 3:if(x.cursor>=x.limit)return;x.cursor++}}}function w(){return k<=x.cursor}function l(){return p<=x.cursor}function m(){return g<=x.cursor}function f(){var e;if(x.ket=x.cursor,x.find_among_b(C,37)&&(x.bra=x.cursor,(e=x.find_among_b(z,5))&&w()))switch(e){case 1:x.slice_del();break;case 2:x.slice_from("e")}}function v(){var e;if(x.ket=x.cursor,!(e=x.find_among_b(S,51)))return!1;switch(x.bra=x.cursor,e){case 1:if(!m())return!1;x.slice_del();break;case 2:if(!m())return!1;x.slice_del(),x.ket=x.cursor,x.eq_s_b(2,"ic")&&(x.bra=x.cursor,m()&&x.slice_del());break;case 3:if(!m())return!1;x.slice_from("log");break;case 4:if(!m())return!1;x.slice_from("u");break;case 5:if(!m())return!1;x.slice_from("ente");break;case 6:if(!w())return!1;x.slice_del();break;case 7:if(!l())return!1;x.slice_del(),x.ket=x.cursor,e=x.find_among_b(P,4),e&&(x.bra=x.cursor,m()&&(x.slice_del(),1==e&&(x.ket=x.cursor,x.eq_s_b(2,"at")&&(x.bra=x.cursor,m()&&x.slice_del()))));break;case 8:if(!m())return!1;x.slice_del(),x.ket=x.cursor,e=x.find_among_b(F,3),e&&(x.bra=x.cursor,1==e&&m()&&x.slice_del());break;case 9:if(!m())return!1;x.slice_del(),x.ket=x.cursor,x.eq_s_b(2,"at")&&(x.bra=x.cursor,m()&&(x.slice_del(),x.ket=x.cursor,x.eq_s_b(2,"ic")&&(x.bra=x.cursor,m()&&x.slice_del())))}return!0}function b(){var e,r;x.cursor>=k&&(r=x.limit_backward,x.limit_backward=k,x.ket=x.cursor,e=x.find_among_b(W,87),e&&(x.bra=x.cursor,1==e&&x.slice_del()),x.limit_backward=r)}function d(){var e=x.limit-x.cursor;if(x.ket=x.cursor,x.in_grouping_b(y,97,242)&&(x.bra=x.cursor,w()&&(x.slice_del(),x.ket=x.cursor,x.eq_s_b(1,"i")&&(x.bra=x.cursor,w()))))return void x.slice_del();x.cursor=x.limit-e}function _(){d(),x.ket=x.cursor,x.eq_s_b(1,"h")&&(x.bra=x.cursor,x.in_grouping_b(U,99,103)&&w()&&x.slice_del())}var g,p,k,h=[new r("",-1,7),new r("qu",0,6),new r("á",0,1),new r("é",0,2),new r("í",0,3),new r("ó",0,4),new r("ú",0,5)],q=[new r("",-1,3),new r("I",0,1),new r("U",0,2)],C=[new r("la",-1,-1),new r("cela",0,-1),new r("gliela",0,-1),new r("mela",0,-1),new r("tela",0,-1),new r("vela",0,-1),new r("le",-1,-1),new r("cele",6,-1),new r("gliele",6,-1),new r("mele",6,-1),new r("tele",6,-1),new r("vele",6,-1),new r("ne",-1,-1),new r("cene",12,-1),new r("gliene",12,-1),new r("mene",12,-1),new r("sene",12,-1),new r("tene",12,-1),new r("vene",12,-1),new r("ci",-1,-1),new r("li",-1,-1),new r("celi",20,-1),new r("glieli",20,-1),new r("meli",20,-1),new r("teli",20,-1),new r("veli",20,-1),new r("gli",20,-1),new r("mi",-1,-1),new r("si",-1,-1),new r("ti",-1,-1),new r("vi",-1,-1),new r("lo",-1,-1),new r("celo",31,-1),new r("glielo",31,-1),new r("melo",31,-1),new r("telo",31,-1),new r("velo",31,-1)],z=[new r("ando",-1,1),new r("endo",-1,1),new r("ar",-1,2),new r("er",-1,2),new r("ir",-1,2)],P=[new r("ic",-1,-1),new r("abil",-1,-1),new r("os",-1,-1),new r("iv",-1,1)],F=[new r("ic",-1,1),new r("abil",-1,1),new r("iv",-1,1)],S=[new r("ica",-1,1),new r("logia",-1,3),new r("osa",-1,1),new r("ista",-1,1),new r("iva",-1,9),new r("anza",-1,1),new r("enza",-1,5),new r("ice",-1,1),new r("atrice",7,1),new r("iche",-1,1),new r("logie",-1,3),new r("abile",-1,1),new r("ibile",-1,1),new r("usione",-1,4),new r("azione",-1,2),new r("uzione",-1,4),new r("atore",-1,2),new r("ose",-1,1),new r("ante",-1,1),new r("mente",-1,1),new r("amente",19,7),new r("iste",-1,1),new r("ive",-1,9),new r("anze",-1,1),new r("enze",-1,5),new r("ici",-1,1),new r("atrici",25,1),new r("ichi",-1,1),new r("abili",-1,1),new r("ibili",-1,1),new r("ismi",-1,1),new r("usioni",-1,4),new r("azioni",-1,2),new r("uzioni",-1,4),new r("atori",-1,2),new r("osi",-1,1),new r("anti",-1,1),new r("amenti",-1,6),new r("imenti",-1,6),new r("isti",-1,1),new r("ivi",-1,9),new r("ico",-1,1),new r("ismo",-1,1),new r("oso",-1,1),new r("amento",-1,6),new r("imento",-1,6),new r("ivo",-1,9),new r("ità",-1,8),new r("istà",-1,1),new r("istè",-1,1),new r("istì",-1,1)],W=[new r("isca",-1,1),new r("enda",-1,1),new r("ata",-1,1),new r("ita",-1,1),new r("uta",-1,1),new r("ava",-1,1),new r("eva",-1,1),new r("iva",-1,1),new r("erebbe",-1,1),new r("irebbe",-1,1),new r("isce",-1,1),new r("ende",-1,1),new r("are",-1,1),new r("ere",-1,1),new r("ire",-1,1),new r("asse",-1,1),new r("ate",-1,1),new r("avate",16,1),new r("evate",16,1),new r("ivate",16,1),new r("ete",-1,1),new r("erete",20,1),new r("irete",20,1),new r("ite",-1,1),new r("ereste",-1,1),new r("ireste",-1,1),new r("ute",-1,1),new r("erai",-1,1),new r("irai",-1,1),new r("isci",-1,1),new r("endi",-1,1),new r("erei",-1,1),new r("irei",-1,1),new r("assi",-1,1),new r("ati",-1,1),new r("iti",-1,1),new r("eresti",-1,1),new r("iresti",-1,1),new r("uti",-1,1),new r("avi",-1,1),new r("evi",-1,1),new r("ivi",-1,1),new r("isco",-1,1),new r("ando",-1,1),new r("endo",-1,1),new r("Yamo",-1,1),new r("iamo",-1,1),new r("avamo",-1,1),new r("evamo",-1,1),new r("ivamo",-1,1),new r("eremo",-1,1),new r("iremo",-1,1),new r("assimo",-1,1),new r("ammo",-1,1),new r("emmo",-1,1),new r("eremmo",54,1),new r("iremmo",54,1),new r("immo",-1,1),new r("ano",-1,1),new r("iscano",58,1),new r("avano",58,1),new r("evano",58,1),new r("ivano",58,1),new r("eranno",-1,1),new r("iranno",-1,1),new r("ono",-1,1),new r("iscono",65,1),new r("arono",65,1),new r("erono",65,1),new r("irono",65,1),new r("erebbero",-1,1),new r("irebbero",-1,1),new r("assero",-1,1),new r("essero",-1,1),new r("issero",-1,1),new r("ato",-1,1),new r("ito",-1,1),new r("uto",-1,1),new r("avo",-1,1),new r("evo",-1,1),new r("ivo",-1,1),new r("ar",-1,1),new r("ir",-1,1),new r("erà",-1,1),new r("irà",-1,1),new r("erò",-1,1),new r("irò",-1,1)],L=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,128,128,8,2,1],y=[17,65,0,0,0,0,0,0,0,0,0,0,0,0,0,128,128,8,2],U=[17],x=new n;this.setCurrent=function(e){x.setCurrent(e)},this.getCurrent=function(){return x.getCurrent()},this.stem=function(){var e=x.cursor;return i(),x.cursor=e,u(),x.limit_backward=e,x.cursor=x.limit,f(),x.cursor=x.limit,v()||(x.cursor=x.limit,b()),x.cursor=x.limit,_(),x.cursor=x.limit_backward,c(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.it.stemmer,"stemmer-it"),e.it.stopWordFilter=e.generateStopWordFilter("a abbia abbiamo abbiano abbiate ad agl agli ai al all alla alle allo anche avemmo avendo avesse avessero avessi avessimo aveste avesti avete aveva avevamo avevano avevate avevi avevo avrai avranno avrebbe avrebbero avrei avremmo avremo avreste avresti avrete avrà avrò avuta avute avuti avuto c che chi ci coi col come con contro cui da dagl dagli dai dal dall dalla dalle dallo degl degli dei del dell della delle dello di dov dove e ebbe ebbero ebbi ed era erano eravamo eravate eri ero essendo faccia facciamo facciano facciate faccio facemmo facendo facesse facessero facessi facessimo faceste facesti faceva facevamo facevano facevate facevi facevo fai fanno farai faranno farebbe farebbero farei faremmo faremo fareste faresti farete farà farò fece fecero feci fosse fossero fossi fossimo foste fosti fu fui fummo furono gli ha hai hanno ho i il in io l la le lei li lo loro lui ma mi mia mie miei mio ne negl negli nei nel nell nella nelle nello noi non nostra nostre nostri nostro o per perché più quale quanta quante quanti quanto quella quelle quelli quello questa queste questi questo sarai saranno sarebbe sarebbero sarei saremmo saremo sareste saresti sarete sarà sarò se sei si sia siamo siano siate siete sono sta stai stando stanno starai staranno starebbe starebbero starei staremmo staremo stareste staresti starete starà starò stava stavamo stavano stavate stavi stavo stemmo stesse stessero stessi stessimo steste stesti stette stettero stetti stia stiamo stiano stiate sto su sua sue sugl sugli sui sul sull sulla sulle sullo suo suoi ti tra tu tua tue tuo tuoi tutti tutto un una uno vi voi vostra vostre vostri vostro è".split(" ")),e.Pipeline.registerFunction(e.it.stopWordFilter,"stopWordFilter-it")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.ja.min.js b/assets/javascripts/lunr/min/lunr.ja.min.js new file mode 100644 index 0000000000..5f254ebe91 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.ja.min.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r="2"==e.version[0];e.ja=function(){this.pipeline.reset(),this.pipeline.add(e.ja.trimmer,e.ja.stopWordFilter,e.ja.stemmer),r?this.tokenizer=e.ja.tokenizer:(e.tokenizer&&(e.tokenizer=e.ja.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.ja.tokenizer))};var t=new e.TinySegmenter;e.ja.tokenizer=function(i){var n,o,s,p,a,u,m,l,c,f;if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(t){return r?new e.Token(t.toLowerCase()):t.toLowerCase()});for(o=i.toString().toLowerCase().replace(/^\s+/,""),n=o.length-1;n>=0;n--)if(/\S/.test(o.charAt(n))){o=o.substring(0,n+1);break}for(a=[],s=o.length,c=0,l=0;c<=s;c++)if(u=o.charAt(c),m=c-l,u.match(/\s/)||c==s){if(m>0)for(p=t.segment(o.slice(l,c)).filter(function(e){return!!e}),f=l,n=0;n=C.limit)break;C.cursor++;continue}break}for(C.cursor=o,C.bra=o,C.eq_s(1,"y")?(C.ket=C.cursor,C.slice_from("Y")):C.cursor=o;;)if(e=C.cursor,C.in_grouping(q,97,232)){if(i=C.cursor,C.bra=i,C.eq_s(1,"i"))C.ket=C.cursor,C.in_grouping(q,97,232)&&(C.slice_from("I"),C.cursor=e);else if(C.cursor=i,C.eq_s(1,"y"))C.ket=C.cursor,C.slice_from("Y"),C.cursor=e;else if(n(e))break}else if(n(e))break}function n(r){return C.cursor=r,r>=C.limit||(C.cursor++,!1)}function o(){_=C.limit,d=_,t()||(_=C.cursor,_<3&&(_=3),t()||(d=C.cursor))}function t(){for(;!C.in_grouping(q,97,232);){if(C.cursor>=C.limit)return!0;C.cursor++}for(;!C.out_grouping(q,97,232);){if(C.cursor>=C.limit)return!0;C.cursor++}return!1}function s(){for(var r;;)if(C.bra=C.cursor,r=C.find_among(p,3))switch(C.ket=C.cursor,r){case 1:C.slice_from("y");break;case 2:C.slice_from("i");break;case 3:if(C.cursor>=C.limit)return;C.cursor++}}function u(){return _<=C.cursor}function c(){return d<=C.cursor}function a(){var r=C.limit-C.cursor;C.find_among_b(g,3)&&(C.cursor=C.limit-r,C.ket=C.cursor,C.cursor>C.limit_backward&&(C.cursor--,C.bra=C.cursor,C.slice_del()))}function l(){var r;w=!1,C.ket=C.cursor,C.eq_s_b(1,"e")&&(C.bra=C.cursor,u()&&(r=C.limit-C.cursor,C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-r,C.slice_del(),w=!0,a())))}function m(){var r;u()&&(r=C.limit-C.cursor,C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-r,C.eq_s_b(3,"gem")||(C.cursor=C.limit-r,C.slice_del(),a())))}function f(){var r,e,i,n,o,t,s=C.limit-C.cursor;if(C.ket=C.cursor,r=C.find_among_b(h,5))switch(C.bra=C.cursor,r){case 1:u()&&C.slice_from("heid");break;case 2:m();break;case 3:u()&&C.out_grouping_b(j,97,232)&&C.slice_del()}if(C.cursor=C.limit-s,l(),C.cursor=C.limit-s,C.ket=C.cursor,C.eq_s_b(4,"heid")&&(C.bra=C.cursor,c()&&(e=C.limit-C.cursor,C.eq_s_b(1,"c")||(C.cursor=C.limit-e,C.slice_del(),C.ket=C.cursor,C.eq_s_b(2,"en")&&(C.bra=C.cursor,m())))),C.cursor=C.limit-s,C.ket=C.cursor,r=C.find_among_b(k,6))switch(C.bra=C.cursor,r){case 1:if(c()){if(C.slice_del(),i=C.limit-C.cursor,C.ket=C.cursor,C.eq_s_b(2,"ig")&&(C.bra=C.cursor,c()&&(n=C.limit-C.cursor,!C.eq_s_b(1,"e")))){C.cursor=C.limit-n,C.slice_del();break}C.cursor=C.limit-i,a()}break;case 2:c()&&(o=C.limit-C.cursor,C.eq_s_b(1,"e")||(C.cursor=C.limit-o,C.slice_del()));break;case 3:c()&&(C.slice_del(),l());break;case 4:c()&&C.slice_del();break;case 5:c()&&w&&C.slice_del()}C.cursor=C.limit-s,C.out_grouping_b(z,73,232)&&(t=C.limit-C.cursor,C.find_among_b(v,4)&&C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-t,C.ket=C.cursor,C.cursor>C.limit_backward&&(C.cursor--,C.bra=C.cursor,C.slice_del())))}var d,_,w,b=[new e("",-1,6),new e("á",0,1),new e("ä",0,1),new e("é",0,2),new e("ë",0,2),new e("í",0,3),new e("ï",0,3),new e("ó",0,4),new e("ö",0,4),new e("ú",0,5),new e("ü",0,5)],p=[new e("",-1,3),new e("I",0,2),new e("Y",0,1)],g=[new e("dd",-1,-1),new e("kk",-1,-1),new e("tt",-1,-1)],h=[new e("ene",-1,2),new e("se",-1,3),new e("en",-1,2),new e("heden",2,1),new e("s",-1,3)],k=[new e("end",-1,1),new e("ig",-1,2),new e("ing",-1,1),new e("lijk",-1,3),new e("baar",-1,4),new e("bar",-1,5)],v=[new e("aa",-1,-1),new e("ee",-1,-1),new e("oo",-1,-1),new e("uu",-1,-1)],q=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],z=[1,0,0,17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],j=[17,67,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],C=new i;this.setCurrent=function(r){C.setCurrent(r)},this.getCurrent=function(){return C.getCurrent()},this.stem=function(){var e=C.cursor;return r(),C.cursor=e,o(),C.limit_backward=e,C.cursor=C.limit,f(),C.cursor=C.limit_backward,s(),!0}};return function(r){return"function"==typeof r.update?r.update(function(r){return n.setCurrent(r),n.stem(),n.getCurrent()}):(n.setCurrent(r),n.stem(),n.getCurrent())}}(),r.Pipeline.registerFunction(r.nl.stemmer,"stemmer-nl"),r.nl.stopWordFilter=r.generateStopWordFilter(" aan al alles als altijd andere ben bij daar dan dat de der deze die dit doch doen door dus een eens en er ge geen geweest haar had heb hebben heeft hem het hier hij hoe hun iemand iets ik in is ja je kan kon kunnen maar me meer men met mij mijn moet na naar niet niets nog nu of om omdat onder ons ook op over reeds te tegen toch toen tot u uit uw van veel voor want waren was wat werd wezen wie wil worden wordt zal ze zelf zich zij zijn zo zonder zou".split(" ")),r.Pipeline.registerFunction(r.nl.stopWordFilter,"stopWordFilter-nl")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.no.min.js b/assets/javascripts/lunr/min/lunr.no.min.js new file mode 100644 index 0000000000..92bc7e4e89 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.no.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Norwegian` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.no=function(){this.pipeline.reset(),this.pipeline.add(e.no.trimmer,e.no.stopWordFilter,e.no.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.no.stemmer))},e.no.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.no.trimmer=e.trimmerSupport.generateTrimmer(e.no.wordCharacters),e.Pipeline.registerFunction(e.no.trimmer,"trimmer-no"),e.no.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){function e(){var e,r=w.cursor+3;if(a=w.limit,0<=r||r<=w.limit){for(s=r;;){if(e=w.cursor,w.in_grouping(d,97,248)){w.cursor=e;break}if(e>=w.limit)return;w.cursor=e+1}for(;!w.out_grouping(d,97,248);){if(w.cursor>=w.limit)return;w.cursor++}a=w.cursor,a=a&&(r=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,e=w.find_among_b(m,29),w.limit_backward=r,e))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:n=w.limit-w.cursor,w.in_grouping_b(c,98,122)?w.slice_del():(w.cursor=w.limit-n,w.eq_s_b(1,"k")&&w.out_grouping_b(d,97,248)&&w.slice_del());break;case 3:w.slice_from("er")}}function t(){var e,r=w.limit-w.cursor;w.cursor>=a&&(e=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,w.find_among_b(u,2)?(w.bra=w.cursor,w.limit_backward=e,w.cursor=w.limit-r,w.cursor>w.limit_backward&&(w.cursor--,w.bra=w.cursor,w.slice_del())):w.limit_backward=e)}function o(){var e,r;w.cursor>=a&&(r=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,e=w.find_among_b(l,11),e?(w.bra=w.cursor,w.limit_backward=r,1==e&&w.slice_del()):w.limit_backward=r)}var s,a,m=[new r("a",-1,1),new r("e",-1,1),new r("ede",1,1),new r("ande",1,1),new r("ende",1,1),new r("ane",1,1),new r("ene",1,1),new r("hetene",6,1),new r("erte",1,3),new r("en",-1,1),new r("heten",9,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",12,1),new r("s",-1,2),new r("as",14,1),new r("es",14,1),new r("edes",16,1),new r("endes",16,1),new r("enes",16,1),new r("hetenes",19,1),new r("ens",14,1),new r("hetens",21,1),new r("ers",14,1),new r("ets",14,1),new r("et",-1,1),new r("het",25,1),new r("ert",-1,3),new r("ast",-1,1)],u=[new r("dt",-1,-1),new r("vt",-1,-1)],l=[new r("leg",-1,1),new r("eleg",0,1),new r("ig",-1,1),new r("eig",2,1),new r("lig",2,1),new r("elig",4,1),new r("els",-1,1),new r("lov",-1,1),new r("elov",7,1),new r("slov",7,1),new r("hetslov",9,1)],d=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],c=[119,125,149,1],w=new n;this.setCurrent=function(e){w.setCurrent(e)},this.getCurrent=function(){return w.getCurrent()},this.stem=function(){var r=w.cursor;return e(),w.limit_backward=r,w.cursor=w.limit,i(),w.cursor=w.limit,t(),w.cursor=w.limit,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.no.stemmer,"stemmer-no"),e.no.stopWordFilter=e.generateStopWordFilter("alle at av bare begge ble blei bli blir blitt både båe da de deg dei deim deira deires dem den denne der dere deres det dette di din disse ditt du dykk dykkar då eg ein eit eitt eller elles en enn er et ett etter for fordi fra før ha hadde han hans har hennar henne hennes her hjå ho hoe honom hoss hossen hun hva hvem hver hvilke hvilken hvis hvor hvordan hvorfor i ikke ikkje ikkje ingen ingi inkje inn inni ja jeg kan kom korleis korso kun kunne kva kvar kvarhelst kven kvi kvifor man mange me med medan meg meget mellom men mi min mine mitt mot mykje ned no noe noen noka noko nokon nokor nokre nå når og også om opp oss over på samme seg selv si si sia sidan siden sin sine sitt sjøl skal skulle slik so som som somme somt så sånn til um upp ut uten var vart varte ved vere verte vi vil ville vore vors vort vår være være vært å".split(" ")),e.Pipeline.registerFunction(e.no.stopWordFilter,"stopWordFilter-no")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.pt.min.js b/assets/javascripts/lunr/min/lunr.pt.min.js new file mode 100644 index 0000000000..6c16996d65 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.pt.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Portuguese` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.pt=function(){this.pipeline.reset(),this.pipeline.add(e.pt.trimmer,e.pt.stopWordFilter,e.pt.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.pt.stemmer))},e.pt.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.pt.trimmer=e.trimmerSupport.generateTrimmer(e.pt.wordCharacters),e.Pipeline.registerFunction(e.pt.trimmer,"trimmer-pt"),e.pt.stemmer=function(){var r=e.stemmerSupport.Among,s=e.stemmerSupport.SnowballProgram,n=new function(){function e(){for(var e;;){if(z.bra=z.cursor,e=z.find_among(k,3))switch(z.ket=z.cursor,e){case 1:z.slice_from("a~");continue;case 2:z.slice_from("o~");continue;case 3:if(z.cursor>=z.limit)break;z.cursor++;continue}break}}function n(){if(z.out_grouping(y,97,250)){for(;!z.in_grouping(y,97,250);){if(z.cursor>=z.limit)return!0;z.cursor++}return!1}return!0}function i(){if(z.in_grouping(y,97,250))for(;!z.out_grouping(y,97,250);){if(z.cursor>=z.limit)return!1;z.cursor++}return g=z.cursor,!0}function o(){var e,r,s=z.cursor;if(z.in_grouping(y,97,250))if(e=z.cursor,n()){if(z.cursor=e,i())return}else g=z.cursor;if(z.cursor=s,z.out_grouping(y,97,250)){if(r=z.cursor,n()){if(z.cursor=r,!z.in_grouping(y,97,250)||z.cursor>=z.limit)return;z.cursor++}g=z.cursor}}function t(){for(;!z.in_grouping(y,97,250);){if(z.cursor>=z.limit)return!1;z.cursor++}for(;!z.out_grouping(y,97,250);){if(z.cursor>=z.limit)return!1;z.cursor++}return!0}function a(){var e=z.cursor;g=z.limit,b=g,h=g,o(),z.cursor=e,t()&&(b=z.cursor,t()&&(h=z.cursor))}function u(){for(var e;;){if(z.bra=z.cursor,e=z.find_among(q,3))switch(z.ket=z.cursor,e){case 1:z.slice_from("ã");continue;case 2:z.slice_from("õ");continue;case 3:if(z.cursor>=z.limit)break;z.cursor++;continue}break}}function w(){return g<=z.cursor}function m(){return b<=z.cursor}function c(){return h<=z.cursor}function l(){var e;if(z.ket=z.cursor,!(e=z.find_among_b(F,45)))return!1;switch(z.bra=z.cursor,e){case 1:if(!c())return!1;z.slice_del();break;case 2:if(!c())return!1;z.slice_from("log");break;case 3:if(!c())return!1;z.slice_from("u");break;case 4:if(!c())return!1;z.slice_from("ente");break;case 5:if(!m())return!1;z.slice_del(),z.ket=z.cursor,e=z.find_among_b(j,4),e&&(z.bra=z.cursor,c()&&(z.slice_del(),1==e&&(z.ket=z.cursor,z.eq_s_b(2,"at")&&(z.bra=z.cursor,c()&&z.slice_del()))));break;case 6:if(!c())return!1;z.slice_del(),z.ket=z.cursor,e=z.find_among_b(C,3),e&&(z.bra=z.cursor,1==e&&c()&&z.slice_del());break;case 7:if(!c())return!1;z.slice_del(),z.ket=z.cursor,e=z.find_among_b(P,3),e&&(z.bra=z.cursor,1==e&&c()&&z.slice_del());break;case 8:if(!c())return!1;z.slice_del(),z.ket=z.cursor,z.eq_s_b(2,"at")&&(z.bra=z.cursor,c()&&z.slice_del());break;case 9:if(!w()||!z.eq_s_b(1,"e"))return!1;z.slice_from("ir")}return!0}function f(){var e,r;if(z.cursor>=g){if(r=z.limit_backward,z.limit_backward=g,z.ket=z.cursor,e=z.find_among_b(S,120))return z.bra=z.cursor,1==e&&z.slice_del(),z.limit_backward=r,!0;z.limit_backward=r}return!1}function d(){var e;z.ket=z.cursor,(e=z.find_among_b(W,7))&&(z.bra=z.cursor,1==e&&w()&&z.slice_del())}function v(e,r){if(z.eq_s_b(1,e)){z.bra=z.cursor;var s=z.limit-z.cursor;if(z.eq_s_b(1,r))return z.cursor=z.limit-s,w()&&z.slice_del(),!1}return!0}function p(){var e;if(z.ket=z.cursor,e=z.find_among_b(L,4))switch(z.bra=z.cursor,e){case 1:w()&&(z.slice_del(),z.ket=z.cursor,z.limit-z.cursor,v("u","g")&&v("i","c"));break;case 2:z.slice_from("c")}}function _(){if(!l()&&(z.cursor=z.limit,!f()))return z.cursor=z.limit,void d();z.cursor=z.limit,z.ket=z.cursor,z.eq_s_b(1,"i")&&(z.bra=z.cursor,z.eq_s_b(1,"c")&&(z.cursor=z.limit,w()&&z.slice_del()))}var h,b,g,k=[new r("",-1,3),new r("ã",0,1),new r("õ",0,2)],q=[new r("",-1,3),new r("a~",0,1),new r("o~",0,2)],j=[new r("ic",-1,-1),new r("ad",-1,-1),new r("os",-1,-1),new r("iv",-1,1)],C=[new r("ante",-1,1),new r("avel",-1,1),new r("ível",-1,1)],P=[new r("ic",-1,1),new r("abil",-1,1),new r("iv",-1,1)],F=[new r("ica",-1,1),new r("ância",-1,1),new r("ência",-1,4),new r("ira",-1,9),new r("adora",-1,1),new r("osa",-1,1),new r("ista",-1,1),new r("iva",-1,8),new r("eza",-1,1),new r("logía",-1,2),new r("idade",-1,7),new r("ante",-1,1),new r("mente",-1,6),new r("amente",12,5),new r("ável",-1,1),new r("ível",-1,1),new r("ución",-1,3),new r("ico",-1,1),new r("ismo",-1,1),new r("oso",-1,1),new r("amento",-1,1),new r("imento",-1,1),new r("ivo",-1,8),new r("aça~o",-1,1),new r("ador",-1,1),new r("icas",-1,1),new r("ências",-1,4),new r("iras",-1,9),new r("adoras",-1,1),new r("osas",-1,1),new r("istas",-1,1),new r("ivas",-1,8),new r("ezas",-1,1),new r("logías",-1,2),new r("idades",-1,7),new r("uciones",-1,3),new r("adores",-1,1),new r("antes",-1,1),new r("aço~es",-1,1),new r("icos",-1,1),new r("ismos",-1,1),new r("osos",-1,1),new r("amentos",-1,1),new r("imentos",-1,1),new r("ivos",-1,8)],S=[new r("ada",-1,1),new r("ida",-1,1),new r("ia",-1,1),new r("aria",2,1),new r("eria",2,1),new r("iria",2,1),new r("ara",-1,1),new r("era",-1,1),new r("ira",-1,1),new r("ava",-1,1),new r("asse",-1,1),new r("esse",-1,1),new r("isse",-1,1),new r("aste",-1,1),new r("este",-1,1),new r("iste",-1,1),new r("ei",-1,1),new r("arei",16,1),new r("erei",16,1),new r("irei",16,1),new r("am",-1,1),new r("iam",20,1),new r("ariam",21,1),new r("eriam",21,1),new r("iriam",21,1),new r("aram",20,1),new r("eram",20,1),new r("iram",20,1),new r("avam",20,1),new r("em",-1,1),new r("arem",29,1),new r("erem",29,1),new r("irem",29,1),new r("assem",29,1),new r("essem",29,1),new r("issem",29,1),new r("ado",-1,1),new r("ido",-1,1),new r("ando",-1,1),new r("endo",-1,1),new r("indo",-1,1),new r("ara~o",-1,1),new r("era~o",-1,1),new r("ira~o",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("ir",-1,1),new r("as",-1,1),new r("adas",47,1),new r("idas",47,1),new r("ias",47,1),new r("arias",50,1),new r("erias",50,1),new r("irias",50,1),new r("aras",47,1),new r("eras",47,1),new r("iras",47,1),new r("avas",47,1),new r("es",-1,1),new r("ardes",58,1),new r("erdes",58,1),new r("irdes",58,1),new r("ares",58,1),new r("eres",58,1),new r("ires",58,1),new r("asses",58,1),new r("esses",58,1),new r("isses",58,1),new r("astes",58,1),new r("estes",58,1),new r("istes",58,1),new r("is",-1,1),new r("ais",71,1),new r("eis",71,1),new r("areis",73,1),new r("ereis",73,1),new r("ireis",73,1),new r("áreis",73,1),new r("éreis",73,1),new r("íreis",73,1),new r("ásseis",73,1),new r("ésseis",73,1),new r("ísseis",73,1),new r("áveis",73,1),new r("íeis",73,1),new r("aríeis",84,1),new r("eríeis",84,1),new r("iríeis",84,1),new r("ados",-1,1),new r("idos",-1,1),new r("amos",-1,1),new r("áramos",90,1),new r("éramos",90,1),new r("íramos",90,1),new r("ávamos",90,1),new r("íamos",90,1),new r("aríamos",95,1),new r("eríamos",95,1),new r("iríamos",95,1),new r("emos",-1,1),new r("aremos",99,1),new r("eremos",99,1),new r("iremos",99,1),new r("ássemos",99,1),new r("êssemos",99,1),new r("íssemos",99,1),new r("imos",-1,1),new r("armos",-1,1),new r("ermos",-1,1),new r("irmos",-1,1),new r("ámos",-1,1),new r("arás",-1,1),new r("erás",-1,1),new r("irás",-1,1),new r("eu",-1,1),new r("iu",-1,1),new r("ou",-1,1),new r("ará",-1,1),new r("erá",-1,1),new r("irá",-1,1)],W=[new r("a",-1,1),new r("i",-1,1),new r("o",-1,1),new r("os",-1,1),new r("á",-1,1),new r("í",-1,1),new r("ó",-1,1)],L=[new r("e",-1,1),new r("ç",-1,2),new r("é",-1,1),new r("ê",-1,1)],y=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,3,19,12,2],z=new s;this.setCurrent=function(e){z.setCurrent(e)},this.getCurrent=function(){return z.getCurrent()},this.stem=function(){var r=z.cursor;return e(),z.cursor=r,a(),z.limit_backward=r,z.cursor=z.limit,_(),z.cursor=z.limit,p(),z.cursor=z.limit_backward,u(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.pt.stemmer,"stemmer-pt"),e.pt.stopWordFilter=e.generateStopWordFilter("a ao aos aquela aquelas aquele aqueles aquilo as até com como da das de dela delas dele deles depois do dos e ela elas ele eles em entre era eram essa essas esse esses esta estamos estas estava estavam este esteja estejam estejamos estes esteve estive estivemos estiver estivera estiveram estiverem estivermos estivesse estivessem estivéramos estivéssemos estou está estávamos estão eu foi fomos for fora foram forem formos fosse fossem fui fôramos fôssemos haja hajam hajamos havemos hei houve houvemos houver houvera houveram houverei houverem houveremos houveria houveriam houvermos houverá houverão houveríamos houvesse houvessem houvéramos houvéssemos há hão isso isto já lhe lhes mais mas me mesmo meu meus minha minhas muito na nas nem no nos nossa nossas nosso nossos num numa não nós o os ou para pela pelas pelo pelos por qual quando que quem se seja sejam sejamos sem serei seremos seria seriam será serão seríamos seu seus somos sou sua suas são só também te tem temos tenha tenham tenhamos tenho terei teremos teria teriam terá terão teríamos teu teus teve tinha tinham tive tivemos tiver tivera tiveram tiverem tivermos tivesse tivessem tivéramos tivéssemos tu tua tuas tém tínhamos um uma você vocês vos à às éramos".split(" ")),e.Pipeline.registerFunction(e.pt.stopWordFilter,"stopWordFilter-pt")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.ro.min.js b/assets/javascripts/lunr/min/lunr.ro.min.js new file mode 100644 index 0000000000..7277140181 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.ro.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Romanian` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ro=function(){this.pipeline.reset(),this.pipeline.add(e.ro.trimmer,e.ro.stopWordFilter,e.ro.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ro.stemmer))},e.ro.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.ro.trimmer=e.trimmerSupport.generateTrimmer(e.ro.wordCharacters),e.Pipeline.registerFunction(e.ro.trimmer,"trimmer-ro"),e.ro.stemmer=function(){var i=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,n=new function(){function e(e,i){L.eq_s(1,e)&&(L.ket=L.cursor,L.in_grouping(W,97,259)&&L.slice_from(i))}function n(){for(var i,r;;){if(i=L.cursor,L.in_grouping(W,97,259)&&(r=L.cursor,L.bra=r,e("u","U"),L.cursor=r,e("i","I")),L.cursor=i,L.cursor>=L.limit)break;L.cursor++}}function t(){if(L.out_grouping(W,97,259)){for(;!L.in_grouping(W,97,259);){if(L.cursor>=L.limit)return!0;L.cursor++}return!1}return!0}function a(){if(L.in_grouping(W,97,259))for(;!L.out_grouping(W,97,259);){if(L.cursor>=L.limit)return!0;L.cursor++}return!1}function o(){var e,i,r=L.cursor;if(L.in_grouping(W,97,259)){if(e=L.cursor,!t())return void(h=L.cursor);if(L.cursor=e,!a())return void(h=L.cursor)}L.cursor=r,L.out_grouping(W,97,259)&&(i=L.cursor,t()&&(L.cursor=i,L.in_grouping(W,97,259)&&L.cursor=L.limit)return!1;L.cursor++}for(;!L.out_grouping(W,97,259);){if(L.cursor>=L.limit)return!1;L.cursor++}return!0}function c(){var e=L.cursor;h=L.limit,k=h,g=h,o(),L.cursor=e,u()&&(k=L.cursor,u()&&(g=L.cursor))}function s(){for(var e;;){if(L.bra=L.cursor,e=L.find_among(z,3))switch(L.ket=L.cursor,e){case 1:L.slice_from("i");continue;case 2:L.slice_from("u");continue;case 3:if(L.cursor>=L.limit)break;L.cursor++;continue}break}}function w(){return h<=L.cursor}function m(){return k<=L.cursor}function l(){return g<=L.cursor}function f(){var e,i;if(L.ket=L.cursor,(e=L.find_among_b(C,16))&&(L.bra=L.cursor,m()))switch(e){case 1:L.slice_del();break;case 2:L.slice_from("a");break;case 3:L.slice_from("e");break;case 4:L.slice_from("i");break;case 5:i=L.limit-L.cursor,L.eq_s_b(2,"ab")||(L.cursor=L.limit-i,L.slice_from("i"));break;case 6:L.slice_from("at");break;case 7:L.slice_from("aţi")}}function p(){var e,i=L.limit-L.cursor;if(L.ket=L.cursor,(e=L.find_among_b(P,46))&&(L.bra=L.cursor,m())){switch(e){case 1:L.slice_from("abil");break;case 2:L.slice_from("ibil");break;case 3:L.slice_from("iv");break;case 4:L.slice_from("ic");break;case 5:L.slice_from("at");break;case 6:L.slice_from("it")}return _=!0,L.cursor=L.limit-i,!0}return!1}function d(){var e,i;for(_=!1;;)if(i=L.limit-L.cursor,!p()){L.cursor=L.limit-i;break}if(L.ket=L.cursor,(e=L.find_among_b(F,62))&&(L.bra=L.cursor,l())){switch(e){case 1:L.slice_del();break;case 2:L.eq_s_b(1,"ţ")&&(L.bra=L.cursor,L.slice_from("t"));break;case 3:L.slice_from("ist")}_=!0}}function b(){var e,i,r;if(L.cursor>=h){if(i=L.limit_backward,L.limit_backward=h,L.ket=L.cursor,e=L.find_among_b(q,94))switch(L.bra=L.cursor,e){case 1:if(r=L.limit-L.cursor,!L.out_grouping_b(W,97,259)&&(L.cursor=L.limit-r,!L.eq_s_b(1,"u")))break;case 2:L.slice_del()}L.limit_backward=i}}function v(){var e;L.ket=L.cursor,(e=L.find_among_b(S,5))&&(L.bra=L.cursor,w()&&1==e&&L.slice_del())}var _,g,k,h,z=[new i("",-1,3),new i("I",0,1),new i("U",0,2)],C=[new i("ea",-1,3),new i("aţia",-1,7),new i("aua",-1,2),new i("iua",-1,4),new i("aţie",-1,7),new i("ele",-1,3),new i("ile",-1,5),new i("iile",6,4),new i("iei",-1,4),new i("atei",-1,6),new i("ii",-1,4),new i("ului",-1,1),new i("ul",-1,1),new i("elor",-1,3),new i("ilor",-1,4),new i("iilor",14,4)],P=[new i("icala",-1,4),new i("iciva",-1,4),new i("ativa",-1,5),new i("itiva",-1,6),new i("icale",-1,4),new i("aţiune",-1,5),new i("iţiune",-1,6),new i("atoare",-1,5),new i("itoare",-1,6),new i("ătoare",-1,5),new i("icitate",-1,4),new i("abilitate",-1,1),new i("ibilitate",-1,2),new i("ivitate",-1,3),new i("icive",-1,4),new i("ative",-1,5),new i("itive",-1,6),new i("icali",-1,4),new i("atori",-1,5),new i("icatori",18,4),new i("itori",-1,6),new i("ători",-1,5),new i("icitati",-1,4),new i("abilitati",-1,1),new i("ivitati",-1,3),new i("icivi",-1,4),new i("ativi",-1,5),new i("itivi",-1,6),new i("icităi",-1,4),new i("abilităi",-1,1),new i("ivităi",-1,3),new i("icităţi",-1,4),new i("abilităţi",-1,1),new i("ivităţi",-1,3),new i("ical",-1,4),new i("ator",-1,5),new i("icator",35,4),new i("itor",-1,6),new i("ător",-1,5),new i("iciv",-1,4),new i("ativ",-1,5),new i("itiv",-1,6),new i("icală",-1,4),new i("icivă",-1,4),new i("ativă",-1,5),new i("itivă",-1,6)],F=[new i("ica",-1,1),new i("abila",-1,1),new i("ibila",-1,1),new i("oasa",-1,1),new i("ata",-1,1),new i("ita",-1,1),new i("anta",-1,1),new i("ista",-1,3),new i("uta",-1,1),new i("iva",-1,1),new i("ic",-1,1),new i("ice",-1,1),new i("abile",-1,1),new i("ibile",-1,1),new i("isme",-1,3),new i("iune",-1,2),new i("oase",-1,1),new i("ate",-1,1),new i("itate",17,1),new i("ite",-1,1),new i("ante",-1,1),new i("iste",-1,3),new i("ute",-1,1),new i("ive",-1,1),new i("ici",-1,1),new i("abili",-1,1),new i("ibili",-1,1),new i("iuni",-1,2),new i("atori",-1,1),new i("osi",-1,1),new i("ati",-1,1),new i("itati",30,1),new i("iti",-1,1),new i("anti",-1,1),new i("isti",-1,3),new i("uti",-1,1),new i("işti",-1,3),new i("ivi",-1,1),new i("ităi",-1,1),new i("oşi",-1,1),new i("ităţi",-1,1),new i("abil",-1,1),new i("ibil",-1,1),new i("ism",-1,3),new i("ator",-1,1),new i("os",-1,1),new i("at",-1,1),new i("it",-1,1),new i("ant",-1,1),new i("ist",-1,3),new i("ut",-1,1),new i("iv",-1,1),new i("ică",-1,1),new i("abilă",-1,1),new i("ibilă",-1,1),new i("oasă",-1,1),new i("ată",-1,1),new i("ită",-1,1),new i("antă",-1,1),new i("istă",-1,3),new i("ută",-1,1),new i("ivă",-1,1)],q=[new i("ea",-1,1),new i("ia",-1,1),new i("esc",-1,1),new i("ăsc",-1,1),new i("ind",-1,1),new i("ând",-1,1),new i("are",-1,1),new i("ere",-1,1),new i("ire",-1,1),new i("âre",-1,1),new i("se",-1,2),new i("ase",10,1),new i("sese",10,2),new i("ise",10,1),new i("use",10,1),new i("âse",10,1),new i("eşte",-1,1),new i("ăşte",-1,1),new i("eze",-1,1),new i("ai",-1,1),new i("eai",19,1),new i("iai",19,1),new i("sei",-1,2),new i("eşti",-1,1),new i("ăşti",-1,1),new i("ui",-1,1),new i("ezi",-1,1),new i("âi",-1,1),new i("aşi",-1,1),new i("seşi",-1,2),new i("aseşi",29,1),new i("seseşi",29,2),new i("iseşi",29,1),new i("useşi",29,1),new i("âseşi",29,1),new i("işi",-1,1),new i("uşi",-1,1),new i("âşi",-1,1),new i("aţi",-1,2),new i("eaţi",38,1),new i("iaţi",38,1),new i("eţi",-1,2),new i("iţi",-1,2),new i("âţi",-1,2),new i("arăţi",-1,1),new i("serăţi",-1,2),new i("aserăţi",45,1),new i("seserăţi",45,2),new i("iserăţi",45,1),new i("userăţi",45,1),new i("âserăţi",45,1),new i("irăţi",-1,1),new i("urăţi",-1,1),new i("ârăţi",-1,1),new i("am",-1,1),new i("eam",54,1),new i("iam",54,1),new i("em",-1,2),new i("asem",57,1),new i("sesem",57,2),new i("isem",57,1),new i("usem",57,1),new i("âsem",57,1),new i("im",-1,2),new i("âm",-1,2),new i("ăm",-1,2),new i("arăm",65,1),new i("serăm",65,2),new i("aserăm",67,1),new i("seserăm",67,2),new i("iserăm",67,1),new i("userăm",67,1),new i("âserăm",67,1),new i("irăm",65,1),new i("urăm",65,1),new i("ârăm",65,1),new i("au",-1,1),new i("eau",76,1),new i("iau",76,1),new i("indu",-1,1),new i("ându",-1,1),new i("ez",-1,1),new i("ească",-1,1),new i("ară",-1,1),new i("seră",-1,2),new i("aseră",84,1),new i("seseră",84,2),new i("iseră",84,1),new i("useră",84,1),new i("âseră",84,1),new i("iră",-1,1),new i("ură",-1,1),new i("âră",-1,1),new i("ează",-1,1)],S=[new i("a",-1,1),new i("e",-1,1),new i("ie",1,1),new i("i",-1,1),new i("ă",-1,1)],W=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,2,32,0,0,4],L=new r;this.setCurrent=function(e){L.setCurrent(e)},this.getCurrent=function(){return L.getCurrent()},this.stem=function(){var e=L.cursor;return n(),L.cursor=e,c(),L.limit_backward=e,L.cursor=L.limit,f(),L.cursor=L.limit,d(),L.cursor=L.limit,_||(L.cursor=L.limit,b(),L.cursor=L.limit),v(),L.cursor=L.limit_backward,s(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.ro.stemmer,"stemmer-ro"),e.ro.stopWordFilter=e.generateStopWordFilter("acea aceasta această aceea acei aceia acel acela acele acelea acest acesta aceste acestea aceşti aceştia acolo acord acum ai aia aibă aici al ale alea altceva altcineva am ar are asemenea asta astea astăzi asupra au avea avem aveţi azi aş aşadar aţi bine bucur bună ca care caut ce cel ceva chiar cinci cine cineva contra cu cum cumva curând curînd când cât câte câtva câţi cînd cît cîte cîtva cîţi că căci cărei căror cărui către da dacă dar datorită dată dau de deci deja deoarece departe deşi din dinaintea dintr- dintre doi doilea două drept după dă ea ei el ele eram este eu eşti face fata fi fie fiecare fii fim fiu fiţi frumos fără graţie halbă iar ieri la le li lor lui lângă lîngă mai mea mei mele mereu meu mi mie mine mult multă mulţi mulţumesc mâine mîine mă ne nevoie nici nicăieri nimeni nimeri nimic nişte noastre noastră noi noroc nostru nouă noştri nu opt ori oricare orice oricine oricum oricând oricât oricînd oricît oriunde patra patru patrulea pe pentru peste pic poate pot prea prima primul prin puţin puţina puţină până pînă rog sa sale sau se spate spre sub sunt suntem sunteţi sută sînt sîntem sînteţi să săi său ta tale te timp tine toate toată tot totuşi toţi trei treia treilea tu tăi tău un una unde undeva unei uneia unele uneori unii unor unora unu unui unuia unul vi voastre voastră voi vostru vouă voştri vreme vreo vreun vă zece zero zi zice îi îl îmi împotriva în înainte înaintea încotro încât încît între întrucât întrucît îţi ăla ălea ăsta ăstea ăştia şapte şase şi ştiu ţi ţie".split(" ")),e.Pipeline.registerFunction(e.ro.stopWordFilter,"stopWordFilter-ro")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.ru.min.js b/assets/javascripts/lunr/min/lunr.ru.min.js new file mode 100644 index 0000000000..186cc485c2 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.ru.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Russian` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ru=function(){this.pipeline.reset(),this.pipeline.add(e.ru.trimmer,e.ru.stopWordFilter,e.ru.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ru.stemmer))},e.ru.wordCharacters="Ѐ-҄҇-ԯᴫᵸⷠ-ⷿꙀ-ꚟ︮︯",e.ru.trimmer=e.trimmerSupport.generateTrimmer(e.ru.wordCharacters),e.Pipeline.registerFunction(e.ru.trimmer,"trimmer-ru"),e.ru.stemmer=function(){var n=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,t=new function(){function e(){for(;!W.in_grouping(S,1072,1103);){if(W.cursor>=W.limit)return!1;W.cursor++}return!0}function t(){for(;!W.out_grouping(S,1072,1103);){if(W.cursor>=W.limit)return!1;W.cursor++}return!0}function w(){b=W.limit,_=b,e()&&(b=W.cursor,t()&&e()&&t()&&(_=W.cursor))}function i(){return _<=W.cursor}function u(e,n){var r,t;if(W.ket=W.cursor,r=W.find_among_b(e,n)){switch(W.bra=W.cursor,r){case 1:if(t=W.limit-W.cursor,!W.eq_s_b(1,"а")&&(W.cursor=W.limit-t,!W.eq_s_b(1,"я")))return!1;case 2:W.slice_del()}return!0}return!1}function o(){return u(h,9)}function s(e,n){var r;return W.ket=W.cursor,!!(r=W.find_among_b(e,n))&&(W.bra=W.cursor,1==r&&W.slice_del(),!0)}function c(){return s(g,26)}function m(){return!!c()&&(u(C,8),!0)}function f(){return s(k,2)}function l(){return u(P,46)}function a(){s(v,36)}function p(){var e;W.ket=W.cursor,(e=W.find_among_b(F,2))&&(W.bra=W.cursor,i()&&1==e&&W.slice_del())}function d(){var e;if(W.ket=W.cursor,e=W.find_among_b(q,4))switch(W.bra=W.cursor,e){case 1:if(W.slice_del(),W.ket=W.cursor,!W.eq_s_b(1,"н"))break;W.bra=W.cursor;case 2:if(!W.eq_s_b(1,"н"))break;case 3:W.slice_del()}}var _,b,h=[new n("в",-1,1),new n("ив",0,2),new n("ыв",0,2),new n("вши",-1,1),new n("ивши",3,2),new n("ывши",3,2),new n("вшись",-1,1),new n("ившись",6,2),new n("ывшись",6,2)],g=[new n("ее",-1,1),new n("ие",-1,1),new n("ое",-1,1),new n("ые",-1,1),new n("ими",-1,1),new n("ыми",-1,1),new n("ей",-1,1),new n("ий",-1,1),new n("ой",-1,1),new n("ый",-1,1),new n("ем",-1,1),new n("им",-1,1),new n("ом",-1,1),new n("ым",-1,1),new n("его",-1,1),new n("ого",-1,1),new n("ему",-1,1),new n("ому",-1,1),new n("их",-1,1),new n("ых",-1,1),new n("ею",-1,1),new n("ою",-1,1),new n("ую",-1,1),new n("юю",-1,1),new n("ая",-1,1),new n("яя",-1,1)],C=[new n("ем",-1,1),new n("нн",-1,1),new n("вш",-1,1),new n("ивш",2,2),new n("ывш",2,2),new n("щ",-1,1),new n("ющ",5,1),new n("ующ",6,2)],k=[new n("сь",-1,1),new n("ся",-1,1)],P=[new n("ла",-1,1),new n("ила",0,2),new n("ыла",0,2),new n("на",-1,1),new n("ена",3,2),new n("ете",-1,1),new n("ите",-1,2),new n("йте",-1,1),new n("ейте",7,2),new n("уйте",7,2),new n("ли",-1,1),new n("или",10,2),new n("ыли",10,2),new n("й",-1,1),new n("ей",13,2),new n("уй",13,2),new n("л",-1,1),new n("ил",16,2),new n("ыл",16,2),new n("ем",-1,1),new n("им",-1,2),new n("ым",-1,2),new n("н",-1,1),new n("ен",22,2),new n("ло",-1,1),new n("ило",24,2),new n("ыло",24,2),new n("но",-1,1),new n("ено",27,2),new n("нно",27,1),new n("ет",-1,1),new n("ует",30,2),new n("ит",-1,2),new n("ыт",-1,2),new n("ют",-1,1),new n("уют",34,2),new n("ят",-1,2),new n("ны",-1,1),new n("ены",37,2),new n("ть",-1,1),new n("ить",39,2),new n("ыть",39,2),new n("ешь",-1,1),new n("ишь",-1,2),new n("ю",-1,2),new n("ую",44,2)],v=[new n("а",-1,1),new n("ев",-1,1),new n("ов",-1,1),new n("е",-1,1),new n("ие",3,1),new n("ье",3,1),new n("и",-1,1),new n("еи",6,1),new n("ии",6,1),new n("ами",6,1),new n("ями",6,1),new n("иями",10,1),new n("й",-1,1),new n("ей",12,1),new n("ией",13,1),new n("ий",12,1),new n("ой",12,1),new n("ам",-1,1),new n("ем",-1,1),new n("ием",18,1),new n("ом",-1,1),new n("ям",-1,1),new n("иям",21,1),new n("о",-1,1),new n("у",-1,1),new n("ах",-1,1),new n("ях",-1,1),new n("иях",26,1),new n("ы",-1,1),new n("ь",-1,1),new n("ю",-1,1),new n("ию",30,1),new n("ью",30,1),new n("я",-1,1),new n("ия",33,1),new n("ья",33,1)],F=[new n("ост",-1,1),new n("ость",-1,1)],q=[new n("ейше",-1,1),new n("н",-1,2),new n("ейш",-1,1),new n("ь",-1,3)],S=[33,65,8,232],W=new r;this.setCurrent=function(e){W.setCurrent(e)},this.getCurrent=function(){return W.getCurrent()},this.stem=function(){return w(),W.cursor=W.limit,!(W.cursor=i&&(e-=i,t[e>>3]&1<<(7&e)))return this.cursor++,!0}return!1},in_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e<=s&&e>=i&&(e-=i,t[e>>3]&1<<(7&e)))return this.cursor--,!0}return!1},out_grouping:function(t,i,s){if(this.cursors||e>3]&1<<(7&e)))return this.cursor++,!0}return!1},out_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e>s||e>3]&1<<(7&e)))return this.cursor--,!0}return!1},eq_s:function(t,i){if(this.limit-this.cursor>1),f=0,l=o0||e==s||c)break;c=!0}}for(;;){var _=t[s];if(o>=_.s_size){if(this.cursor=n+_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n+_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},find_among_b:function(t,i){for(var s=0,e=i,n=this.cursor,u=this.limit_backward,o=0,h=0,c=!1;;){for(var a=s+(e-s>>1),f=0,l=o=0;m--){if(n-l==u){f=-1;break}if(f=r.charCodeAt(n-1-l)-_.s[m])break;l++}if(f<0?(e=a,h=l):(s=a,o=l),e-s<=1){if(s>0||e==s||c)break;c=!0}}for(;;){var _=t[s];if(o>=_.s_size){if(this.cursor=n-_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n-_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},replace_s:function(t,i,s){var e=s.length-(i-t),n=r.substring(0,t),u=r.substring(i);return r=n+s+u,this.limit+=e,this.cursor>=i?this.cursor+=e:this.cursor>t&&(this.cursor=t),e},slice_check:function(){if(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>r.length)throw"faulty slice operation"},slice_from:function(r){this.slice_check(),this.replace_s(this.bra,this.ket,r)},slice_del:function(){this.slice_from("")},insert:function(r,t,i){var s=this.replace_s(r,t,i);r<=this.bra&&(this.bra+=s),r<=this.ket&&(this.ket+=s)},slice_to:function(){return this.slice_check(),r.substring(this.bra,this.ket)},eq_v_b:function(r){return this.eq_s_b(r.length,r)}}}},r.trimmerSupport={generateTrimmer:function(r){var t=new RegExp("^[^"+r+"]+"),i=new RegExp("[^"+r+"]+$");return function(r){return"function"==typeof r.update?r.update(function(r){return r.replace(t,"").replace(i,"")}):r.replace(t,"").replace(i,"")}}}}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.sv.min.js b/assets/javascripts/lunr/min/lunr.sv.min.js new file mode 100644 index 0000000000..3e5eb64000 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.sv.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Swedish` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.sv=function(){this.pipeline.reset(),this.pipeline.add(e.sv.trimmer,e.sv.stopWordFilter,e.sv.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.sv.stemmer))},e.sv.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.sv.trimmer=e.trimmerSupport.generateTrimmer(e.sv.wordCharacters),e.Pipeline.registerFunction(e.sv.trimmer,"trimmer-sv"),e.sv.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,t=new function(){function e(){var e,r=w.cursor+3;if(o=w.limit,0<=r||r<=w.limit){for(a=r;;){if(e=w.cursor,w.in_grouping(l,97,246)){w.cursor=e;break}if(w.cursor=e,w.cursor>=w.limit)return;w.cursor++}for(;!w.out_grouping(l,97,246);){if(w.cursor>=w.limit)return;w.cursor++}o=w.cursor,o=o&&(w.limit_backward=o,w.cursor=w.limit,w.ket=w.cursor,e=w.find_among_b(u,37),w.limit_backward=r,e))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:w.in_grouping_b(d,98,121)&&w.slice_del()}}function i(){var e=w.limit_backward;w.cursor>=o&&(w.limit_backward=o,w.cursor=w.limit,w.find_among_b(c,7)&&(w.cursor=w.limit,w.ket=w.cursor,w.cursor>w.limit_backward&&(w.bra=--w.cursor,w.slice_del())),w.limit_backward=e)}function s(){var e,r;if(w.cursor>=o){if(r=w.limit_backward,w.limit_backward=o,w.cursor=w.limit,w.ket=w.cursor,e=w.find_among_b(m,5))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:w.slice_from("lös");break;case 3:w.slice_from("full")}w.limit_backward=r}}var a,o,u=[new r("a",-1,1),new r("arna",0,1),new r("erna",0,1),new r("heterna",2,1),new r("orna",0,1),new r("ad",-1,1),new r("e",-1,1),new r("ade",6,1),new r("ande",6,1),new r("arne",6,1),new r("are",6,1),new r("aste",6,1),new r("en",-1,1),new r("anden",12,1),new r("aren",12,1),new r("heten",12,1),new r("ern",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",18,1),new r("or",-1,1),new r("s",-1,2),new r("as",21,1),new r("arnas",22,1),new r("ernas",22,1),new r("ornas",22,1),new r("es",21,1),new r("ades",26,1),new r("andes",26,1),new r("ens",21,1),new r("arens",29,1),new r("hetens",29,1),new r("erns",21,1),new r("at",-1,1),new r("andet",-1,1),new r("het",-1,1),new r("ast",-1,1)],c=[new r("dd",-1,-1),new r("gd",-1,-1),new r("nn",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1),new r("tt",-1,-1)],m=[new r("ig",-1,1),new r("lig",0,1),new r("els",-1,1),new r("fullt",-1,3),new r("löst",-1,2)],l=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,24,0,32],d=[119,127,149],w=new n;this.setCurrent=function(e){w.setCurrent(e)},this.getCurrent=function(){return w.getCurrent()},this.stem=function(){var r=w.cursor;return e(),w.limit_backward=r,w.cursor=w.limit,t(),w.cursor=w.limit,i(),w.cursor=w.limit,s(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return t.setCurrent(e),t.stem(),t.getCurrent()}):(t.setCurrent(e),t.stem(),t.getCurrent())}}(),e.Pipeline.registerFunction(e.sv.stemmer,"stemmer-sv"),e.sv.stopWordFilter=e.generateStopWordFilter("alla allt att av blev bli blir blivit de dem den denna deras dess dessa det detta dig din dina ditt du där då efter ej eller en er era ert ett från för ha hade han hans har henne hennes hon honom hur här i icke ingen inom inte jag ju kan kunde man med mellan men mig min mina mitt mot mycket ni nu när någon något några och om oss på samma sedan sig sin sina sitta själv skulle som så sådan sådana sådant till under upp ut utan vad var vara varför varit varje vars vart vem vi vid vilka vilkas vilken vilket vår våra vårt än är åt över".split(" ")),e.Pipeline.registerFunction(e.sv.stopWordFilter,"stopWordFilter-sv")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.ta.min.js b/assets/javascripts/lunr/min/lunr.ta.min.js new file mode 100644 index 0000000000..a644bed228 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.ta.min.js @@ -0,0 +1 @@ +!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ta=function(){this.pipeline.reset(),this.pipeline.add(e.ta.trimmer,e.ta.stopWordFilter,e.ta.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ta.stemmer))},e.ta.wordCharacters="஀-உஊ-ஏஐ-ஙச-ட஠-னப-யர-ஹ஺-ிீ-௉ொ-௏ௐ-௙௚-௟௠-௩௪-௯௰-௹௺-௿a-zA-Za-zA-Z0-90-9",e.ta.trimmer=e.trimmerSupport.generateTrimmer(e.ta.wordCharacters),e.Pipeline.registerFunction(e.ta.trimmer,"trimmer-ta"),e.ta.stopWordFilter=e.generateStopWordFilter("அங்கு அங்கே அது அதை அந்த அவர் அவர்கள் அவள் அவன் அவை ஆக ஆகவே ஆகையால் ஆதலால் ஆதலினால் ஆனாலும் ஆனால் இங்கு இங்கே இது இதை இந்த இப்படி இவர் இவர்கள் இவள் இவன் இவை இவ்வளவு உனக்கு உனது உன் உன்னால் எங்கு எங்கே எது எதை எந்த எப்படி எவர் எவர்கள் எவள் எவன் எவை எவ்வளவு எனக்கு எனது எனவே என் என்ன என்னால் ஏது ஏன் தனது தன்னால் தானே தான் நாங்கள் நாம் நான் நீ நீங்கள்".split(" ")),e.ta.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var t=e.wordcut;t.init(),e.ta.tokenizer=function(r){if(!arguments.length||null==r||void 0==r)return[];if(Array.isArray(r))return r.map(function(t){return isLunr2?new e.Token(t.toLowerCase()):t.toLowerCase()});var i=r.toString().toLowerCase().replace(/^\s+/,"");return t.cut(i).split("|")},e.Pipeline.registerFunction(e.ta.stemmer,"stemmer-ta"),e.Pipeline.registerFunction(e.ta.stopWordFilter,"stopWordFilter-ta")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.te.min.js b/assets/javascripts/lunr/min/lunr.te.min.js new file mode 100644 index 0000000000..9fa7a93b99 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.te.min.js @@ -0,0 +1 @@ +!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.te=function(){this.pipeline.reset(),this.pipeline.add(e.te.trimmer,e.te.stopWordFilter,e.te.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.te.stemmer))},e.te.wordCharacters="ఀ-ఄఅ-ఔక-హా-ౌౕ-ౖౘ-ౚౠ-ౡౢ-ౣ౦-౯౸-౿఼ఽ్ౝ౷౤౥",e.te.trimmer=e.trimmerSupport.generateTrimmer(e.te.wordCharacters),e.Pipeline.registerFunction(e.te.trimmer,"trimmer-te"),e.te.stopWordFilter=e.generateStopWordFilter("అందరూ అందుబాటులో అడగండి అడగడం అడ్డంగా అనుగుణంగా అనుమతించు అనుమతిస్తుంది అయితే ఇప్పటికే ఉన్నారు ఎక్కడైనా ఎప్పుడు ఎవరైనా ఎవరో ఏ ఏదైనా ఏమైనప్పటికి ఒక ఒకరు కనిపిస్తాయి కాదు కూడా గా గురించి చుట్టూ చేయగలిగింది తగిన తర్వాత దాదాపు దూరంగా నిజంగా పై ప్రకారం ప్రక్కన మధ్య మరియు మరొక మళ్ళీ మాత్రమే మెచ్చుకో వద్ద వెంట వేరుగా వ్యతిరేకంగా సంబంధం".split(" ")),e.te.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var t=e.wordcut;t.init(),e.te.tokenizer=function(r){if(!arguments.length||null==r||void 0==r)return[];if(Array.isArray(r))return r.map(function(t){return isLunr2?new e.Token(t.toLowerCase()):t.toLowerCase()});var i=r.toString().toLowerCase().replace(/^\s+/,"");return t.cut(i).split("|")},e.Pipeline.registerFunction(e.te.stemmer,"stemmer-te"),e.Pipeline.registerFunction(e.te.stopWordFilter,"stopWordFilter-te")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.th.min.js b/assets/javascripts/lunr/min/lunr.th.min.js new file mode 100644 index 0000000000..dee3aac6e5 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.th.min.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r="2"==e.version[0];e.th=function(){this.pipeline.reset(),this.pipeline.add(e.th.trimmer),r?this.tokenizer=e.th.tokenizer:(e.tokenizer&&(e.tokenizer=e.th.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.th.tokenizer))},e.th.wordCharacters="[฀-๿]",e.th.trimmer=e.trimmerSupport.generateTrimmer(e.th.wordCharacters),e.Pipeline.registerFunction(e.th.trimmer,"trimmer-th");var t=e.wordcut;t.init(),e.th.tokenizer=function(i){if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(t){return r?new e.Token(t):t});var n=i.toString().replace(/^\s+/,"");return t.cut(n).split("|")}}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.tr.min.js b/assets/javascripts/lunr/min/lunr.tr.min.js new file mode 100644 index 0000000000..563f6ec1f5 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.tr.min.js @@ -0,0 +1,18 @@ +/*! + * Lunr languages, `Turkish` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +!function(r,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(r.lunr)}(this,function(){return function(r){if(void 0===r)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===r.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");r.tr=function(){this.pipeline.reset(),this.pipeline.add(r.tr.trimmer,r.tr.stopWordFilter,r.tr.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(r.tr.stemmer))},r.tr.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",r.tr.trimmer=r.trimmerSupport.generateTrimmer(r.tr.wordCharacters),r.Pipeline.registerFunction(r.tr.trimmer,"trimmer-tr"),r.tr.stemmer=function(){var i=r.stemmerSupport.Among,e=r.stemmerSupport.SnowballProgram,n=new function(){function r(r,i,e){for(;;){var n=Dr.limit-Dr.cursor;if(Dr.in_grouping_b(r,i,e)){Dr.cursor=Dr.limit-n;break}if(Dr.cursor=Dr.limit-n,Dr.cursor<=Dr.limit_backward)return!1;Dr.cursor--}return!0}function n(){var i,e;i=Dr.limit-Dr.cursor,r(Wr,97,305);for(var n=0;nDr.limit_backward&&(Dr.cursor--,e=Dr.limit-Dr.cursor,i()))?(Dr.cursor=Dr.limit-e,!0):(Dr.cursor=Dr.limit-n,r()?(Dr.cursor=Dr.limit-n,!1):(Dr.cursor=Dr.limit-n,!(Dr.cursor<=Dr.limit_backward)&&(Dr.cursor--,!!i()&&(Dr.cursor=Dr.limit-n,!0))))}function u(r){return t(r,function(){return Dr.in_grouping_b(Wr,97,305)})}function o(){return u(function(){return Dr.eq_s_b(1,"n")})}function s(){return u(function(){return Dr.eq_s_b(1,"s")})}function c(){return u(function(){return Dr.eq_s_b(1,"y")})}function l(){return t(function(){return Dr.in_grouping_b(Lr,105,305)},function(){return Dr.out_grouping_b(Wr,97,305)})}function a(){return Dr.find_among_b(ur,10)&&l()}function m(){return n()&&Dr.in_grouping_b(Lr,105,305)&&s()}function d(){return Dr.find_among_b(or,2)}function f(){return n()&&Dr.in_grouping_b(Lr,105,305)&&c()}function b(){return n()&&Dr.find_among_b(sr,4)}function w(){return n()&&Dr.find_among_b(cr,4)&&o()}function _(){return n()&&Dr.find_among_b(lr,2)&&c()}function k(){return n()&&Dr.find_among_b(ar,2)}function p(){return n()&&Dr.find_among_b(mr,4)}function g(){return n()&&Dr.find_among_b(dr,2)}function y(){return n()&&Dr.find_among_b(fr,4)}function z(){return n()&&Dr.find_among_b(br,2)}function v(){return n()&&Dr.find_among_b(wr,2)&&c()}function h(){return Dr.eq_s_b(2,"ki")}function q(){return n()&&Dr.find_among_b(_r,2)&&o()}function C(){return n()&&Dr.find_among_b(kr,4)&&c()}function P(){return n()&&Dr.find_among_b(pr,4)}function F(){return n()&&Dr.find_among_b(gr,4)&&c()}function S(){return Dr.find_among_b(yr,4)}function W(){return n()&&Dr.find_among_b(zr,2)}function L(){return n()&&Dr.find_among_b(vr,4)}function x(){return n()&&Dr.find_among_b(hr,8)}function A(){return Dr.find_among_b(qr,2)}function E(){return n()&&Dr.find_among_b(Cr,32)&&c()}function j(){return Dr.find_among_b(Pr,8)&&c()}function T(){return n()&&Dr.find_among_b(Fr,4)&&c()}function Z(){return Dr.eq_s_b(3,"ken")&&c()}function B(){var r=Dr.limit-Dr.cursor;return!(T()||(Dr.cursor=Dr.limit-r,E()||(Dr.cursor=Dr.limit-r,j()||(Dr.cursor=Dr.limit-r,Z()))))}function D(){if(A()){var r=Dr.limit-Dr.cursor;if(S()||(Dr.cursor=Dr.limit-r,W()||(Dr.cursor=Dr.limit-r,C()||(Dr.cursor=Dr.limit-r,P()||(Dr.cursor=Dr.limit-r,F()||(Dr.cursor=Dr.limit-r))))),T())return!1}return!0}function G(){if(W()){Dr.bra=Dr.cursor,Dr.slice_del();var r=Dr.limit-Dr.cursor;return Dr.ket=Dr.cursor,x()||(Dr.cursor=Dr.limit-r,E()||(Dr.cursor=Dr.limit-r,j()||(Dr.cursor=Dr.limit-r,T()||(Dr.cursor=Dr.limit-r)))),nr=!1,!1}return!0}function H(){if(!L())return!0;var r=Dr.limit-Dr.cursor;return!E()&&(Dr.cursor=Dr.limit-r,!j())}function I(){var r,i=Dr.limit-Dr.cursor;return!(S()||(Dr.cursor=Dr.limit-i,F()||(Dr.cursor=Dr.limit-i,P()||(Dr.cursor=Dr.limit-i,C()))))||(Dr.bra=Dr.cursor,Dr.slice_del(),r=Dr.limit-Dr.cursor,Dr.ket=Dr.cursor,T()||(Dr.cursor=Dr.limit-r),!1)}function J(){var r,i=Dr.limit-Dr.cursor;if(Dr.ket=Dr.cursor,nr=!0,B()&&(Dr.cursor=Dr.limit-i,D()&&(Dr.cursor=Dr.limit-i,G()&&(Dr.cursor=Dr.limit-i,H()&&(Dr.cursor=Dr.limit-i,I()))))){if(Dr.cursor=Dr.limit-i,!x())return;Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,r=Dr.limit-Dr.cursor,S()||(Dr.cursor=Dr.limit-r,W()||(Dr.cursor=Dr.limit-r,C()||(Dr.cursor=Dr.limit-r,P()||(Dr.cursor=Dr.limit-r,F()||(Dr.cursor=Dr.limit-r))))),T()||(Dr.cursor=Dr.limit-r)}Dr.bra=Dr.cursor,Dr.slice_del()}function K(){var r,i,e,n;if(Dr.ket=Dr.cursor,h()){if(r=Dr.limit-Dr.cursor,p())return Dr.bra=Dr.cursor,Dr.slice_del(),i=Dr.limit-Dr.cursor,Dr.ket=Dr.cursor,W()?(Dr.bra=Dr.cursor,Dr.slice_del(),K()):(Dr.cursor=Dr.limit-i,a()&&(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K()))),!0;if(Dr.cursor=Dr.limit-r,w()){if(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,e=Dr.limit-Dr.cursor,d())Dr.bra=Dr.cursor,Dr.slice_del();else{if(Dr.cursor=Dr.limit-e,Dr.ket=Dr.cursor,!a()&&(Dr.cursor=Dr.limit-e,!m()&&(Dr.cursor=Dr.limit-e,!K())))return!0;Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K())}return!0}if(Dr.cursor=Dr.limit-r,g()){if(n=Dr.limit-Dr.cursor,d())Dr.bra=Dr.cursor,Dr.slice_del();else if(Dr.cursor=Dr.limit-n,m())Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K());else if(Dr.cursor=Dr.limit-n,!K())return!1;return!0}}return!1}function M(r){if(Dr.ket=Dr.cursor,!g()&&(Dr.cursor=Dr.limit-r,!k()))return!1;var i=Dr.limit-Dr.cursor;if(d())Dr.bra=Dr.cursor,Dr.slice_del();else if(Dr.cursor=Dr.limit-i,m())Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K());else if(Dr.cursor=Dr.limit-i,!K())return!1;return!0}function N(r){if(Dr.ket=Dr.cursor,!z()&&(Dr.cursor=Dr.limit-r,!b()))return!1;var i=Dr.limit-Dr.cursor;return!(!m()&&(Dr.cursor=Dr.limit-i,!d()))&&(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K()),!0)}function O(){var r,i=Dr.limit-Dr.cursor;return Dr.ket=Dr.cursor,!(!w()&&(Dr.cursor=Dr.limit-i,!v()))&&(Dr.bra=Dr.cursor,Dr.slice_del(),r=Dr.limit-Dr.cursor,Dr.ket=Dr.cursor,!(!W()||(Dr.bra=Dr.cursor,Dr.slice_del(),!K()))||(Dr.cursor=Dr.limit-r,Dr.ket=Dr.cursor,!(a()||(Dr.cursor=Dr.limit-r,m()||(Dr.cursor=Dr.limit-r,K())))||(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K()),!0)))}function Q(){var r,i,e=Dr.limit-Dr.cursor;if(Dr.ket=Dr.cursor,!p()&&(Dr.cursor=Dr.limit-e,!f()&&(Dr.cursor=Dr.limit-e,!_())))return!1;if(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,r=Dr.limit-Dr.cursor,a())Dr.bra=Dr.cursor,Dr.slice_del(),i=Dr.limit-Dr.cursor,Dr.ket=Dr.cursor,W()||(Dr.cursor=Dr.limit-i);else if(Dr.cursor=Dr.limit-r,!W())return!0;return Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,K(),!0}function R(){var r,i,e=Dr.limit-Dr.cursor;if(Dr.ket=Dr.cursor,W())return Dr.bra=Dr.cursor,Dr.slice_del(),void K();if(Dr.cursor=Dr.limit-e,Dr.ket=Dr.cursor,q())if(Dr.bra=Dr.cursor,Dr.slice_del(),r=Dr.limit-Dr.cursor,Dr.ket=Dr.cursor,d())Dr.bra=Dr.cursor,Dr.slice_del();else{if(Dr.cursor=Dr.limit-r,Dr.ket=Dr.cursor,!a()&&(Dr.cursor=Dr.limit-r,!m())){if(Dr.cursor=Dr.limit-r,Dr.ket=Dr.cursor,!W())return;if(Dr.bra=Dr.cursor,Dr.slice_del(),!K())return}Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K())}else if(Dr.cursor=Dr.limit-e,!M(e)&&(Dr.cursor=Dr.limit-e,!N(e))){if(Dr.cursor=Dr.limit-e,Dr.ket=Dr.cursor,y())return Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,i=Dr.limit-Dr.cursor,void(a()?(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K())):(Dr.cursor=Dr.limit-i,W()?(Dr.bra=Dr.cursor,Dr.slice_del(),K()):(Dr.cursor=Dr.limit-i,K())));if(Dr.cursor=Dr.limit-e,!O()){if(Dr.cursor=Dr.limit-e,d())return Dr.bra=Dr.cursor,void Dr.slice_del();Dr.cursor=Dr.limit-e,K()||(Dr.cursor=Dr.limit-e,Q()||(Dr.cursor=Dr.limit-e,Dr.ket=Dr.cursor,(a()||(Dr.cursor=Dr.limit-e,m()))&&(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K()))))}}}function U(){var r;if(Dr.ket=Dr.cursor,r=Dr.find_among_b(Sr,4))switch(Dr.bra=Dr.cursor,r){case 1:Dr.slice_from("p");break;case 2:Dr.slice_from("ç");break;case 3:Dr.slice_from("t");break;case 4:Dr.slice_from("k")}}function V(){for(;;){var r=Dr.limit-Dr.cursor;if(Dr.in_grouping_b(Wr,97,305)){Dr.cursor=Dr.limit-r;break}if(Dr.cursor=Dr.limit-r,Dr.cursor<=Dr.limit_backward)return!1;Dr.cursor--}return!0}function X(r,i,e){if(Dr.cursor=Dr.limit-r,V()){var n=Dr.limit-Dr.cursor;if(!Dr.eq_s_b(1,i)&&(Dr.cursor=Dr.limit-n,!Dr.eq_s_b(1,e)))return!0;Dr.cursor=Dr.limit-r;var t=Dr.cursor;return Dr.insert(Dr.cursor,Dr.cursor,e),Dr.cursor=t,!1}return!0}function Y(){var r=Dr.limit-Dr.cursor;(Dr.eq_s_b(1,"d")||(Dr.cursor=Dr.limit-r,Dr.eq_s_b(1,"g")))&&X(r,"a","ı")&&X(r,"e","i")&&X(r,"o","u")&&X(r,"ö","ü")}function $(){for(var r,i=Dr.cursor,e=2;;){for(r=Dr.cursor;!Dr.in_grouping(Wr,97,305);){if(Dr.cursor>=Dr.limit)return Dr.cursor=r,!(e>0)&&(Dr.cursor=i,!0);Dr.cursor++}e--}}function rr(r,i,e){for(;!Dr.eq_s(i,e);){if(Dr.cursor>=Dr.limit)return!0;Dr.cursor++}return(tr=i)!=Dr.limit||(Dr.cursor=r,!1)}function ir(){var r=Dr.cursor;return!rr(r,2,"ad")||(Dr.cursor=r,!rr(r,5,"soyad"))}function er(){var r=Dr.cursor;return!ir()&&(Dr.limit_backward=r,Dr.cursor=Dr.limit,Y(),Dr.cursor=Dr.limit,U(),!0)}var nr,tr,ur=[new i("m",-1,-1),new i("n",-1,-1),new i("miz",-1,-1),new i("niz",-1,-1),new i("muz",-1,-1),new i("nuz",-1,-1),new i("müz",-1,-1),new i("nüz",-1,-1),new i("mız",-1,-1),new i("nız",-1,-1)],or=[new i("leri",-1,-1),new i("ları",-1,-1)],sr=[new i("ni",-1,-1),new i("nu",-1,-1),new i("nü",-1,-1),new i("nı",-1,-1)],cr=[new i("in",-1,-1),new i("un",-1,-1),new i("ün",-1,-1),new i("ın",-1,-1)],lr=[new i("a",-1,-1),new i("e",-1,-1)],ar=[new i("na",-1,-1),new i("ne",-1,-1)],mr=[new i("da",-1,-1),new i("ta",-1,-1),new i("de",-1,-1),new i("te",-1,-1)],dr=[new i("nda",-1,-1),new i("nde",-1,-1)],fr=[new i("dan",-1,-1),new i("tan",-1,-1),new i("den",-1,-1),new i("ten",-1,-1)],br=[new i("ndan",-1,-1),new i("nden",-1,-1)],wr=[new i("la",-1,-1),new i("le",-1,-1)],_r=[new i("ca",-1,-1),new i("ce",-1,-1)],kr=[new i("im",-1,-1),new i("um",-1,-1),new i("üm",-1,-1),new i("ım",-1,-1)],pr=[new i("sin",-1,-1),new i("sun",-1,-1),new i("sün",-1,-1),new i("sın",-1,-1)],gr=[new i("iz",-1,-1),new i("uz",-1,-1),new i("üz",-1,-1),new i("ız",-1,-1)],yr=[new i("siniz",-1,-1),new i("sunuz",-1,-1),new i("sünüz",-1,-1),new i("sınız",-1,-1)],zr=[new i("lar",-1,-1),new i("ler",-1,-1)],vr=[new i("niz",-1,-1),new i("nuz",-1,-1),new i("nüz",-1,-1),new i("nız",-1,-1)],hr=[new i("dir",-1,-1),new i("tir",-1,-1),new i("dur",-1,-1),new i("tur",-1,-1),new i("dür",-1,-1),new i("tür",-1,-1),new i("dır",-1,-1),new i("tır",-1,-1)],qr=[new i("casına",-1,-1),new i("cesine",-1,-1)],Cr=[new i("di",-1,-1),new i("ti",-1,-1),new i("dik",-1,-1),new i("tik",-1,-1),new i("duk",-1,-1),new i("tuk",-1,-1),new i("dük",-1,-1),new i("tük",-1,-1),new i("dık",-1,-1),new i("tık",-1,-1),new i("dim",-1,-1),new i("tim",-1,-1),new i("dum",-1,-1),new i("tum",-1,-1),new i("düm",-1,-1),new i("tüm",-1,-1),new i("dım",-1,-1),new i("tım",-1,-1),new i("din",-1,-1),new i("tin",-1,-1),new i("dun",-1,-1),new i("tun",-1,-1),new i("dün",-1,-1),new i("tün",-1,-1),new i("dın",-1,-1),new i("tın",-1,-1),new i("du",-1,-1),new i("tu",-1,-1),new i("dü",-1,-1),new i("tü",-1,-1),new i("dı",-1,-1),new i("tı",-1,-1)],Pr=[new i("sa",-1,-1),new i("se",-1,-1),new i("sak",-1,-1),new i("sek",-1,-1),new i("sam",-1,-1),new i("sem",-1,-1),new i("san",-1,-1),new i("sen",-1,-1)],Fr=[new i("miş",-1,-1),new i("muş",-1,-1),new i("müş",-1,-1),new i("mış",-1,-1)],Sr=[new i("b",-1,1),new i("c",-1,2),new i("d",-1,3),new i("ğ",-1,4)],Wr=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,8,0,0,0,0,0,0,1],Lr=[1,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,1],xr=[1,64,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],Ar=[17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130],Er=[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],jr=[17],Tr=[65],Zr=[65],Br=[["a",xr,97,305],["e",Ar,101,252],["ı",Er,97,305],["i",jr,101,105],["o",Tr,111,117],["ö",Zr,246,252],["u",Tr,111,117]],Dr=new e;this.setCurrent=function(r){Dr.setCurrent(r)},this.getCurrent=function(){return Dr.getCurrent()},this.stem=function(){return!!($()&&(Dr.limit_backward=Dr.cursor,Dr.cursor=Dr.limit,J(),Dr.cursor=Dr.limit,nr&&(R(),Dr.cursor=Dr.limit_backward,er())))}};return function(r){return"function"==typeof r.update?r.update(function(r){return n.setCurrent(r),n.stem(),n.getCurrent()}):(n.setCurrent(r),n.stem(),n.getCurrent())}}(),r.Pipeline.registerFunction(r.tr.stemmer,"stemmer-tr"),r.tr.stopWordFilter=r.generateStopWordFilter("acaba altmış altı ama ancak arada aslında ayrıca bana bazı belki ben benden beni benim beri beş bile bin bir biri birkaç birkez birçok birşey birşeyi biz bizden bize bizi bizim bu buna bunda bundan bunlar bunları bunların bunu bunun burada böyle böylece da daha dahi de defa değil diye diğer doksan dokuz dolayı dolayısıyla dört edecek eden ederek edilecek ediliyor edilmesi ediyor elli en etmesi etti ettiği ettiğini eğer gibi göre halen hangi hatta hem henüz hep hepsi her herhangi herkesin hiç hiçbir iki ile ilgili ise itibaren itibariyle için işte kadar karşın katrilyon kendi kendilerine kendini kendisi kendisine kendisini kez ki kim kimden kime kimi kimse kırk milyar milyon mu mü mı nasıl ne neden nedenle nerde nerede nereye niye niçin o olan olarak oldu olduklarını olduğu olduğunu olmadı olmadığı olmak olması olmayan olmaz olsa olsun olup olur olursa oluyor on ona ondan onlar onlardan onları onların onu onun otuz oysa pek rağmen sadece sanki sekiz seksen sen senden seni senin siz sizden sizi sizin tarafından trilyon tüm var vardı ve veya ya yani yapacak yapmak yaptı yaptıkları yaptığı yaptığını yapılan yapılması yapıyor yedi yerine yetmiş yine yirmi yoksa yüz zaten çok çünkü öyle üzere üç şey şeyden şeyi şeyler şu şuna şunda şundan şunları şunu şöyle".split(" ")),r.Pipeline.registerFunction(r.tr.stopWordFilter,"stopWordFilter-tr")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.vi.min.js b/assets/javascripts/lunr/min/lunr.vi.min.js new file mode 100644 index 0000000000..22aed28c49 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.vi.min.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.vi=function(){this.pipeline.reset(),this.pipeline.add(e.vi.stopWordFilter,e.vi.trimmer)},e.vi.wordCharacters="[A-Za-ẓ̀͐́͑̉̃̓ÂâÊêÔôĂ-ăĐ-đƠ-ơƯ-ư]",e.vi.trimmer=e.trimmerSupport.generateTrimmer(e.vi.wordCharacters),e.Pipeline.registerFunction(e.vi.trimmer,"trimmer-vi"),e.vi.stopWordFilter=e.generateStopWordFilter("là cái nhưng mà".split(" "))}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/min/lunr.zh.min.js b/assets/javascripts/lunr/min/lunr.zh.min.js new file mode 100644 index 0000000000..fda66e9c57 --- /dev/null +++ b/assets/javascripts/lunr/min/lunr.zh.min.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r(require("@node-rs/jieba")):r()(e.lunr)}(this,function(e){return function(r,t){if(void 0===r)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===r.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var i="2"==r.version[0];r.zh=function(){this.pipeline.reset(),this.pipeline.add(r.zh.trimmer,r.zh.stopWordFilter,r.zh.stemmer),i?this.tokenizer=r.zh.tokenizer:(r.tokenizer&&(r.tokenizer=r.zh.tokenizer),this.tokenizerFn&&(this.tokenizerFn=r.zh.tokenizer))},r.zh.tokenizer=function(n){if(!arguments.length||null==n||void 0==n)return[];if(Array.isArray(n))return n.map(function(e){return i?new r.Token(e.toLowerCase()):e.toLowerCase()});t&&e.load(t);var o=n.toString().trim().toLowerCase(),s=[];e.cut(o,!0).forEach(function(e){s=s.concat(e.split(" "))}),s=s.filter(function(e){return!!e});var u=0;return s.map(function(e,t){if(i){var n=o.indexOf(e,u),s={};return s.position=[n,e.length],s.index=t,u=n,new r.Token(e,s)}return e})},r.zh.wordCharacters="\\w一-龥",r.zh.trimmer=r.trimmerSupport.generateTrimmer(r.zh.wordCharacters),r.Pipeline.registerFunction(r.zh.trimmer,"trimmer-zh"),r.zh.stemmer=function(){return function(e){return e}}(),r.Pipeline.registerFunction(r.zh.stemmer,"stemmer-zh"),r.zh.stopWordFilter=r.generateStopWordFilter("的 一 不 在 人 有 是 为 為 以 于 於 上 他 而 后 後 之 来 來 及 了 因 下 可 到 由 这 這 与 與 也 此 但 并 並 个 個 其 已 无 無 小 我 们 們 起 最 再 今 去 好 只 又 或 很 亦 某 把 那 你 乃 它 吧 被 比 别 趁 当 當 从 從 得 打 凡 儿 兒 尔 爾 该 該 各 给 給 跟 和 何 还 還 即 几 幾 既 看 据 據 距 靠 啦 另 么 麽 每 嘛 拿 哪 您 凭 憑 且 却 卻 让 讓 仍 啥 如 若 使 谁 誰 虽 雖 随 隨 同 所 她 哇 嗡 往 些 向 沿 哟 喲 用 咱 则 則 怎 曾 至 致 着 著 诸 諸 自".split(" ")),r.Pipeline.registerFunction(r.zh.stopWordFilter,"stopWordFilter-zh")}}); \ No newline at end of file diff --git a/assets/javascripts/lunr/tinyseg.js b/assets/javascripts/lunr/tinyseg.js new file mode 100644 index 0000000000..167fa6dd69 --- /dev/null +++ b/assets/javascripts/lunr/tinyseg.js @@ -0,0 +1,206 @@ +/** + * export the module via AMD, CommonJS or as a browser global + * Export code from https://github.com/umdjs/umd/blob/master/returnExports.js + */ +;(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(factory) + } else if (typeof exports === 'object') { + /** + * Node. Does not work with strict CommonJS, but + * only CommonJS-like environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(this, function () { + /** + * Just return a value to define the module export. + * This example returns an object, but the module + * can return a function as the exported value. + */ + + return function(lunr) { + // TinySegmenter 0.1 -- Super compact Japanese tokenizer in Javascript + // (c) 2008 Taku Kudo + // TinySegmenter is freely distributable under the terms of a new BSD licence. + // For details, see http://chasen.org/~taku/software/TinySegmenter/LICENCE.txt + + function TinySegmenter() { + var patterns = { + "[一二三四五六七八九十百千万億兆]":"M", + "[一-龠々〆ヵヶ]":"H", + "[ぁ-ん]":"I", + "[ァ-ヴーア-ン゙ー]":"K", + "[a-zA-Za-zA-Z]":"A", + "[0-90-9]":"N" + } + this.chartype_ = []; + for (var i in patterns) { + var regexp = new RegExp(i); + this.chartype_.push([regexp, patterns[i]]); + } + + this.BIAS__ = -332 + this.BC1__ = {"HH":6,"II":2461,"KH":406,"OH":-1378}; + this.BC2__ = {"AA":-3267,"AI":2744,"AN":-878,"HH":-4070,"HM":-1711,"HN":4012,"HO":3761,"IA":1327,"IH":-1184,"II":-1332,"IK":1721,"IO":5492,"KI":3831,"KK":-8741,"MH":-3132,"MK":3334,"OO":-2920}; + this.BC3__ = {"HH":996,"HI":626,"HK":-721,"HN":-1307,"HO":-836,"IH":-301,"KK":2762,"MK":1079,"MM":4034,"OA":-1652,"OH":266}; + this.BP1__ = {"BB":295,"OB":304,"OO":-125,"UB":352}; + this.BP2__ = {"BO":60,"OO":-1762}; + this.BQ1__ = {"BHH":1150,"BHM":1521,"BII":-1158,"BIM":886,"BMH":1208,"BNH":449,"BOH":-91,"BOO":-2597,"OHI":451,"OIH":-296,"OKA":1851,"OKH":-1020,"OKK":904,"OOO":2965}; + this.BQ2__ = {"BHH":118,"BHI":-1159,"BHM":466,"BIH":-919,"BKK":-1720,"BKO":864,"OHH":-1139,"OHM":-181,"OIH":153,"UHI":-1146}; + this.BQ3__ = {"BHH":-792,"BHI":2664,"BII":-299,"BKI":419,"BMH":937,"BMM":8335,"BNN":998,"BOH":775,"OHH":2174,"OHM":439,"OII":280,"OKH":1798,"OKI":-793,"OKO":-2242,"OMH":-2402,"OOO":11699}; + this.BQ4__ = {"BHH":-3895,"BIH":3761,"BII":-4654,"BIK":1348,"BKK":-1806,"BMI":-3385,"BOO":-12396,"OAH":926,"OHH":266,"OHK":-2036,"ONN":-973}; + this.BW1__ = {",と":660,",同":727,"B1あ":1404,"B1同":542,"、と":660,"、同":727,"」と":1682,"あっ":1505,"いう":1743,"いっ":-2055,"いる":672,"うし":-4817,"うん":665,"から":3472,"がら":600,"こう":-790,"こと":2083,"こん":-1262,"さら":-4143,"さん":4573,"した":2641,"して":1104,"すで":-3399,"そこ":1977,"それ":-871,"たち":1122,"ため":601,"った":3463,"つい":-802,"てい":805,"てき":1249,"でき":1127,"です":3445,"では":844,"とい":-4915,"とみ":1922,"どこ":3887,"ない":5713,"なっ":3015,"など":7379,"なん":-1113,"にし":2468,"には":1498,"にも":1671,"に対":-912,"の一":-501,"の中":741,"ませ":2448,"まで":1711,"まま":2600,"まる":-2155,"やむ":-1947,"よっ":-2565,"れた":2369,"れで":-913,"をし":1860,"を見":731,"亡く":-1886,"京都":2558,"取り":-2784,"大き":-2604,"大阪":1497,"平方":-2314,"引き":-1336,"日本":-195,"本当":-2423,"毎日":-2113,"目指":-724,"B1あ":1404,"B1同":542,"」と":1682}; + this.BW2__ = {"..":-11822,"11":-669,"――":-5730,"−−":-13175,"いう":-1609,"うか":2490,"かし":-1350,"かも":-602,"から":-7194,"かれ":4612,"がい":853,"がら":-3198,"きた":1941,"くな":-1597,"こと":-8392,"この":-4193,"させ":4533,"され":13168,"さん":-3977,"しい":-1819,"しか":-545,"した":5078,"して":972,"しな":939,"その":-3744,"たい":-1253,"たた":-662,"ただ":-3857,"たち":-786,"たと":1224,"たは":-939,"った":4589,"って":1647,"っと":-2094,"てい":6144,"てき":3640,"てく":2551,"ては":-3110,"ても":-3065,"でい":2666,"でき":-1528,"でし":-3828,"です":-4761,"でも":-4203,"とい":1890,"とこ":-1746,"とと":-2279,"との":720,"とみ":5168,"とも":-3941,"ない":-2488,"なが":-1313,"など":-6509,"なの":2614,"なん":3099,"にお":-1615,"にし":2748,"にな":2454,"によ":-7236,"に対":-14943,"に従":-4688,"に関":-11388,"のか":2093,"ので":-7059,"のに":-6041,"のの":-6125,"はい":1073,"はが":-1033,"はず":-2532,"ばれ":1813,"まし":-1316,"まで":-6621,"まれ":5409,"めて":-3153,"もい":2230,"もの":-10713,"らか":-944,"らし":-1611,"らに":-1897,"りし":651,"りま":1620,"れた":4270,"れて":849,"れば":4114,"ろう":6067,"われ":7901,"を通":-11877,"んだ":728,"んな":-4115,"一人":602,"一方":-1375,"一日":970,"一部":-1051,"上が":-4479,"会社":-1116,"出て":2163,"分の":-7758,"同党":970,"同日":-913,"大阪":-2471,"委員":-1250,"少な":-1050,"年度":-8669,"年間":-1626,"府県":-2363,"手権":-1982,"新聞":-4066,"日新":-722,"日本":-7068,"日米":3372,"曜日":-601,"朝鮮":-2355,"本人":-2697,"東京":-1543,"然と":-1384,"社会":-1276,"立て":-990,"第に":-1612,"米国":-4268,"11":-669}; + this.BW3__ = {"あた":-2194,"あり":719,"ある":3846,"い.":-1185,"い。":-1185,"いい":5308,"いえ":2079,"いく":3029,"いた":2056,"いっ":1883,"いる":5600,"いわ":1527,"うち":1117,"うと":4798,"えと":1454,"か.":2857,"か。":2857,"かけ":-743,"かっ":-4098,"かに":-669,"から":6520,"かり":-2670,"が,":1816,"が、":1816,"がき":-4855,"がけ":-1127,"がっ":-913,"がら":-4977,"がり":-2064,"きた":1645,"けど":1374,"こと":7397,"この":1542,"ころ":-2757,"さい":-714,"さを":976,"し,":1557,"し、":1557,"しい":-3714,"した":3562,"して":1449,"しな":2608,"しま":1200,"す.":-1310,"す。":-1310,"する":6521,"ず,":3426,"ず、":3426,"ずに":841,"そう":428,"た.":8875,"た。":8875,"たい":-594,"たの":812,"たり":-1183,"たる":-853,"だ.":4098,"だ。":4098,"だっ":1004,"った":-4748,"って":300,"てい":6240,"てお":855,"ても":302,"です":1437,"でに":-1482,"では":2295,"とう":-1387,"とし":2266,"との":541,"とも":-3543,"どう":4664,"ない":1796,"なく":-903,"など":2135,"に,":-1021,"に、":-1021,"にし":1771,"にな":1906,"には":2644,"の,":-724,"の、":-724,"の子":-1000,"は,":1337,"は、":1337,"べき":2181,"まし":1113,"ます":6943,"まっ":-1549,"まで":6154,"まれ":-793,"らし":1479,"られ":6820,"るる":3818,"れ,":854,"れ、":854,"れた":1850,"れて":1375,"れば":-3246,"れる":1091,"われ":-605,"んだ":606,"んで":798,"カ月":990,"会議":860,"入り":1232,"大会":2217,"始め":1681,"市":965,"新聞":-5055,"日,":974,"日、":974,"社会":2024,"カ月":990}; + this.TC1__ = {"AAA":1093,"HHH":1029,"HHM":580,"HII":998,"HOH":-390,"HOM":-331,"IHI":1169,"IOH":-142,"IOI":-1015,"IOM":467,"MMH":187,"OOI":-1832}; + this.TC2__ = {"HHO":2088,"HII":-1023,"HMM":-1154,"IHI":-1965,"KKH":703,"OII":-2649}; + this.TC3__ = {"AAA":-294,"HHH":346,"HHI":-341,"HII":-1088,"HIK":731,"HOH":-1486,"IHH":128,"IHI":-3041,"IHO":-1935,"IIH":-825,"IIM":-1035,"IOI":-542,"KHH":-1216,"KKA":491,"KKH":-1217,"KOK":-1009,"MHH":-2694,"MHM":-457,"MHO":123,"MMH":-471,"NNH":-1689,"NNO":662,"OHO":-3393}; + this.TC4__ = {"HHH":-203,"HHI":1344,"HHK":365,"HHM":-122,"HHN":182,"HHO":669,"HIH":804,"HII":679,"HOH":446,"IHH":695,"IHO":-2324,"IIH":321,"III":1497,"IIO":656,"IOO":54,"KAK":4845,"KKA":3386,"KKK":3065,"MHH":-405,"MHI":201,"MMH":-241,"MMM":661,"MOM":841}; + this.TQ1__ = {"BHHH":-227,"BHHI":316,"BHIH":-132,"BIHH":60,"BIII":1595,"BNHH":-744,"BOHH":225,"BOOO":-908,"OAKK":482,"OHHH":281,"OHIH":249,"OIHI":200,"OIIH":-68}; + this.TQ2__ = {"BIHH":-1401,"BIII":-1033,"BKAK":-543,"BOOO":-5591}; + this.TQ3__ = {"BHHH":478,"BHHM":-1073,"BHIH":222,"BHII":-504,"BIIH":-116,"BIII":-105,"BMHI":-863,"BMHM":-464,"BOMH":620,"OHHH":346,"OHHI":1729,"OHII":997,"OHMH":481,"OIHH":623,"OIIH":1344,"OKAK":2792,"OKHH":587,"OKKA":679,"OOHH":110,"OOII":-685}; + this.TQ4__ = {"BHHH":-721,"BHHM":-3604,"BHII":-966,"BIIH":-607,"BIII":-2181,"OAAA":-2763,"OAKK":180,"OHHH":-294,"OHHI":2446,"OHHO":480,"OHIH":-1573,"OIHH":1935,"OIHI":-493,"OIIH":626,"OIII":-4007,"OKAK":-8156}; + this.TW1__ = {"につい":-4681,"東京都":2026}; + this.TW2__ = {"ある程":-2049,"いった":-1256,"ころが":-2434,"しょう":3873,"その後":-4430,"だって":-1049,"ていた":1833,"として":-4657,"ともに":-4517,"もので":1882,"一気に":-792,"初めて":-1512,"同時に":-8097,"大きな":-1255,"対して":-2721,"社会党":-3216}; + this.TW3__ = {"いただ":-1734,"してい":1314,"として":-4314,"につい":-5483,"にとっ":-5989,"に当た":-6247,"ので,":-727,"ので、":-727,"のもの":-600,"れから":-3752,"十二月":-2287}; + this.TW4__ = {"いう.":8576,"いう。":8576,"からな":-2348,"してい":2958,"たが,":1516,"たが、":1516,"ている":1538,"という":1349,"ました":5543,"ません":1097,"ようと":-4258,"よると":5865}; + this.UC1__ = {"A":484,"K":93,"M":645,"O":-505}; + this.UC2__ = {"A":819,"H":1059,"I":409,"M":3987,"N":5775,"O":646}; + this.UC3__ = {"A":-1370,"I":2311}; + this.UC4__ = {"A":-2643,"H":1809,"I":-1032,"K":-3450,"M":3565,"N":3876,"O":6646}; + this.UC5__ = {"H":313,"I":-1238,"K":-799,"M":539,"O":-831}; + this.UC6__ = {"H":-506,"I":-253,"K":87,"M":247,"O":-387}; + this.UP1__ = {"O":-214}; + this.UP2__ = {"B":69,"O":935}; + this.UP3__ = {"B":189}; + this.UQ1__ = {"BH":21,"BI":-12,"BK":-99,"BN":142,"BO":-56,"OH":-95,"OI":477,"OK":410,"OO":-2422}; + this.UQ2__ = {"BH":216,"BI":113,"OK":1759}; + this.UQ3__ = {"BA":-479,"BH":42,"BI":1913,"BK":-7198,"BM":3160,"BN":6427,"BO":14761,"OI":-827,"ON":-3212}; + this.UW1__ = {",":156,"、":156,"「":-463,"あ":-941,"う":-127,"が":-553,"き":121,"こ":505,"で":-201,"と":-547,"ど":-123,"に":-789,"の":-185,"は":-847,"も":-466,"や":-470,"よ":182,"ら":-292,"り":208,"れ":169,"を":-446,"ん":-137,"・":-135,"主":-402,"京":-268,"区":-912,"午":871,"国":-460,"大":561,"委":729,"市":-411,"日":-141,"理":361,"生":-408,"県":-386,"都":-718,"「":-463,"・":-135}; + this.UW2__ = {",":-829,"、":-829,"〇":892,"「":-645,"」":3145,"あ":-538,"い":505,"う":134,"お":-502,"か":1454,"が":-856,"く":-412,"こ":1141,"さ":878,"ざ":540,"し":1529,"す":-675,"せ":300,"そ":-1011,"た":188,"だ":1837,"つ":-949,"て":-291,"で":-268,"と":-981,"ど":1273,"な":1063,"に":-1764,"の":130,"は":-409,"ひ":-1273,"べ":1261,"ま":600,"も":-1263,"や":-402,"よ":1639,"り":-579,"る":-694,"れ":571,"を":-2516,"ん":2095,"ア":-587,"カ":306,"キ":568,"ッ":831,"三":-758,"不":-2150,"世":-302,"中":-968,"主":-861,"事":492,"人":-123,"会":978,"保":362,"入":548,"初":-3025,"副":-1566,"北":-3414,"区":-422,"大":-1769,"天":-865,"太":-483,"子":-1519,"学":760,"実":1023,"小":-2009,"市":-813,"年":-1060,"強":1067,"手":-1519,"揺":-1033,"政":1522,"文":-1355,"新":-1682,"日":-1815,"明":-1462,"最":-630,"朝":-1843,"本":-1650,"東":-931,"果":-665,"次":-2378,"民":-180,"気":-1740,"理":752,"発":529,"目":-1584,"相":-242,"県":-1165,"立":-763,"第":810,"米":509,"自":-1353,"行":838,"西":-744,"見":-3874,"調":1010,"議":1198,"込":3041,"開":1758,"間":-1257,"「":-645,"」":3145,"ッ":831,"ア":-587,"カ":306,"キ":568}; + this.UW3__ = {",":4889,"1":-800,"−":-1723,"、":4889,"々":-2311,"〇":5827,"」":2670,"〓":-3573,"あ":-2696,"い":1006,"う":2342,"え":1983,"お":-4864,"か":-1163,"が":3271,"く":1004,"け":388,"げ":401,"こ":-3552,"ご":-3116,"さ":-1058,"し":-395,"す":584,"せ":3685,"そ":-5228,"た":842,"ち":-521,"っ":-1444,"つ":-1081,"て":6167,"で":2318,"と":1691,"ど":-899,"な":-2788,"に":2745,"の":4056,"は":4555,"ひ":-2171,"ふ":-1798,"へ":1199,"ほ":-5516,"ま":-4384,"み":-120,"め":1205,"も":2323,"や":-788,"よ":-202,"ら":727,"り":649,"る":5905,"れ":2773,"わ":-1207,"を":6620,"ん":-518,"ア":551,"グ":1319,"ス":874,"ッ":-1350,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278,"・":-3794,"一":-1619,"下":-1759,"世":-2087,"両":3815,"中":653,"主":-758,"予":-1193,"二":974,"人":2742,"今":792,"他":1889,"以":-1368,"低":811,"何":4265,"作":-361,"保":-2439,"元":4858,"党":3593,"全":1574,"公":-3030,"六":755,"共":-1880,"円":5807,"再":3095,"分":457,"初":2475,"別":1129,"前":2286,"副":4437,"力":365,"動":-949,"務":-1872,"化":1327,"北":-1038,"区":4646,"千":-2309,"午":-783,"協":-1006,"口":483,"右":1233,"各":3588,"合":-241,"同":3906,"和":-837,"員":4513,"国":642,"型":1389,"場":1219,"外":-241,"妻":2016,"学":-1356,"安":-423,"実":-1008,"家":1078,"小":-513,"少":-3102,"州":1155,"市":3197,"平":-1804,"年":2416,"広":-1030,"府":1605,"度":1452,"建":-2352,"当":-3885,"得":1905,"思":-1291,"性":1822,"戸":-488,"指":-3973,"政":-2013,"教":-1479,"数":3222,"文":-1489,"新":1764,"日":2099,"旧":5792,"昨":-661,"時":-1248,"曜":-951,"最":-937,"月":4125,"期":360,"李":3094,"村":364,"東":-805,"核":5156,"森":2438,"業":484,"氏":2613,"民":-1694,"決":-1073,"法":1868,"海":-495,"無":979,"物":461,"特":-3850,"生":-273,"用":914,"町":1215,"的":7313,"直":-1835,"省":792,"県":6293,"知":-1528,"私":4231,"税":401,"立":-960,"第":1201,"米":7767,"系":3066,"約":3663,"級":1384,"統":-4229,"総":1163,"線":1255,"者":6457,"能":725,"自":-2869,"英":785,"見":1044,"調":-562,"財":-733,"費":1777,"車":1835,"軍":1375,"込":-1504,"通":-1136,"選":-681,"郎":1026,"郡":4404,"部":1200,"金":2163,"長":421,"開":-1432,"間":1302,"関":-1282,"雨":2009,"電":-1045,"非":2066,"駅":1620,"1":-800,"」":2670,"・":-3794,"ッ":-1350,"ア":551,"グ":1319,"ス":874,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278}; + this.UW4__ = {",":3930,".":3508,"―":-4841,"、":3930,"。":3508,"〇":4999,"「":1895,"」":3798,"〓":-5156,"あ":4752,"い":-3435,"う":-640,"え":-2514,"お":2405,"か":530,"が":6006,"き":-4482,"ぎ":-3821,"く":-3788,"け":-4376,"げ":-4734,"こ":2255,"ご":1979,"さ":2864,"し":-843,"じ":-2506,"す":-731,"ず":1251,"せ":181,"そ":4091,"た":5034,"だ":5408,"ち":-3654,"っ":-5882,"つ":-1659,"て":3994,"で":7410,"と":4547,"な":5433,"に":6499,"ぬ":1853,"ね":1413,"の":7396,"は":8578,"ば":1940,"ひ":4249,"び":-4134,"ふ":1345,"へ":6665,"べ":-744,"ほ":1464,"ま":1051,"み":-2082,"む":-882,"め":-5046,"も":4169,"ゃ":-2666,"や":2795,"ょ":-1544,"よ":3351,"ら":-2922,"り":-9726,"る":-14896,"れ":-2613,"ろ":-4570,"わ":-1783,"を":13150,"ん":-2352,"カ":2145,"コ":1789,"セ":1287,"ッ":-724,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637,"・":-4371,"ー":-11870,"一":-2069,"中":2210,"予":782,"事":-190,"井":-1768,"人":1036,"以":544,"会":950,"体":-1286,"作":530,"側":4292,"先":601,"党":-2006,"共":-1212,"内":584,"円":788,"初":1347,"前":1623,"副":3879,"力":-302,"動":-740,"務":-2715,"化":776,"区":4517,"協":1013,"参":1555,"合":-1834,"和":-681,"員":-910,"器":-851,"回":1500,"国":-619,"園":-1200,"地":866,"場":-1410,"塁":-2094,"士":-1413,"多":1067,"大":571,"子":-4802,"学":-1397,"定":-1057,"寺":-809,"小":1910,"屋":-1328,"山":-1500,"島":-2056,"川":-2667,"市":2771,"年":374,"庁":-4556,"後":456,"性":553,"感":916,"所":-1566,"支":856,"改":787,"政":2182,"教":704,"文":522,"方":-856,"日":1798,"時":1829,"最":845,"月":-9066,"木":-485,"来":-442,"校":-360,"業":-1043,"氏":5388,"民":-2716,"気":-910,"沢":-939,"済":-543,"物":-735,"率":672,"球":-1267,"生":-1286,"産":-1101,"田":-2900,"町":1826,"的":2586,"目":922,"省":-3485,"県":2997,"空":-867,"立":-2112,"第":788,"米":2937,"系":786,"約":2171,"経":1146,"統":-1169,"総":940,"線":-994,"署":749,"者":2145,"能":-730,"般":-852,"行":-792,"規":792,"警":-1184,"議":-244,"谷":-1000,"賞":730,"車":-1481,"軍":1158,"輪":-1433,"込":-3370,"近":929,"道":-1291,"選":2596,"郎":-4866,"都":1192,"野":-1100,"銀":-2213,"長":357,"間":-2344,"院":-2297,"際":-2604,"電":-878,"領":-1659,"題":-792,"館":-1984,"首":1749,"高":2120,"「":1895,"」":3798,"・":-4371,"ッ":-724,"ー":-11870,"カ":2145,"コ":1789,"セ":1287,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637}; + this.UW5__ = {",":465,".":-299,"1":-514,"E2":-32768,"]":-2762,"、":465,"。":-299,"「":363,"あ":1655,"い":331,"う":-503,"え":1199,"お":527,"か":647,"が":-421,"き":1624,"ぎ":1971,"く":312,"げ":-983,"さ":-1537,"し":-1371,"す":-852,"だ":-1186,"ち":1093,"っ":52,"つ":921,"て":-18,"で":-850,"と":-127,"ど":1682,"な":-787,"に":-1224,"の":-635,"は":-578,"べ":1001,"み":502,"め":865,"ゃ":3350,"ょ":854,"り":-208,"る":429,"れ":504,"わ":419,"を":-1264,"ん":327,"イ":241,"ル":451,"ン":-343,"中":-871,"京":722,"会":-1153,"党":-654,"務":3519,"区":-901,"告":848,"員":2104,"大":-1296,"学":-548,"定":1785,"嵐":-1304,"市":-2991,"席":921,"年":1763,"思":872,"所":-814,"挙":1618,"新":-1682,"日":218,"月":-4353,"査":932,"格":1356,"機":-1508,"氏":-1347,"田":240,"町":-3912,"的":-3149,"相":1319,"省":-1052,"県":-4003,"研":-997,"社":-278,"空":-813,"統":1955,"者":-2233,"表":663,"語":-1073,"議":1219,"選":-1018,"郎":-368,"長":786,"間":1191,"題":2368,"館":-689,"1":-514,"E2":-32768,"「":363,"イ":241,"ル":451,"ン":-343}; + this.UW6__ = {",":227,".":808,"1":-270,"E1":306,"、":227,"。":808,"あ":-307,"う":189,"か":241,"が":-73,"く":-121,"こ":-200,"じ":1782,"す":383,"た":-428,"っ":573,"て":-1014,"で":101,"と":-105,"な":-253,"に":-149,"の":-417,"は":-236,"も":-206,"り":187,"る":-135,"を":195,"ル":-673,"ン":-496,"一":-277,"中":201,"件":-800,"会":624,"前":302,"区":1792,"員":-1212,"委":798,"学":-960,"市":887,"広":-695,"後":535,"業":-697,"相":753,"社":-507,"福":974,"空":-822,"者":1811,"連":463,"郎":1082,"1":-270,"E1":306,"ル":-673,"ン":-496}; + + return this; + } + TinySegmenter.prototype.ctype_ = function(str) { + for (var i in this.chartype_) { + if (str.match(this.chartype_[i][0])) { + return this.chartype_[i][1]; + } + } + return "O"; + } + + TinySegmenter.prototype.ts_ = function(v) { + if (v) { return v; } + return 0; + } + + TinySegmenter.prototype.segment = function(input) { + if (input == null || input == undefined || input == "") { + return []; + } + var result = []; + var seg = ["B3","B2","B1"]; + var ctype = ["O","O","O"]; + var o = input.split(""); + for (i = 0; i < o.length; ++i) { + seg.push(o[i]); + ctype.push(this.ctype_(o[i])) + } + seg.push("E1"); + seg.push("E2"); + seg.push("E3"); + ctype.push("O"); + ctype.push("O"); + ctype.push("O"); + var word = seg[3]; + var p1 = "U"; + var p2 = "U"; + var p3 = "U"; + for (var i = 4; i < seg.length - 3; ++i) { + var score = this.BIAS__; + var w1 = seg[i-3]; + var w2 = seg[i-2]; + var w3 = seg[i-1]; + var w4 = seg[i]; + var w5 = seg[i+1]; + var w6 = seg[i+2]; + var c1 = ctype[i-3]; + var c2 = ctype[i-2]; + var c3 = ctype[i-1]; + var c4 = ctype[i]; + var c5 = ctype[i+1]; + var c6 = ctype[i+2]; + score += this.ts_(this.UP1__[p1]); + score += this.ts_(this.UP2__[p2]); + score += this.ts_(this.UP3__[p3]); + score += this.ts_(this.BP1__[p1 + p2]); + score += this.ts_(this.BP2__[p2 + p3]); + score += this.ts_(this.UW1__[w1]); + score += this.ts_(this.UW2__[w2]); + score += this.ts_(this.UW3__[w3]); + score += this.ts_(this.UW4__[w4]); + score += this.ts_(this.UW5__[w5]); + score += this.ts_(this.UW6__[w6]); + score += this.ts_(this.BW1__[w2 + w3]); + score += this.ts_(this.BW2__[w3 + w4]); + score += this.ts_(this.BW3__[w4 + w5]); + score += this.ts_(this.TW1__[w1 + w2 + w3]); + score += this.ts_(this.TW2__[w2 + w3 + w4]); + score += this.ts_(this.TW3__[w3 + w4 + w5]); + score += this.ts_(this.TW4__[w4 + w5 + w6]); + score += this.ts_(this.UC1__[c1]); + score += this.ts_(this.UC2__[c2]); + score += this.ts_(this.UC3__[c3]); + score += this.ts_(this.UC4__[c4]); + score += this.ts_(this.UC5__[c5]); + score += this.ts_(this.UC6__[c6]); + score += this.ts_(this.BC1__[c2 + c3]); + score += this.ts_(this.BC2__[c3 + c4]); + score += this.ts_(this.BC3__[c4 + c5]); + score += this.ts_(this.TC1__[c1 + c2 + c3]); + score += this.ts_(this.TC2__[c2 + c3 + c4]); + score += this.ts_(this.TC3__[c3 + c4 + c5]); + score += this.ts_(this.TC4__[c4 + c5 + c6]); + // score += this.ts_(this.TC5__[c4 + c5 + c6]); + score += this.ts_(this.UQ1__[p1 + c1]); + score += this.ts_(this.UQ2__[p2 + c2]); + score += this.ts_(this.UQ3__[p3 + c3]); + score += this.ts_(this.BQ1__[p2 + c2 + c3]); + score += this.ts_(this.BQ2__[p2 + c3 + c4]); + score += this.ts_(this.BQ3__[p3 + c2 + c3]); + score += this.ts_(this.BQ4__[p3 + c3 + c4]); + score += this.ts_(this.TQ1__[p2 + c1 + c2 + c3]); + score += this.ts_(this.TQ2__[p2 + c2 + c3 + c4]); + score += this.ts_(this.TQ3__[p3 + c1 + c2 + c3]); + score += this.ts_(this.TQ4__[p3 + c2 + c3 + c4]); + var p = "O"; + if (score > 0) { + result.push(word); + word = ""; + p = "B"; + } + p1 = p2; + p2 = p3; + p3 = p; + word += seg[i]; + } + result.push(word); + + return result; + } + + lunr.TinySegmenter = TinySegmenter; + }; + +})); \ No newline at end of file diff --git a/assets/javascripts/lunr/wordcut.js b/assets/javascripts/lunr/wordcut.js new file mode 100644 index 0000000000..0d898c9ed1 --- /dev/null +++ b/assets/javascripts/lunr/wordcut.js @@ -0,0 +1,6708 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}(g.lunr || (g.lunr = {})).wordcut = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 1; + }) + this.addWords(words, false) + } + if(finalize){ + this.finalizeDict(); + } + }, + + dictSeek: function (l, r, ch, strOffset, pos) { + var ans = null; + while (l <= r) { + var m = Math.floor((l + r) / 2), + dict_item = this.dict[m], + len = dict_item.length; + if (len <= strOffset) { + l = m + 1; + } else { + var ch_ = dict_item[strOffset]; + if (ch_ < ch) { + l = m + 1; + } else if (ch_ > ch) { + r = m - 1; + } else { + ans = m; + if (pos == LEFT) { + r = m - 1; + } else { + l = m + 1; + } + } + } + } + return ans; + }, + + isFinal: function (acceptor) { + return this.dict[acceptor.l].length == acceptor.strOffset; + }, + + createAcceptor: function () { + return { + l: 0, + r: this.dict.length - 1, + strOffset: 0, + isFinal: false, + dict: this, + transit: function (ch) { + return this.dict.transit(this, ch); + }, + isError: false, + tag: "DICT", + w: 1, + type: "DICT" + }; + }, + + transit: function (acceptor, ch) { + var l = this.dictSeek(acceptor.l, + acceptor.r, + ch, + acceptor.strOffset, + LEFT); + if (l !== null) { + var r = this.dictSeek(l, + acceptor.r, + ch, + acceptor.strOffset, + RIGHT); + acceptor.l = l; + acceptor.r = r; + acceptor.strOffset++; + acceptor.isFinal = this.isFinal(acceptor); + } else { + acceptor.isError = true; + } + return acceptor; + }, + + sortuniq: function(a){ + return a.sort().filter(function(item, pos, arr){ + return !pos || item != arr[pos - 1]; + }) + }, + + flatten: function(a){ + //[[1,2],[3]] -> [1,2,3] + return [].concat.apply([], a); + } +}; +module.exports = WordcutDict; + +}).call(this,"/dist/tmp") +},{"glob":16,"path":22}],3:[function(require,module,exports){ +var WordRule = { + createAcceptor: function(tag) { + if (tag["WORD_RULE"]) + return null; + + return {strOffset: 0, + isFinal: false, + transit: function(ch) { + var lch = ch.toLowerCase(); + if (lch >= "a" && lch <= "z") { + this.isFinal = true; + this.strOffset++; + } else { + this.isError = true; + } + return this; + }, + isError: false, + tag: "WORD_RULE", + type: "WORD_RULE", + w: 1}; + } +}; + +var NumberRule = { + createAcceptor: function(tag) { + if (tag["NUMBER_RULE"]) + return null; + + return {strOffset: 0, + isFinal: false, + transit: function(ch) { + if (ch >= "0" && ch <= "9") { + this.isFinal = true; + this.strOffset++; + } else { + this.isError = true; + } + return this; + }, + isError: false, + tag: "NUMBER_RULE", + type: "NUMBER_RULE", + w: 1}; + } +}; + +var SpaceRule = { + tag: "SPACE_RULE", + createAcceptor: function(tag) { + + if (tag["SPACE_RULE"]) + return null; + + return {strOffset: 0, + isFinal: false, + transit: function(ch) { + if (ch == " " || ch == "\t" || ch == "\r" || ch == "\n" || + ch == "\u00A0" || ch=="\u2003"//nbsp and emsp + ) { + this.isFinal = true; + this.strOffset++; + } else { + this.isError = true; + } + return this; + }, + isError: false, + tag: SpaceRule.tag, + w: 1, + type: "SPACE_RULE"}; + } +} + +var SingleSymbolRule = { + tag: "SINSYM", + createAcceptor: function(tag) { + return {strOffset: 0, + isFinal: false, + transit: function(ch) { + if (this.strOffset == 0 && ch.match(/^[\@\(\)\/\,\-\."`]$/)) { + this.isFinal = true; + this.strOffset++; + } else { + this.isError = true; + } + return this; + }, + isError: false, + tag: "SINSYM", + w: 1, + type: "SINSYM"}; + } +} + + +var LatinRules = [WordRule, SpaceRule, SingleSymbolRule, NumberRule]; + +module.exports = LatinRules; + +},{}],4:[function(require,module,exports){ +var _ = require("underscore") + , WordcutCore = require("./wordcut_core"); +var PathInfoBuilder = { + + /* + buildByPartAcceptors: function(path, acceptors, i) { + var + var genInfos = partAcceptors.reduce(function(genInfos, acceptor) { + + }, []); + + return genInfos; + } + */ + + buildByAcceptors: function(path, finalAcceptors, i) { + var self = this; + var infos = finalAcceptors.map(function(acceptor) { + var p = i - acceptor.strOffset + 1 + , _info = path[p]; + + var info = {p: p, + mw: _info.mw + (acceptor.mw === undefined ? 0 : acceptor.mw), + w: acceptor.w + _info.w, + unk: (acceptor.unk ? acceptor.unk : 0) + _info.unk, + type: acceptor.type}; + + if (acceptor.type == "PART") { + for(var j = p + 1; j <= i; j++) { + path[j].merge = p; + } + info.merge = p; + } + + return info; + }); + return infos.filter(function(info) { return info; }); + }, + + fallback: function(path, leftBoundary, text, i) { + var _info = path[leftBoundary]; + if (text[i].match(/[\u0E48-\u0E4E]/)) { + if (leftBoundary != 0) + leftBoundary = path[leftBoundary].p; + return {p: leftBoundary, + mw: 0, + w: 1 + _info.w, + unk: 1 + _info.unk, + type: "UNK"}; +/* } else if(leftBoundary > 0 && path[leftBoundary].type !== "UNK") { + leftBoundary = path[leftBoundary].p; + return {p: leftBoundary, + w: 1 + _info.w, + unk: 1 + _info.unk, + type: "UNK"}; */ + } else { + return {p: leftBoundary, + mw: _info.mw, + w: 1 + _info.w, + unk: 1 + _info.unk, + type: "UNK"}; + } + }, + + build: function(path, finalAcceptors, i, leftBoundary, text) { + var basicPathInfos = this.buildByAcceptors(path, finalAcceptors, i); + if (basicPathInfos.length > 0) { + return basicPathInfos; + } else { + return [this.fallback(path, leftBoundary, text, i)]; + } + } +}; + +module.exports = function() { + return _.clone(PathInfoBuilder); +} + +},{"./wordcut_core":8,"underscore":25}],5:[function(require,module,exports){ +var _ = require("underscore"); + + +var PathSelector = { + selectPath: function(paths) { + var path = paths.reduce(function(selectedPath, path) { + if (selectedPath == null) { + return path; + } else { + if (path.unk < selectedPath.unk) + return path; + if (path.unk == selectedPath.unk) { + if (path.mw < selectedPath.mw) + return path + if (path.mw == selectedPath.mw) { + if (path.w < selectedPath.w) + return path; + } + } + return selectedPath; + } + }, null); + return path; + }, + + createPath: function() { + return [{p:null, w:0, unk:0, type: "INIT", mw:0}]; + } +}; + +module.exports = function() { + return _.clone(PathSelector); +}; + +},{"underscore":25}],6:[function(require,module,exports){ +function isMatch(pat, offset, ch) { + if (pat.length <= offset) + return false; + var _ch = pat[offset]; + return _ch == ch || + (_ch.match(/[กข]/) && ch.match(/[ก-ฮ]/)) || + (_ch.match(/[มบ]/) && ch.match(/[ก-ฮ]/)) || + (_ch.match(/\u0E49/) && ch.match(/[\u0E48-\u0E4B]/)); +} + +var Rule0 = { + pat: "เหก็ม", + createAcceptor: function(tag) { + return {strOffset: 0, + isFinal: false, + transit: function(ch) { + if (isMatch(Rule0.pat, this.strOffset,ch)) { + this.isFinal = (this.strOffset + 1 == Rule0.pat.length); + this.strOffset++; + } else { + this.isError = true; + } + return this; + }, + isError: false, + tag: "THAI_RULE", + type: "THAI_RULE", + w: 1}; + } +}; + +var PartRule = { + createAcceptor: function(tag) { + return {strOffset: 0, + patterns: [ + "แก", "เก", "ก้", "กก์", "กา", "กี", "กิ", "กืก" + ], + isFinal: false, + transit: function(ch) { + var offset = this.strOffset; + this.patterns = this.patterns.filter(function(pat) { + return isMatch(pat, offset, ch); + }); + + if (this.patterns.length > 0) { + var len = 1 + offset; + this.isFinal = this.patterns.some(function(pat) { + return pat.length == len; + }); + this.strOffset++; + } else { + this.isError = true; + } + return this; + }, + isError: false, + tag: "PART", + type: "PART", + unk: 1, + w: 1}; + } +}; + +var ThaiRules = [Rule0, PartRule]; + +module.exports = ThaiRules; + +},{}],7:[function(require,module,exports){ +var sys = require("sys") + , WordcutDict = require("./dict") + , WordcutCore = require("./wordcut_core") + , PathInfoBuilder = require("./path_info_builder") + , PathSelector = require("./path_selector") + , Acceptors = require("./acceptors") + , latinRules = require("./latin_rules") + , thaiRules = require("./thai_rules") + , _ = require("underscore"); + + +var Wordcut = Object.create(WordcutCore); +Wordcut.defaultPathInfoBuilder = PathInfoBuilder; +Wordcut.defaultPathSelector = PathSelector; +Wordcut.defaultAcceptors = Acceptors; +Wordcut.defaultLatinRules = latinRules; +Wordcut.defaultThaiRules = thaiRules; +Wordcut.defaultDict = WordcutDict; + + +Wordcut.initNoDict = function(dict_path) { + var self = this; + self.pathInfoBuilder = new self.defaultPathInfoBuilder; + self.pathSelector = new self.defaultPathSelector; + self.acceptors = new self.defaultAcceptors; + self.defaultLatinRules.forEach(function(rule) { + self.acceptors.creators.push(rule); + }); + self.defaultThaiRules.forEach(function(rule) { + self.acceptors.creators.push(rule); + }); +}; + +Wordcut.init = function(dict_path, withDefault, additionalWords) { + withDefault = withDefault || false; + this.initNoDict(); + var dict = _.clone(this.defaultDict); + dict.init(dict_path, withDefault, additionalWords); + this.acceptors.creators.push(dict); +}; + +module.exports = Wordcut; + +},{"./acceptors":1,"./dict":2,"./latin_rules":3,"./path_info_builder":4,"./path_selector":5,"./thai_rules":6,"./wordcut_core":8,"sys":28,"underscore":25}],8:[function(require,module,exports){ +var WordcutCore = { + + buildPath: function(text) { + var self = this + , path = self.pathSelector.createPath() + , leftBoundary = 0; + self.acceptors.reset(); + for (var i = 0; i < text.length; i++) { + var ch = text[i]; + self.acceptors.transit(ch); + + var possiblePathInfos = self + .pathInfoBuilder + .build(path, + self.acceptors.getFinalAcceptors(), + i, + leftBoundary, + text); + var selectedPath = self.pathSelector.selectPath(possiblePathInfos) + + path.push(selectedPath); + if (selectedPath.type !== "UNK") { + leftBoundary = i; + } + } + return path; + }, + + pathToRanges: function(path) { + var e = path.length - 1 + , ranges = []; + + while (e > 0) { + var info = path[e] + , s = info.p; + + if (info.merge !== undefined && ranges.length > 0) { + var r = ranges[ranges.length - 1]; + r.s = info.merge; + s = r.s; + } else { + ranges.push({s:s, e:e}); + } + e = s; + } + return ranges.reverse(); + }, + + rangesToText: function(text, ranges, delimiter) { + return ranges.map(function(r) { + return text.substring(r.s, r.e); + }).join(delimiter); + }, + + cut: function(text, delimiter) { + var path = this.buildPath(text) + , ranges = this.pathToRanges(path); + return this + .rangesToText(text, ranges, + (delimiter === undefined ? "|" : delimiter)); + }, + + cutIntoRanges: function(text, noText) { + var path = this.buildPath(text) + , ranges = this.pathToRanges(path); + + if (!noText) { + ranges.forEach(function(r) { + r.text = text.substring(r.s, r.e); + }); + } + return ranges; + }, + + cutIntoArray: function(text) { + var path = this.buildPath(text) + , ranges = this.pathToRanges(path); + + return ranges.map(function(r) { + return text.substring(r.s, r.e) + }); + } +}; + +module.exports = WordcutCore; + +},{}],9:[function(require,module,exports){ +// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 +// +// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! +// +// Originally from narwhal.js (http://narwhaljs.org) +// Copyright (c) 2009 Thomas Robinson <280north.com> +// +// 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 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. + +// when used in node, this will actually load the util module we depend on +// versus loading the builtin util module as happens otherwise +// this is a bug in node module loading as far as I am concerned +var util = require('util/'); + +var pSlice = Array.prototype.slice; +var hasOwn = Object.prototype.hasOwnProperty; + +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = module.exports = ok; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({ message: message, +// actual: actual, +// expected: expected }) + +assert.AssertionError = function AssertionError(options) { + this.name = 'AssertionError'; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + if (options.message) { + this.message = options.message; + this.generatedMessage = false; + } else { + this.message = getMessage(this); + this.generatedMessage = true; + } + var stackStartFunction = options.stackStartFunction || fail; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } + else { + // non v8 browsers so we can have a stacktrace + var err = new Error(); + if (err.stack) { + var out = err.stack; + + // try to strip useless frames + var fn_name = stackStartFunction.name; + var idx = out.indexOf('\n' + fn_name); + if (idx >= 0) { + // once we have located the function frame + // we need to strip out everything before it (and its line) + var next_line = out.indexOf('\n', idx + 1); + out = out.substring(next_line + 1); + } + + this.stack = out; + } + } +}; + +// assert.AssertionError instanceof Error +util.inherits(assert.AssertionError, Error); + +function replacer(key, value) { + if (util.isUndefined(value)) { + return '' + value; + } + if (util.isNumber(value) && !isFinite(value)) { + return value.toString(); + } + if (util.isFunction(value) || util.isRegExp(value)) { + return value.toString(); + } + return value; +} + +function truncate(s, n) { + if (util.isString(s)) { + return s.length < n ? s : s.slice(0, n); + } else { + return s; + } +} + +function getMessage(self) { + return truncate(JSON.stringify(self.actual, replacer), 128) + ' ' + + self.operator + ' ' + + truncate(JSON.stringify(self.expected, replacer), 128); +} + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, !!guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +function ok(value, message) { + if (!value) fail(value, true, message, '==', assert.ok); +} +assert.ok = ok; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, '==', assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, '!=', assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected)) { + fail(actual, expected, message, 'deepEqual', assert.deepEqual); + } +}; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + } else if (util.isBuffer(actual) && util.isBuffer(expected)) { + if (actual.length != expected.length) return false; + + for (var i = 0; i < actual.length; i++) { + if (actual[i] !== expected[i]) return false; + } + + return true; + + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (util.isDate(actual) && util.isDate(expected)) { + return actual.getTime() === expected.getTime(); + + // 7.3 If the expected value is a RegExp object, the actual value is + // equivalent if it is also a RegExp object with the same source and + // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). + } else if (util.isRegExp(actual) && util.isRegExp(expected)) { + return actual.source === expected.source && + actual.global === expected.global && + actual.multiline === expected.multiline && + actual.lastIndex === expected.lastIndex && + actual.ignoreCase === expected.ignoreCase; + + // 7.4. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if (!util.isObject(actual) && !util.isObject(expected)) { + return actual == expected; + + // 7.5 For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected); + } +} + +function isArguments(object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +} + +function objEquiv(a, b) { + if (util.isNullOrUndefined(a) || util.isNullOrUndefined(b)) + return false; + // an identical 'prototype' property. + if (a.prototype !== b.prototype) return false; + // if one is a primitive, the other must be same + if (util.isPrimitive(a) || util.isPrimitive(b)) { + return a === b; + } + var aIsArgs = isArguments(a), + bIsArgs = isArguments(b); + if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) + return false; + if (aIsArgs) { + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b); + } + var ka = objectKeys(a), + kb = objectKeys(b), + key, i; + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key])) return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected)) { + fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); + } +}; + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, '===', assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as +// determined by !==. assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, '!==', assert.notStrictEqual); + } +}; + +function expectedException(actual, expected) { + if (!actual || !expected) { + return false; + } + + if (Object.prototype.toString.call(expected) == '[object RegExp]') { + return expected.test(actual); + } else if (actual instanceof expected) { + return true; + } else if (expected.call({}, actual) === true) { + return true; + } + + return false; +} + +function _throws(shouldThrow, block, expected, message) { + var actual; + + if (util.isString(expected)) { + message = expected; + expected = null; + } + + try { + block(); + } catch (e) { + actual = e; + } + + message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + + (message ? ' ' + message : '.'); + + if (shouldThrow && !actual) { + fail(actual, expected, 'Missing expected exception' + message); + } + + if (!shouldThrow && expectedException(actual, expected)) { + fail(actual, expected, 'Got unwanted exception' + message); + } + + if ((shouldThrow && actual && expected && + !expectedException(actual, expected)) || (!shouldThrow && actual)) { + throw actual; + } +} + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert.throws = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [true].concat(pSlice.call(arguments))); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/message) { + _throws.apply(this, [false].concat(pSlice.call(arguments))); +}; + +assert.ifError = function(err) { if (err) {throw err;}}; + +var objectKeys = Object.keys || function (obj) { + var keys = []; + for (var key in obj) { + if (hasOwn.call(obj, key)) keys.push(key); + } + return keys; +}; + +},{"util/":28}],10:[function(require,module,exports){ +'use strict'; +module.exports = balanced; +function balanced(a, b, str) { + if (a instanceof RegExp) a = maybeMatch(a, str); + if (b instanceof RegExp) b = maybeMatch(b, str); + + var r = range(a, b, str); + + return r && { + start: r[0], + end: r[1], + pre: str.slice(0, r[0]), + body: str.slice(r[0] + a.length, r[1]), + post: str.slice(r[1] + b.length) + }; +} + +function maybeMatch(reg, str) { + var m = str.match(reg); + return m ? m[0] : null; +} + +balanced.range = range; +function range(a, b, str) { + var begs, beg, left, right, result; + var ai = str.indexOf(a); + var bi = str.indexOf(b, ai + 1); + var i = ai; + + if (ai >= 0 && bi > 0) { + begs = []; + left = str.length; + + while (i >= 0 && !result) { + if (i == ai) { + begs.push(i); + ai = str.indexOf(a, i + 1); + } else if (begs.length == 1) { + result = [ begs.pop(), bi ]; + } else { + beg = begs.pop(); + if (beg < left) { + left = beg; + right = bi; + } + + bi = str.indexOf(b, i + 1); + } + + i = ai < bi && ai >= 0 ? ai : bi; + } + + if (begs.length) { + result = [ left, right ]; + } + } + + return result; +} + +},{}],11:[function(require,module,exports){ +var concatMap = require('concat-map'); +var balanced = require('balanced-match'); + +module.exports = expandTop; + +var escSlash = '\0SLASH'+Math.random()+'\0'; +var escOpen = '\0OPEN'+Math.random()+'\0'; +var escClose = '\0CLOSE'+Math.random()+'\0'; +var escComma = '\0COMMA'+Math.random()+'\0'; +var escPeriod = '\0PERIOD'+Math.random()+'\0'; + +function numeric(str) { + return parseInt(str, 10) == str + ? parseInt(str, 10) + : str.charCodeAt(0); +} + +function escapeBraces(str) { + return str.split('\\\\').join(escSlash) + .split('\\{').join(escOpen) + .split('\\}').join(escClose) + .split('\\,').join(escComma) + .split('\\.').join(escPeriod); +} + +function unescapeBraces(str) { + return str.split(escSlash).join('\\') + .split(escOpen).join('{') + .split(escClose).join('}') + .split(escComma).join(',') + .split(escPeriod).join('.'); +} + + +// Basically just str.split(","), but handling cases +// where we have nested braced sections, which should be +// treated as individual members, like {a,{b,c},d} +function parseCommaParts(str) { + if (!str) + return ['']; + + var parts = []; + var m = balanced('{', '}', str); + + if (!m) + return str.split(','); + + var pre = m.pre; + var body = m.body; + var post = m.post; + var p = pre.split(','); + + p[p.length-1] += '{' + body + '}'; + var postParts = parseCommaParts(post); + if (post.length) { + p[p.length-1] += postParts.shift(); + p.push.apply(p, postParts); + } + + parts.push.apply(parts, p); + + return parts; +} + +function expandTop(str) { + if (!str) + return []; + + // I don't know why Bash 4.3 does this, but it does. + // Anything starting with {} will have the first two bytes preserved + // but *only* at the top level, so {},a}b will not expand to anything, + // but a{},b}c will be expanded to [a}c,abc]. + // One could argue that this is a bug in Bash, but since the goal of + // this module is to match Bash's rules, we escape a leading {} + if (str.substr(0, 2) === '{}') { + str = '\\{\\}' + str.substr(2); + } + + return expand(escapeBraces(str), true).map(unescapeBraces); +} + +function identity(e) { + return e; +} + +function embrace(str) { + return '{' + str + '}'; +} +function isPadded(el) { + return /^-?0\d/.test(el); +} + +function lte(i, y) { + return i <= y; +} +function gte(i, y) { + return i >= y; +} + +function expand(str, isTop) { + var expansions = []; + + var m = balanced('{', '}', str); + if (!m || /\$$/.test(m.pre)) return [str]; + + var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); + var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); + var isSequence = isNumericSequence || isAlphaSequence; + var isOptions = m.body.indexOf(',') >= 0; + if (!isSequence && !isOptions) { + // {a},b} + if (m.post.match(/,.*\}/)) { + str = m.pre + '{' + m.body + escClose + m.post; + return expand(str); + } + return [str]; + } + + var n; + if (isSequence) { + n = m.body.split(/\.\./); + } else { + n = parseCommaParts(m.body); + if (n.length === 1) { + // x{{a,b}}y ==> x{a}y x{b}y + n = expand(n[0], false).map(embrace); + if (n.length === 1) { + var post = m.post.length + ? expand(m.post, false) + : ['']; + return post.map(function(p) { + return m.pre + n[0] + p; + }); + } + } + } + + // at this point, n is the parts, and we know it's not a comma set + // with a single entry. + + // no need to expand pre, since it is guaranteed to be free of brace-sets + var pre = m.pre; + var post = m.post.length + ? expand(m.post, false) + : ['']; + + var N; + + if (isSequence) { + var x = numeric(n[0]); + var y = numeric(n[1]); + var width = Math.max(n[0].length, n[1].length) + var incr = n.length == 3 + ? Math.abs(numeric(n[2])) + : 1; + var test = lte; + var reverse = y < x; + if (reverse) { + incr *= -1; + test = gte; + } + var pad = n.some(isPadded); + + N = []; + + for (var i = x; test(i, y); i += incr) { + var c; + if (isAlphaSequence) { + c = String.fromCharCode(i); + if (c === '\\') + c = ''; + } else { + c = String(i); + if (pad) { + var need = width - c.length; + if (need > 0) { + var z = new Array(need + 1).join('0'); + if (i < 0) + c = '-' + z + c.slice(1); + else + c = z + c; + } + } + } + N.push(c); + } + } else { + N = concatMap(n, function(el) { return expand(el, false) }); + } + + for (var j = 0; j < N.length; j++) { + for (var k = 0; k < post.length; k++) { + var expansion = pre + N[j] + post[k]; + if (!isTop || isSequence || expansion) + expansions.push(expansion); + } + } + + return expansions; +} + + +},{"balanced-match":10,"concat-map":13}],12:[function(require,module,exports){ + +},{}],13:[function(require,module,exports){ +module.exports = function (xs, fn) { + var res = []; + for (var i = 0; i < xs.length; i++) { + var x = fn(xs[i], i); + if (isArray(x)) res.push.apply(res, x); + else res.push(x); + } + return res; +}; + +var isArray = Array.isArray || function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]'; +}; + +},{}],14:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +function EventEmitter() { + this._events = this._events || {}; + this._maxListeners = this._maxListeners || undefined; +} +module.exports = EventEmitter; + +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; + +EventEmitter.prototype._events = undefined; +EventEmitter.prototype._maxListeners = undefined; + +// By default EventEmitters will print a warning if more than 10 listeners are +// added to it. This is a useful default which helps finding memory leaks. +EventEmitter.defaultMaxListeners = 10; + +// Obviously not all Emitters should be limited to 10. This function allows +// that to be increased. Set to zero for unlimited. +EventEmitter.prototype.setMaxListeners = function(n) { + if (!isNumber(n) || n < 0 || isNaN(n)) + throw TypeError('n must be a positive number'); + this._maxListeners = n; + return this; +}; + +EventEmitter.prototype.emit = function(type) { + var er, handler, len, args, i, listeners; + + if (!this._events) + this._events = {}; + + // If there is no 'error' event listener then throw. + if (type === 'error') { + if (!this._events.error || + (isObject(this._events.error) && !this._events.error.length)) { + er = arguments[1]; + if (er instanceof Error) { + throw er; // Unhandled 'error' event + } + throw TypeError('Uncaught, unspecified "error" event.'); + } + } + + handler = this._events[type]; + + if (isUndefined(handler)) + return false; + + if (isFunction(handler)) { + switch (arguments.length) { + // fast cases + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + handler.apply(this, args); + } + } else if (isObject(handler)) { + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + + listeners = handler.slice(); + len = listeners.length; + for (i = 0; i < len; i++) + listeners[i].apply(this, args); + } + + return true; +}; + +EventEmitter.prototype.addListener = function(type, listener) { + var m; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events) + this._events = {}; + + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (this._events.newListener) + this.emit('newListener', type, + isFunction(listener.listener) ? + listener.listener : listener); + + if (!this._events[type]) + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + else if (isObject(this._events[type])) + // If we've already got an array, just append. + this._events[type].push(listener); + else + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; + + // Check for listener leak + if (isObject(this._events[type]) && !this._events[type].warned) { + var m; + if (!isUndefined(this._maxListeners)) { + m = this._maxListeners; + } else { + m = EventEmitter.defaultMaxListeners; + } + + if (m && m > 0 && this._events[type].length > m) { + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + if (typeof console.trace === 'function') { + // not supported in IE 10 + console.trace(); + } + } + } + + return this; +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.once = function(type, listener) { + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + var fired = false; + + function g() { + this.removeListener(type, g); + + if (!fired) { + fired = true; + listener.apply(this, arguments); + } + } + + g.listener = listener; + this.on(type, g); + + return this; +}; + +// emits a 'removeListener' event iff the listener was removed +EventEmitter.prototype.removeListener = function(type, listener) { + var list, position, length, i; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events || !this._events[type]) + return this; + + list = this._events[type]; + length = list.length; + position = -1; + + if (list === listener || + (isFunction(list.listener) && list.listener === listener)) { + delete this._events[type]; + if (this._events.removeListener) + this.emit('removeListener', type, listener); + + } else if (isObject(list)) { + for (i = length; i-- > 0;) { + if (list[i] === listener || + (list[i].listener && list[i].listener === listener)) { + position = i; + break; + } + } + + if (position < 0) + return this; + + if (list.length === 1) { + list.length = 0; + delete this._events[type]; + } else { + list.splice(position, 1); + } + + if (this._events.removeListener) + this.emit('removeListener', type, listener); + } + + return this; +}; + +EventEmitter.prototype.removeAllListeners = function(type) { + var key, listeners; + + if (!this._events) + return this; + + // not listening for removeListener, no need to emit + if (!this._events.removeListener) { + if (arguments.length === 0) + this._events = {}; + else if (this._events[type]) + delete this._events[type]; + return this; + } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + for (key in this._events) { + if (key === 'removeListener') continue; + this.removeAllListeners(key); + } + this.removeAllListeners('removeListener'); + this._events = {}; + return this; + } + + listeners = this._events[type]; + + if (isFunction(listeners)) { + this.removeListener(type, listeners); + } else { + // LIFO order + while (listeners.length) + this.removeListener(type, listeners[listeners.length - 1]); + } + delete this._events[type]; + + return this; +}; + +EventEmitter.prototype.listeners = function(type) { + var ret; + if (!this._events || !this._events[type]) + ret = []; + else if (isFunction(this._events[type])) + ret = [this._events[type]]; + else + ret = this._events[type].slice(); + return ret; +}; + +EventEmitter.listenerCount = function(emitter, type) { + var ret; + if (!emitter._events || !emitter._events[type]) + ret = 0; + else if (isFunction(emitter._events[type])) + ret = 1; + else + ret = emitter._events[type].length; + return ret; +}; + +function isFunction(arg) { + return typeof arg === 'function'; +} + +function isNumber(arg) { + return typeof arg === 'number'; +} + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} + +function isUndefined(arg) { + return arg === void 0; +} + +},{}],15:[function(require,module,exports){ +(function (process){ +exports.alphasort = alphasort +exports.alphasorti = alphasorti +exports.setopts = setopts +exports.ownProp = ownProp +exports.makeAbs = makeAbs +exports.finish = finish +exports.mark = mark +exports.isIgnored = isIgnored +exports.childrenIgnored = childrenIgnored + +function ownProp (obj, field) { + return Object.prototype.hasOwnProperty.call(obj, field) +} + +var path = require("path") +var minimatch = require("minimatch") +var isAbsolute = require("path-is-absolute") +var Minimatch = minimatch.Minimatch + +function alphasorti (a, b) { + return a.toLowerCase().localeCompare(b.toLowerCase()) +} + +function alphasort (a, b) { + return a.localeCompare(b) +} + +function setupIgnores (self, options) { + self.ignore = options.ignore || [] + + if (!Array.isArray(self.ignore)) + self.ignore = [self.ignore] + + if (self.ignore.length) { + self.ignore = self.ignore.map(ignoreMap) + } +} + +function ignoreMap (pattern) { + var gmatcher = null + if (pattern.slice(-3) === '/**') { + var gpattern = pattern.replace(/(\/\*\*)+$/, '') + gmatcher = new Minimatch(gpattern) + } + + return { + matcher: new Minimatch(pattern), + gmatcher: gmatcher + } +} + +function setopts (self, pattern, options) { + if (!options) + options = {} + + // base-matching: just use globstar for that. + if (options.matchBase && -1 === pattern.indexOf("/")) { + if (options.noglobstar) { + throw new Error("base matching requires globstar") + } + pattern = "**/" + pattern + } + + self.silent = !!options.silent + self.pattern = pattern + self.strict = options.strict !== false + self.realpath = !!options.realpath + self.realpathCache = options.realpathCache || Object.create(null) + self.follow = !!options.follow + self.dot = !!options.dot + self.mark = !!options.mark + self.nodir = !!options.nodir + if (self.nodir) + self.mark = true + self.sync = !!options.sync + self.nounique = !!options.nounique + self.nonull = !!options.nonull + self.nosort = !!options.nosort + self.nocase = !!options.nocase + self.stat = !!options.stat + self.noprocess = !!options.noprocess + + self.maxLength = options.maxLength || Infinity + self.cache = options.cache || Object.create(null) + self.statCache = options.statCache || Object.create(null) + self.symlinks = options.symlinks || Object.create(null) + + setupIgnores(self, options) + + self.changedCwd = false + var cwd = process.cwd() + if (!ownProp(options, "cwd")) + self.cwd = cwd + else { + self.cwd = options.cwd + self.changedCwd = path.resolve(options.cwd) !== cwd + } + + self.root = options.root || path.resolve(self.cwd, "/") + self.root = path.resolve(self.root) + if (process.platform === "win32") + self.root = self.root.replace(/\\/g, "/") + + self.nomount = !!options.nomount + + // disable comments and negation unless the user explicitly + // passes in false as the option. + options.nonegate = options.nonegate === false ? false : true + options.nocomment = options.nocomment === false ? false : true + deprecationWarning(options) + + self.minimatch = new Minimatch(pattern, options) + self.options = self.minimatch.options +} + +// TODO(isaacs): remove entirely in v6 +// exported to reset in tests +exports.deprecationWarned +function deprecationWarning(options) { + if (!options.nonegate || !options.nocomment) { + if (process.noDeprecation !== true && !exports.deprecationWarned) { + var msg = 'glob WARNING: comments and negation will be disabled in v6' + if (process.throwDeprecation) + throw new Error(msg) + else if (process.traceDeprecation) + console.trace(msg) + else + console.error(msg) + + exports.deprecationWarned = true + } + } +} + +function finish (self) { + var nou = self.nounique + var all = nou ? [] : Object.create(null) + + for (var i = 0, l = self.matches.length; i < l; i ++) { + var matches = self.matches[i] + if (!matches || Object.keys(matches).length === 0) { + if (self.nonull) { + // do like the shell, and spit out the literal glob + var literal = self.minimatch.globSet[i] + if (nou) + all.push(literal) + else + all[literal] = true + } + } else { + // had matches + var m = Object.keys(matches) + if (nou) + all.push.apply(all, m) + else + m.forEach(function (m) { + all[m] = true + }) + } + } + + if (!nou) + all = Object.keys(all) + + if (!self.nosort) + all = all.sort(self.nocase ? alphasorti : alphasort) + + // at *some* point we statted all of these + if (self.mark) { + for (var i = 0; i < all.length; i++) { + all[i] = self._mark(all[i]) + } + if (self.nodir) { + all = all.filter(function (e) { + return !(/\/$/.test(e)) + }) + } + } + + if (self.ignore.length) + all = all.filter(function(m) { + return !isIgnored(self, m) + }) + + self.found = all +} + +function mark (self, p) { + var abs = makeAbs(self, p) + var c = self.cache[abs] + var m = p + if (c) { + var isDir = c === 'DIR' || Array.isArray(c) + var slash = p.slice(-1) === '/' + + if (isDir && !slash) + m += '/' + else if (!isDir && slash) + m = m.slice(0, -1) + + if (m !== p) { + var mabs = makeAbs(self, m) + self.statCache[mabs] = self.statCache[abs] + self.cache[mabs] = self.cache[abs] + } + } + + return m +} + +// lotta situps... +function makeAbs (self, f) { + var abs = f + if (f.charAt(0) === '/') { + abs = path.join(self.root, f) + } else if (isAbsolute(f) || f === '') { + abs = f + } else if (self.changedCwd) { + abs = path.resolve(self.cwd, f) + } else { + abs = path.resolve(f) + } + return abs +} + + +// Return true, if pattern ends with globstar '**', for the accompanying parent directory. +// Ex:- If node_modules/** is the pattern, add 'node_modules' to ignore list along with it's contents +function isIgnored (self, path) { + if (!self.ignore.length) + return false + + return self.ignore.some(function(item) { + return item.matcher.match(path) || !!(item.gmatcher && item.gmatcher.match(path)) + }) +} + +function childrenIgnored (self, path) { + if (!self.ignore.length) + return false + + return self.ignore.some(function(item) { + return !!(item.gmatcher && item.gmatcher.match(path)) + }) +} + +}).call(this,require('_process')) +},{"_process":24,"minimatch":20,"path":22,"path-is-absolute":23}],16:[function(require,module,exports){ +(function (process){ +// Approach: +// +// 1. Get the minimatch set +// 2. For each pattern in the set, PROCESS(pattern, false) +// 3. Store matches per-set, then uniq them +// +// PROCESS(pattern, inGlobStar) +// Get the first [n] items from pattern that are all strings +// Join these together. This is PREFIX. +// If there is no more remaining, then stat(PREFIX) and +// add to matches if it succeeds. END. +// +// If inGlobStar and PREFIX is symlink and points to dir +// set ENTRIES = [] +// else readdir(PREFIX) as ENTRIES +// If fail, END +// +// with ENTRIES +// If pattern[n] is GLOBSTAR +// // handle the case where the globstar match is empty +// // by pruning it out, and testing the resulting pattern +// PROCESS(pattern[0..n] + pattern[n+1 .. $], false) +// // handle other cases. +// for ENTRY in ENTRIES (not dotfiles) +// // attach globstar + tail onto the entry +// // Mark that this entry is a globstar match +// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $], true) +// +// else // not globstar +// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot) +// Test ENTRY against pattern[n] +// If fails, continue +// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $]) +// +// Caveat: +// Cache all stats and readdirs results to minimize syscall. Since all +// we ever care about is existence and directory-ness, we can just keep +// `true` for files, and [children,...] for directories, or `false` for +// things that don't exist. + +module.exports = glob + +var fs = require('fs') +var minimatch = require('minimatch') +var Minimatch = minimatch.Minimatch +var inherits = require('inherits') +var EE = require('events').EventEmitter +var path = require('path') +var assert = require('assert') +var isAbsolute = require('path-is-absolute') +var globSync = require('./sync.js') +var common = require('./common.js') +var alphasort = common.alphasort +var alphasorti = common.alphasorti +var setopts = common.setopts +var ownProp = common.ownProp +var inflight = require('inflight') +var util = require('util') +var childrenIgnored = common.childrenIgnored +var isIgnored = common.isIgnored + +var once = require('once') + +function glob (pattern, options, cb) { + if (typeof options === 'function') cb = options, options = {} + if (!options) options = {} + + if (options.sync) { + if (cb) + throw new TypeError('callback provided to sync glob') + return globSync(pattern, options) + } + + return new Glob(pattern, options, cb) +} + +glob.sync = globSync +var GlobSync = glob.GlobSync = globSync.GlobSync + +// old api surface +glob.glob = glob + +glob.hasMagic = function (pattern, options_) { + var options = util._extend({}, options_) + options.noprocess = true + + var g = new Glob(pattern, options) + var set = g.minimatch.set + if (set.length > 1) + return true + + for (var j = 0; j < set[0].length; j++) { + if (typeof set[0][j] !== 'string') + return true + } + + return false +} + +glob.Glob = Glob +inherits(Glob, EE) +function Glob (pattern, options, cb) { + if (typeof options === 'function') { + cb = options + options = null + } + + if (options && options.sync) { + if (cb) + throw new TypeError('callback provided to sync glob') + return new GlobSync(pattern, options) + } + + if (!(this instanceof Glob)) + return new Glob(pattern, options, cb) + + setopts(this, pattern, options) + this._didRealPath = false + + // process each pattern in the minimatch set + var n = this.minimatch.set.length + + // The matches are stored as {: true,...} so that + // duplicates are automagically pruned. + // Later, we do an Object.keys() on these. + // Keep them as a list so we can fill in when nonull is set. + this.matches = new Array(n) + + if (typeof cb === 'function') { + cb = once(cb) + this.on('error', cb) + this.on('end', function (matches) { + cb(null, matches) + }) + } + + var self = this + var n = this.minimatch.set.length + this._processing = 0 + this.matches = new Array(n) + + this._emitQueue = [] + this._processQueue = [] + this.paused = false + + if (this.noprocess) + return this + + if (n === 0) + return done() + + for (var i = 0; i < n; i ++) { + this._process(this.minimatch.set[i], i, false, done) + } + + function done () { + --self._processing + if (self._processing <= 0) + self._finish() + } +} + +Glob.prototype._finish = function () { + assert(this instanceof Glob) + if (this.aborted) + return + + if (this.realpath && !this._didRealpath) + return this._realpath() + + common.finish(this) + this.emit('end', this.found) +} + +Glob.prototype._realpath = function () { + if (this._didRealpath) + return + + this._didRealpath = true + + var n = this.matches.length + if (n === 0) + return this._finish() + + var self = this + for (var i = 0; i < this.matches.length; i++) + this._realpathSet(i, next) + + function next () { + if (--n === 0) + self._finish() + } +} + +Glob.prototype._realpathSet = function (index, cb) { + var matchset = this.matches[index] + if (!matchset) + return cb() + + var found = Object.keys(matchset) + var self = this + var n = found.length + + if (n === 0) + return cb() + + var set = this.matches[index] = Object.create(null) + found.forEach(function (p, i) { + // If there's a problem with the stat, then it means that + // one or more of the links in the realpath couldn't be + // resolved. just return the abs value in that case. + p = self._makeAbs(p) + fs.realpath(p, self.realpathCache, function (er, real) { + if (!er) + set[real] = true + else if (er.syscall === 'stat') + set[p] = true + else + self.emit('error', er) // srsly wtf right here + + if (--n === 0) { + self.matches[index] = set + cb() + } + }) + }) +} + +Glob.prototype._mark = function (p) { + return common.mark(this, p) +} + +Glob.prototype._makeAbs = function (f) { + return common.makeAbs(this, f) +} + +Glob.prototype.abort = function () { + this.aborted = true + this.emit('abort') +} + +Glob.prototype.pause = function () { + if (!this.paused) { + this.paused = true + this.emit('pause') + } +} + +Glob.prototype.resume = function () { + if (this.paused) { + this.emit('resume') + this.paused = false + if (this._emitQueue.length) { + var eq = this._emitQueue.slice(0) + this._emitQueue.length = 0 + for (var i = 0; i < eq.length; i ++) { + var e = eq[i] + this._emitMatch(e[0], e[1]) + } + } + if (this._processQueue.length) { + var pq = this._processQueue.slice(0) + this._processQueue.length = 0 + for (var i = 0; i < pq.length; i ++) { + var p = pq[i] + this._processing-- + this._process(p[0], p[1], p[2], p[3]) + } + } + } +} + +Glob.prototype._process = function (pattern, index, inGlobStar, cb) { + assert(this instanceof Glob) + assert(typeof cb === 'function') + + if (this.aborted) + return + + this._processing++ + if (this.paused) { + this._processQueue.push([pattern, index, inGlobStar, cb]) + return + } + + //console.error('PROCESS %d', this._processing, pattern) + + // Get the first [n] parts of pattern that are all strings. + var n = 0 + while (typeof pattern[n] === 'string') { + n ++ + } + // now n is the index of the first one that is *not* a string. + + // see if there's anything else + var prefix + switch (n) { + // if not, then this is rather simple + case pattern.length: + this._processSimple(pattern.join('/'), index, cb) + return + + case 0: + // pattern *starts* with some non-trivial item. + // going to readdir(cwd), but not include the prefix in matches. + prefix = null + break + + default: + // pattern has some string bits in the front. + // whatever it starts with, whether that's 'absolute' like /foo/bar, + // or 'relative' like '../baz' + prefix = pattern.slice(0, n).join('/') + break + } + + var remain = pattern.slice(n) + + // get the list of entries. + var read + if (prefix === null) + read = '.' + else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { + if (!prefix || !isAbsolute(prefix)) + prefix = '/' + prefix + read = prefix + } else + read = prefix + + var abs = this._makeAbs(read) + + //if ignored, skip _processing + if (childrenIgnored(this, read)) + return cb() + + var isGlobStar = remain[0] === minimatch.GLOBSTAR + if (isGlobStar) + this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb) + else + this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb) +} + +Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) { + var self = this + this._readdir(abs, inGlobStar, function (er, entries) { + return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb) + }) +} + +Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { + + // if the abs isn't a dir, then nothing can match! + if (!entries) + return cb() + + // It will only match dot entries if it starts with a dot, or if + // dot is set. Stuff like @(.foo|.bar) isn't allowed. + var pn = remain[0] + var negate = !!this.minimatch.negate + var rawGlob = pn._glob + var dotOk = this.dot || rawGlob.charAt(0) === '.' + + var matchedEntries = [] + for (var i = 0; i < entries.length; i++) { + var e = entries[i] + if (e.charAt(0) !== '.' || dotOk) { + var m + if (negate && !prefix) { + m = !e.match(pn) + } else { + m = e.match(pn) + } + if (m) + matchedEntries.push(e) + } + } + + //console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries) + + var len = matchedEntries.length + // If there are no matched entries, then nothing matches. + if (len === 0) + return cb() + + // if this is the last remaining pattern bit, then no need for + // an additional stat *unless* the user has specified mark or + // stat explicitly. We know they exist, since readdir returned + // them. + + if (remain.length === 1 && !this.mark && !this.stat) { + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + if (prefix) { + if (prefix !== '/') + e = prefix + '/' + e + else + e = prefix + e + } + + if (e.charAt(0) === '/' && !this.nomount) { + e = path.join(this.root, e) + } + this._emitMatch(index, e) + } + // This was the last one, and no stats were needed + return cb() + } + + // now test all matched entries as stand-ins for that part + // of the pattern. + remain.shift() + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + var newPattern + if (prefix) { + if (prefix !== '/') + e = prefix + '/' + e + else + e = prefix + e + } + this._process([e].concat(remain), index, inGlobStar, cb) + } + cb() +} + +Glob.prototype._emitMatch = function (index, e) { + if (this.aborted) + return + + if (this.matches[index][e]) + return + + if (isIgnored(this, e)) + return + + if (this.paused) { + this._emitQueue.push([index, e]) + return + } + + var abs = this._makeAbs(e) + + if (this.nodir) { + var c = this.cache[abs] + if (c === 'DIR' || Array.isArray(c)) + return + } + + if (this.mark) + e = this._mark(e) + + this.matches[index][e] = true + + var st = this.statCache[abs] + if (st) + this.emit('stat', e, st) + + this.emit('match', e) +} + +Glob.prototype._readdirInGlobStar = function (abs, cb) { + if (this.aborted) + return + + // follow all symlinked directories forever + // just proceed as if this is a non-globstar situation + if (this.follow) + return this._readdir(abs, false, cb) + + var lstatkey = 'lstat\0' + abs + var self = this + var lstatcb = inflight(lstatkey, lstatcb_) + + if (lstatcb) + fs.lstat(abs, lstatcb) + + function lstatcb_ (er, lstat) { + if (er) + return cb() + + var isSym = lstat.isSymbolicLink() + self.symlinks[abs] = isSym + + // If it's not a symlink or a dir, then it's definitely a regular file. + // don't bother doing a readdir in that case. + if (!isSym && !lstat.isDirectory()) { + self.cache[abs] = 'FILE' + cb() + } else + self._readdir(abs, false, cb) + } +} + +Glob.prototype._readdir = function (abs, inGlobStar, cb) { + if (this.aborted) + return + + cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb) + if (!cb) + return + + //console.error('RD %j %j', +inGlobStar, abs) + if (inGlobStar && !ownProp(this.symlinks, abs)) + return this._readdirInGlobStar(abs, cb) + + if (ownProp(this.cache, abs)) { + var c = this.cache[abs] + if (!c || c === 'FILE') + return cb() + + if (Array.isArray(c)) + return cb(null, c) + } + + var self = this + fs.readdir(abs, readdirCb(this, abs, cb)) +} + +function readdirCb (self, abs, cb) { + return function (er, entries) { + if (er) + self._readdirError(abs, er, cb) + else + self._readdirEntries(abs, entries, cb) + } +} + +Glob.prototype._readdirEntries = function (abs, entries, cb) { + if (this.aborted) + return + + // if we haven't asked to stat everything, then just + // assume that everything in there exists, so we can avoid + // having to stat it a second time. + if (!this.mark && !this.stat) { + for (var i = 0; i < entries.length; i ++) { + var e = entries[i] + if (abs === '/') + e = abs + e + else + e = abs + '/' + e + this.cache[e] = true + } + } + + this.cache[abs] = entries + return cb(null, entries) +} + +Glob.prototype._readdirError = function (f, er, cb) { + if (this.aborted) + return + + // handle errors, and cache the information + switch (er.code) { + case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 + case 'ENOTDIR': // totally normal. means it *does* exist. + this.cache[this._makeAbs(f)] = 'FILE' + break + + case 'ENOENT': // not terribly unusual + case 'ELOOP': + case 'ENAMETOOLONG': + case 'UNKNOWN': + this.cache[this._makeAbs(f)] = false + break + + default: // some unusual error. Treat as failure. + this.cache[this._makeAbs(f)] = false + if (this.strict) { + this.emit('error', er) + // If the error is handled, then we abort + // if not, we threw out of here + this.abort() + } + if (!this.silent) + console.error('glob error', er) + break + } + + return cb() +} + +Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) { + var self = this + this._readdir(abs, inGlobStar, function (er, entries) { + self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb) + }) +} + + +Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { + //console.error('pgs2', prefix, remain[0], entries) + + // no entries means not a dir, so it can never have matches + // foo.txt/** doesn't match foo.txt + if (!entries) + return cb() + + // test without the globstar, and with every child both below + // and replacing the globstar. + var remainWithoutGlobStar = remain.slice(1) + var gspref = prefix ? [ prefix ] : [] + var noGlobStar = gspref.concat(remainWithoutGlobStar) + + // the noGlobStar pattern exits the inGlobStar state + this._process(noGlobStar, index, false, cb) + + var isSym = this.symlinks[abs] + var len = entries.length + + // If it's a symlink, and we're in a globstar, then stop + if (isSym && inGlobStar) + return cb() + + for (var i = 0; i < len; i++) { + var e = entries[i] + if (e.charAt(0) === '.' && !this.dot) + continue + + // these two cases enter the inGlobStar state + var instead = gspref.concat(entries[i], remainWithoutGlobStar) + this._process(instead, index, true, cb) + + var below = gspref.concat(entries[i], remain) + this._process(below, index, true, cb) + } + + cb() +} + +Glob.prototype._processSimple = function (prefix, index, cb) { + // XXX review this. Shouldn't it be doing the mounting etc + // before doing stat? kinda weird? + var self = this + this._stat(prefix, function (er, exists) { + self._processSimple2(prefix, index, er, exists, cb) + }) +} +Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) { + + //console.error('ps2', prefix, exists) + + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + // If it doesn't exist, then just mark the lack of results + if (!exists) + return cb() + + if (prefix && isAbsolute(prefix) && !this.nomount) { + var trail = /[\/\\]$/.test(prefix) + if (prefix.charAt(0) === '/') { + prefix = path.join(this.root, prefix) + } else { + prefix = path.resolve(this.root, prefix) + if (trail) + prefix += '/' + } + } + + if (process.platform === 'win32') + prefix = prefix.replace(/\\/g, '/') + + // Mark this as a match + this._emitMatch(index, prefix) + cb() +} + +// Returns either 'DIR', 'FILE', or false +Glob.prototype._stat = function (f, cb) { + var abs = this._makeAbs(f) + var needDir = f.slice(-1) === '/' + + if (f.length > this.maxLength) + return cb() + + if (!this.stat && ownProp(this.cache, abs)) { + var c = this.cache[abs] + + if (Array.isArray(c)) + c = 'DIR' + + // It exists, but maybe not how we need it + if (!needDir || c === 'DIR') + return cb(null, c) + + if (needDir && c === 'FILE') + return cb() + + // otherwise we have to stat, because maybe c=true + // if we know it exists, but not what it is. + } + + var exists + var stat = this.statCache[abs] + if (stat !== undefined) { + if (stat === false) + return cb(null, stat) + else { + var type = stat.isDirectory() ? 'DIR' : 'FILE' + if (needDir && type === 'FILE') + return cb() + else + return cb(null, type, stat) + } + } + + var self = this + var statcb = inflight('stat\0' + abs, lstatcb_) + if (statcb) + fs.lstat(abs, statcb) + + function lstatcb_ (er, lstat) { + if (lstat && lstat.isSymbolicLink()) { + // If it's a symlink, then treat it as the target, unless + // the target does not exist, then treat it as a file. + return fs.stat(abs, function (er, stat) { + if (er) + self._stat2(f, abs, null, lstat, cb) + else + self._stat2(f, abs, er, stat, cb) + }) + } else { + self._stat2(f, abs, er, lstat, cb) + } + } +} + +Glob.prototype._stat2 = function (f, abs, er, stat, cb) { + if (er) { + this.statCache[abs] = false + return cb() + } + + var needDir = f.slice(-1) === '/' + this.statCache[abs] = stat + + if (abs.slice(-1) === '/' && !stat.isDirectory()) + return cb(null, false, stat) + + var c = stat.isDirectory() ? 'DIR' : 'FILE' + this.cache[abs] = this.cache[abs] || c + + if (needDir && c !== 'DIR') + return cb() + + return cb(null, c, stat) +} + +}).call(this,require('_process')) +},{"./common.js":15,"./sync.js":17,"_process":24,"assert":9,"events":14,"fs":12,"inflight":18,"inherits":19,"minimatch":20,"once":21,"path":22,"path-is-absolute":23,"util":28}],17:[function(require,module,exports){ +(function (process){ +module.exports = globSync +globSync.GlobSync = GlobSync + +var fs = require('fs') +var minimatch = require('minimatch') +var Minimatch = minimatch.Minimatch +var Glob = require('./glob.js').Glob +var util = require('util') +var path = require('path') +var assert = require('assert') +var isAbsolute = require('path-is-absolute') +var common = require('./common.js') +var alphasort = common.alphasort +var alphasorti = common.alphasorti +var setopts = common.setopts +var ownProp = common.ownProp +var childrenIgnored = common.childrenIgnored + +function globSync (pattern, options) { + if (typeof options === 'function' || arguments.length === 3) + throw new TypeError('callback provided to sync glob\n'+ + 'See: https://github.com/isaacs/node-glob/issues/167') + + return new GlobSync(pattern, options).found +} + +function GlobSync (pattern, options) { + if (!pattern) + throw new Error('must provide pattern') + + if (typeof options === 'function' || arguments.length === 3) + throw new TypeError('callback provided to sync glob\n'+ + 'See: https://github.com/isaacs/node-glob/issues/167') + + if (!(this instanceof GlobSync)) + return new GlobSync(pattern, options) + + setopts(this, pattern, options) + + if (this.noprocess) + return this + + var n = this.minimatch.set.length + this.matches = new Array(n) + for (var i = 0; i < n; i ++) { + this._process(this.minimatch.set[i], i, false) + } + this._finish() +} + +GlobSync.prototype._finish = function () { + assert(this instanceof GlobSync) + if (this.realpath) { + var self = this + this.matches.forEach(function (matchset, index) { + var set = self.matches[index] = Object.create(null) + for (var p in matchset) { + try { + p = self._makeAbs(p) + var real = fs.realpathSync(p, self.realpathCache) + set[real] = true + } catch (er) { + if (er.syscall === 'stat') + set[self._makeAbs(p)] = true + else + throw er + } + } + }) + } + common.finish(this) +} + + +GlobSync.prototype._process = function (pattern, index, inGlobStar) { + assert(this instanceof GlobSync) + + // Get the first [n] parts of pattern that are all strings. + var n = 0 + while (typeof pattern[n] === 'string') { + n ++ + } + // now n is the index of the first one that is *not* a string. + + // See if there's anything else + var prefix + switch (n) { + // if not, then this is rather simple + case pattern.length: + this._processSimple(pattern.join('/'), index) + return + + case 0: + // pattern *starts* with some non-trivial item. + // going to readdir(cwd), but not include the prefix in matches. + prefix = null + break + + default: + // pattern has some string bits in the front. + // whatever it starts with, whether that's 'absolute' like /foo/bar, + // or 'relative' like '../baz' + prefix = pattern.slice(0, n).join('/') + break + } + + var remain = pattern.slice(n) + + // get the list of entries. + var read + if (prefix === null) + read = '.' + else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { + if (!prefix || !isAbsolute(prefix)) + prefix = '/' + prefix + read = prefix + } else + read = prefix + + var abs = this._makeAbs(read) + + //if ignored, skip processing + if (childrenIgnored(this, read)) + return + + var isGlobStar = remain[0] === minimatch.GLOBSTAR + if (isGlobStar) + this._processGlobStar(prefix, read, abs, remain, index, inGlobStar) + else + this._processReaddir(prefix, read, abs, remain, index, inGlobStar) +} + + +GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) { + var entries = this._readdir(abs, inGlobStar) + + // if the abs isn't a dir, then nothing can match! + if (!entries) + return + + // It will only match dot entries if it starts with a dot, or if + // dot is set. Stuff like @(.foo|.bar) isn't allowed. + var pn = remain[0] + var negate = !!this.minimatch.negate + var rawGlob = pn._glob + var dotOk = this.dot || rawGlob.charAt(0) === '.' + + var matchedEntries = [] + for (var i = 0; i < entries.length; i++) { + var e = entries[i] + if (e.charAt(0) !== '.' || dotOk) { + var m + if (negate && !prefix) { + m = !e.match(pn) + } else { + m = e.match(pn) + } + if (m) + matchedEntries.push(e) + } + } + + var len = matchedEntries.length + // If there are no matched entries, then nothing matches. + if (len === 0) + return + + // if this is the last remaining pattern bit, then no need for + // an additional stat *unless* the user has specified mark or + // stat explicitly. We know they exist, since readdir returned + // them. + + if (remain.length === 1 && !this.mark && !this.stat) { + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + if (prefix) { + if (prefix.slice(-1) !== '/') + e = prefix + '/' + e + else + e = prefix + e + } + + if (e.charAt(0) === '/' && !this.nomount) { + e = path.join(this.root, e) + } + this.matches[index][e] = true + } + // This was the last one, and no stats were needed + return + } + + // now test all matched entries as stand-ins for that part + // of the pattern. + remain.shift() + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + var newPattern + if (prefix) + newPattern = [prefix, e] + else + newPattern = [e] + this._process(newPattern.concat(remain), index, inGlobStar) + } +} + + +GlobSync.prototype._emitMatch = function (index, e) { + var abs = this._makeAbs(e) + if (this.mark) + e = this._mark(e) + + if (this.matches[index][e]) + return + + if (this.nodir) { + var c = this.cache[this._makeAbs(e)] + if (c === 'DIR' || Array.isArray(c)) + return + } + + this.matches[index][e] = true + if (this.stat) + this._stat(e) +} + + +GlobSync.prototype._readdirInGlobStar = function (abs) { + // follow all symlinked directories forever + // just proceed as if this is a non-globstar situation + if (this.follow) + return this._readdir(abs, false) + + var entries + var lstat + var stat + try { + lstat = fs.lstatSync(abs) + } catch (er) { + // lstat failed, doesn't exist + return null + } + + var isSym = lstat.isSymbolicLink() + this.symlinks[abs] = isSym + + // If it's not a symlink or a dir, then it's definitely a regular file. + // don't bother doing a readdir in that case. + if (!isSym && !lstat.isDirectory()) + this.cache[abs] = 'FILE' + else + entries = this._readdir(abs, false) + + return entries +} + +GlobSync.prototype._readdir = function (abs, inGlobStar) { + var entries + + if (inGlobStar && !ownProp(this.symlinks, abs)) + return this._readdirInGlobStar(abs) + + if (ownProp(this.cache, abs)) { + var c = this.cache[abs] + if (!c || c === 'FILE') + return null + + if (Array.isArray(c)) + return c + } + + try { + return this._readdirEntries(abs, fs.readdirSync(abs)) + } catch (er) { + this._readdirError(abs, er) + return null + } +} + +GlobSync.prototype._readdirEntries = function (abs, entries) { + // if we haven't asked to stat everything, then just + // assume that everything in there exists, so we can avoid + // having to stat it a second time. + if (!this.mark && !this.stat) { + for (var i = 0; i < entries.length; i ++) { + var e = entries[i] + if (abs === '/') + e = abs + e + else + e = abs + '/' + e + this.cache[e] = true + } + } + + this.cache[abs] = entries + + // mark and cache dir-ness + return entries +} + +GlobSync.prototype._readdirError = function (f, er) { + // handle errors, and cache the information + switch (er.code) { + case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 + case 'ENOTDIR': // totally normal. means it *does* exist. + this.cache[this._makeAbs(f)] = 'FILE' + break + + case 'ENOENT': // not terribly unusual + case 'ELOOP': + case 'ENAMETOOLONG': + case 'UNKNOWN': + this.cache[this._makeAbs(f)] = false + break + + default: // some unusual error. Treat as failure. + this.cache[this._makeAbs(f)] = false + if (this.strict) + throw er + if (!this.silent) + console.error('glob error', er) + break + } +} + +GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) { + + var entries = this._readdir(abs, inGlobStar) + + // no entries means not a dir, so it can never have matches + // foo.txt/** doesn't match foo.txt + if (!entries) + return + + // test without the globstar, and with every child both below + // and replacing the globstar. + var remainWithoutGlobStar = remain.slice(1) + var gspref = prefix ? [ prefix ] : [] + var noGlobStar = gspref.concat(remainWithoutGlobStar) + + // the noGlobStar pattern exits the inGlobStar state + this._process(noGlobStar, index, false) + + var len = entries.length + var isSym = this.symlinks[abs] + + // If it's a symlink, and we're in a globstar, then stop + if (isSym && inGlobStar) + return + + for (var i = 0; i < len; i++) { + var e = entries[i] + if (e.charAt(0) === '.' && !this.dot) + continue + + // these two cases enter the inGlobStar state + var instead = gspref.concat(entries[i], remainWithoutGlobStar) + this._process(instead, index, true) + + var below = gspref.concat(entries[i], remain) + this._process(below, index, true) + } +} + +GlobSync.prototype._processSimple = function (prefix, index) { + // XXX review this. Shouldn't it be doing the mounting etc + // before doing stat? kinda weird? + var exists = this._stat(prefix) + + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + // If it doesn't exist, then just mark the lack of results + if (!exists) + return + + if (prefix && isAbsolute(prefix) && !this.nomount) { + var trail = /[\/\\]$/.test(prefix) + if (prefix.charAt(0) === '/') { + prefix = path.join(this.root, prefix) + } else { + prefix = path.resolve(this.root, prefix) + if (trail) + prefix += '/' + } + } + + if (process.platform === 'win32') + prefix = prefix.replace(/\\/g, '/') + + // Mark this as a match + this.matches[index][prefix] = true +} + +// Returns either 'DIR', 'FILE', or false +GlobSync.prototype._stat = function (f) { + var abs = this._makeAbs(f) + var needDir = f.slice(-1) === '/' + + if (f.length > this.maxLength) + return false + + if (!this.stat && ownProp(this.cache, abs)) { + var c = this.cache[abs] + + if (Array.isArray(c)) + c = 'DIR' + + // It exists, but maybe not how we need it + if (!needDir || c === 'DIR') + return c + + if (needDir && c === 'FILE') + return false + + // otherwise we have to stat, because maybe c=true + // if we know it exists, but not what it is. + } + + var exists + var stat = this.statCache[abs] + if (!stat) { + var lstat + try { + lstat = fs.lstatSync(abs) + } catch (er) { + return false + } + + if (lstat.isSymbolicLink()) { + try { + stat = fs.statSync(abs) + } catch (er) { + stat = lstat + } + } else { + stat = lstat + } + } + + this.statCache[abs] = stat + + var c = stat.isDirectory() ? 'DIR' : 'FILE' + this.cache[abs] = this.cache[abs] || c + + if (needDir && c !== 'DIR') + return false + + return c +} + +GlobSync.prototype._mark = function (p) { + return common.mark(this, p) +} + +GlobSync.prototype._makeAbs = function (f) { + return common.makeAbs(this, f) +} + +}).call(this,require('_process')) +},{"./common.js":15,"./glob.js":16,"_process":24,"assert":9,"fs":12,"minimatch":20,"path":22,"path-is-absolute":23,"util":28}],18:[function(require,module,exports){ +(function (process){ +var wrappy = require('wrappy') +var reqs = Object.create(null) +var once = require('once') + +module.exports = wrappy(inflight) + +function inflight (key, cb) { + if (reqs[key]) { + reqs[key].push(cb) + return null + } else { + reqs[key] = [cb] + return makeres(key) + } +} + +function makeres (key) { + return once(function RES () { + var cbs = reqs[key] + var len = cbs.length + var args = slice(arguments) + + // XXX It's somewhat ambiguous whether a new callback added in this + // pass should be queued for later execution if something in the + // list of callbacks throws, or if it should just be discarded. + // However, it's such an edge case that it hardly matters, and either + // choice is likely as surprising as the other. + // As it happens, we do go ahead and schedule it for later execution. + try { + for (var i = 0; i < len; i++) { + cbs[i].apply(null, args) + } + } finally { + if (cbs.length > len) { + // added more in the interim. + // de-zalgo, just in case, but don't call again. + cbs.splice(0, len) + process.nextTick(function () { + RES.apply(null, args) + }) + } else { + delete reqs[key] + } + } + }) +} + +function slice (args) { + var length = args.length + var array = [] + + for (var i = 0; i < length; i++) array[i] = args[i] + return array +} + +}).call(this,require('_process')) +},{"_process":24,"once":21,"wrappy":29}],19:[function(require,module,exports){ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} + +},{}],20:[function(require,module,exports){ +module.exports = minimatch +minimatch.Minimatch = Minimatch + +var path = { sep: '/' } +try { + path = require('path') +} catch (er) {} + +var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {} +var expand = require('brace-expansion') + +var plTypes = { + '!': { open: '(?:(?!(?:', close: '))[^/]*?)'}, + '?': { open: '(?:', close: ')?' }, + '+': { open: '(?:', close: ')+' }, + '*': { open: '(?:', close: ')*' }, + '@': { open: '(?:', close: ')' } +} + +// any single thing other than / +// don't need to escape / when using new RegExp() +var qmark = '[^/]' + +// * => any number of characters +var star = qmark + '*?' + +// ** when dots are allowed. Anything goes, except .. and . +// not (^ or / followed by one or two dots followed by $ or /), +// followed by anything, any number of times. +var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?' + +// not a ^ or / followed by a dot, +// followed by anything, any number of times. +var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?' + +// characters that need to be escaped in RegExp. +var reSpecials = charSet('().*{}+?[]^$\\!') + +// "abc" -> { a:true, b:true, c:true } +function charSet (s) { + return s.split('').reduce(function (set, c) { + set[c] = true + return set + }, {}) +} + +// normalizes slashes. +var slashSplit = /\/+/ + +minimatch.filter = filter +function filter (pattern, options) { + options = options || {} + return function (p, i, list) { + return minimatch(p, pattern, options) + } +} + +function ext (a, b) { + a = a || {} + b = b || {} + var t = {} + Object.keys(b).forEach(function (k) { + t[k] = b[k] + }) + Object.keys(a).forEach(function (k) { + t[k] = a[k] + }) + return t +} + +minimatch.defaults = function (def) { + if (!def || !Object.keys(def).length) return minimatch + + var orig = minimatch + + var m = function minimatch (p, pattern, options) { + return orig.minimatch(p, pattern, ext(def, options)) + } + + m.Minimatch = function Minimatch (pattern, options) { + return new orig.Minimatch(pattern, ext(def, options)) + } + + return m +} + +Minimatch.defaults = function (def) { + if (!def || !Object.keys(def).length) return Minimatch + return minimatch.defaults(def).Minimatch +} + +function minimatch (p, pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('glob pattern string required') + } + + if (!options) options = {} + + // shortcut: comments match nothing. + if (!options.nocomment && pattern.charAt(0) === '#') { + return false + } + + // "" only matches "" + if (pattern.trim() === '') return p === '' + + return new Minimatch(pattern, options).match(p) +} + +function Minimatch (pattern, options) { + if (!(this instanceof Minimatch)) { + return new Minimatch(pattern, options) + } + + if (typeof pattern !== 'string') { + throw new TypeError('glob pattern string required') + } + + if (!options) options = {} + pattern = pattern.trim() + + // windows support: need to use /, not \ + if (path.sep !== '/') { + pattern = pattern.split(path.sep).join('/') + } + + this.options = options + this.set = [] + this.pattern = pattern + this.regexp = null + this.negate = false + this.comment = false + this.empty = false + + // make the set of regexps etc. + this.make() +} + +Minimatch.prototype.debug = function () {} + +Minimatch.prototype.make = make +function make () { + // don't do it more than once. + if (this._made) return + + var pattern = this.pattern + var options = this.options + + // empty patterns and comments match nothing. + if (!options.nocomment && pattern.charAt(0) === '#') { + this.comment = true + return + } + if (!pattern) { + this.empty = true + return + } + + // step 1: figure out negation, etc. + this.parseNegate() + + // step 2: expand braces + var set = this.globSet = this.braceExpand() + + if (options.debug) this.debug = console.error + + this.debug(this.pattern, set) + + // step 3: now we have a set, so turn each one into a series of path-portion + // matching patterns. + // These will be regexps, except in the case of "**", which is + // set to the GLOBSTAR object for globstar behavior, + // and will not contain any / characters + set = this.globParts = set.map(function (s) { + return s.split(slashSplit) + }) + + this.debug(this.pattern, set) + + // glob --> regexps + set = set.map(function (s, si, set) { + return s.map(this.parse, this) + }, this) + + this.debug(this.pattern, set) + + // filter out everything that didn't compile properly. + set = set.filter(function (s) { + return s.indexOf(false) === -1 + }) + + this.debug(this.pattern, set) + + this.set = set +} + +Minimatch.prototype.parseNegate = parseNegate +function parseNegate () { + var pattern = this.pattern + var negate = false + var options = this.options + var negateOffset = 0 + + if (options.nonegate) return + + for (var i = 0, l = pattern.length + ; i < l && pattern.charAt(i) === '!' + ; i++) { + negate = !negate + negateOffset++ + } + + if (negateOffset) this.pattern = pattern.substr(negateOffset) + this.negate = negate +} + +// Brace expansion: +// a{b,c}d -> abd acd +// a{b,}c -> abc ac +// a{0..3}d -> a0d a1d a2d a3d +// a{b,c{d,e}f}g -> abg acdfg acefg +// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg +// +// Invalid sets are not expanded. +// a{2..}b -> a{2..}b +// a{b}c -> a{b}c +minimatch.braceExpand = function (pattern, options) { + return braceExpand(pattern, options) +} + +Minimatch.prototype.braceExpand = braceExpand + +function braceExpand (pattern, options) { + if (!options) { + if (this instanceof Minimatch) { + options = this.options + } else { + options = {} + } + } + + pattern = typeof pattern === 'undefined' + ? this.pattern : pattern + + if (typeof pattern === 'undefined') { + throw new TypeError('undefined pattern') + } + + if (options.nobrace || + !pattern.match(/\{.*\}/)) { + // shortcut. no need to expand. + return [pattern] + } + + return expand(pattern) +} + +// parse a component of the expanded set. +// At this point, no pattern may contain "/" in it +// so we're going to return a 2d array, where each entry is the full +// pattern, split on '/', and then turned into a regular expression. +// A regexp is made at the end which joins each array with an +// escaped /, and another full one which joins each regexp with |. +// +// Following the lead of Bash 4.1, note that "**" only has special meaning +// when it is the *only* thing in a path portion. Otherwise, any series +// of * is equivalent to a single *. Globstar behavior is enabled by +// default, and can be disabled by setting options.noglobstar. +Minimatch.prototype.parse = parse +var SUBPARSE = {} +function parse (pattern, isSub) { + if (pattern.length > 1024 * 64) { + throw new TypeError('pattern is too long') + } + + var options = this.options + + // shortcuts + if (!options.noglobstar && pattern === '**') return GLOBSTAR + if (pattern === '') return '' + + var re = '' + var hasMagic = !!options.nocase + var escaping = false + // ? => one single character + var patternListStack = [] + var negativeLists = [] + var stateChar + var inClass = false + var reClassStart = -1 + var classStart = -1 + // . and .. never match anything that doesn't start with ., + // even when options.dot is set. + var patternStart = pattern.charAt(0) === '.' ? '' // anything + // not (start or / followed by . or .. followed by / or end) + : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))' + : '(?!\\.)' + var self = this + + function clearStateChar () { + if (stateChar) { + // we had some state-tracking character + // that wasn't consumed by this pass. + switch (stateChar) { + case '*': + re += star + hasMagic = true + break + case '?': + re += qmark + hasMagic = true + break + default: + re += '\\' + stateChar + break + } + self.debug('clearStateChar %j %j', stateChar, re) + stateChar = false + } + } + + for (var i = 0, len = pattern.length, c + ; (i < len) && (c = pattern.charAt(i)) + ; i++) { + this.debug('%s\t%s %s %j', pattern, i, re, c) + + // skip over any that are escaped. + if (escaping && reSpecials[c]) { + re += '\\' + c + escaping = false + continue + } + + switch (c) { + case '/': + // completely not allowed, even escaped. + // Should already be path-split by now. + return false + + case '\\': + clearStateChar() + escaping = true + continue + + // the various stateChar values + // for the "extglob" stuff. + case '?': + case '*': + case '+': + case '@': + case '!': + this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c) + + // all of those are literals inside a class, except that + // the glob [!a] means [^a] in regexp + if (inClass) { + this.debug(' in class') + if (c === '!' && i === classStart + 1) c = '^' + re += c + continue + } + + // if we already have a stateChar, then it means + // that there was something like ** or +? in there. + // Handle the stateChar, then proceed with this one. + self.debug('call clearStateChar %j', stateChar) + clearStateChar() + stateChar = c + // if extglob is disabled, then +(asdf|foo) isn't a thing. + // just clear the statechar *now*, rather than even diving into + // the patternList stuff. + if (options.noext) clearStateChar() + continue + + case '(': + if (inClass) { + re += '(' + continue + } + + if (!stateChar) { + re += '\\(' + continue + } + + patternListStack.push({ + type: stateChar, + start: i - 1, + reStart: re.length, + open: plTypes[stateChar].open, + close: plTypes[stateChar].close + }) + // negation is (?:(?!js)[^/]*) + re += stateChar === '!' ? '(?:(?!(?:' : '(?:' + this.debug('plType %j %j', stateChar, re) + stateChar = false + continue + + case ')': + if (inClass || !patternListStack.length) { + re += '\\)' + continue + } + + clearStateChar() + hasMagic = true + var pl = patternListStack.pop() + // negation is (?:(?!js)[^/]*) + // The others are (?:) + re += pl.close + if (pl.type === '!') { + negativeLists.push(pl) + } + pl.reEnd = re.length + continue + + case '|': + if (inClass || !patternListStack.length || escaping) { + re += '\\|' + escaping = false + continue + } + + clearStateChar() + re += '|' + continue + + // these are mostly the same in regexp and glob + case '[': + // swallow any state-tracking char before the [ + clearStateChar() + + if (inClass) { + re += '\\' + c + continue + } + + inClass = true + classStart = i + reClassStart = re.length + re += c + continue + + case ']': + // a right bracket shall lose its special + // meaning and represent itself in + // a bracket expression if it occurs + // first in the list. -- POSIX.2 2.8.3.2 + if (i === classStart + 1 || !inClass) { + re += '\\' + c + escaping = false + continue + } + + // handle the case where we left a class open. + // "[z-a]" is valid, equivalent to "\[z-a\]" + if (inClass) { + // split where the last [ was, make sure we don't have + // an invalid re. if so, re-walk the contents of the + // would-be class to re-translate any characters that + // were passed through as-is + // TODO: It would probably be faster to determine this + // without a try/catch and a new RegExp, but it's tricky + // to do safely. For now, this is safe and works. + var cs = pattern.substring(classStart + 1, i) + try { + RegExp('[' + cs + ']') + } catch (er) { + // not a valid class! + var sp = this.parse(cs, SUBPARSE) + re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]' + hasMagic = hasMagic || sp[1] + inClass = false + continue + } + } + + // finish up the class. + hasMagic = true + inClass = false + re += c + continue + + default: + // swallow any state char that wasn't consumed + clearStateChar() + + if (escaping) { + // no need + escaping = false + } else if (reSpecials[c] + && !(c === '^' && inClass)) { + re += '\\' + } + + re += c + + } // switch + } // for + + // handle the case where we left a class open. + // "[abc" is valid, equivalent to "\[abc" + if (inClass) { + // split where the last [ was, and escape it + // this is a huge pita. We now have to re-walk + // the contents of the would-be class to re-translate + // any characters that were passed through as-is + cs = pattern.substr(classStart + 1) + sp = this.parse(cs, SUBPARSE) + re = re.substr(0, reClassStart) + '\\[' + sp[0] + hasMagic = hasMagic || sp[1] + } + + // handle the case where we had a +( thing at the *end* + // of the pattern. + // each pattern list stack adds 3 chars, and we need to go through + // and escape any | chars that were passed through as-is for the regexp. + // Go through and escape them, taking care not to double-escape any + // | chars that were already escaped. + for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) { + var tail = re.slice(pl.reStart + pl.open.length) + this.debug('setting tail', re, pl) + // maybe some even number of \, then maybe 1 \, followed by a | + tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, function (_, $1, $2) { + if (!$2) { + // the | isn't already escaped, so escape it. + $2 = '\\' + } + + // need to escape all those slashes *again*, without escaping the + // one that we need for escaping the | character. As it works out, + // escaping an even number of slashes can be done by simply repeating + // it exactly after itself. That's why this trick works. + // + // I am sorry that you have to see this. + return $1 + $1 + $2 + '|' + }) + + this.debug('tail=%j\n %s', tail, tail, pl, re) + var t = pl.type === '*' ? star + : pl.type === '?' ? qmark + : '\\' + pl.type + + hasMagic = true + re = re.slice(0, pl.reStart) + t + '\\(' + tail + } + + // handle trailing things that only matter at the very end. + clearStateChar() + if (escaping) { + // trailing \\ + re += '\\\\' + } + + // only need to apply the nodot start if the re starts with + // something that could conceivably capture a dot + var addPatternStart = false + switch (re.charAt(0)) { + case '.': + case '[': + case '(': addPatternStart = true + } + + // Hack to work around lack of negative lookbehind in JS + // A pattern like: *.!(x).!(y|z) needs to ensure that a name + // like 'a.xyz.yz' doesn't match. So, the first negative + // lookahead, has to look ALL the way ahead, to the end of + // the pattern. + for (var n = negativeLists.length - 1; n > -1; n--) { + var nl = negativeLists[n] + + var nlBefore = re.slice(0, nl.reStart) + var nlFirst = re.slice(nl.reStart, nl.reEnd - 8) + var nlLast = re.slice(nl.reEnd - 8, nl.reEnd) + var nlAfter = re.slice(nl.reEnd) + + nlLast += nlAfter + + // Handle nested stuff like *(*.js|!(*.json)), where open parens + // mean that we should *not* include the ) in the bit that is considered + // "after" the negated section. + var openParensBefore = nlBefore.split('(').length - 1 + var cleanAfter = nlAfter + for (i = 0; i < openParensBefore; i++) { + cleanAfter = cleanAfter.replace(/\)[+*?]?/, '') + } + nlAfter = cleanAfter + + var dollar = '' + if (nlAfter === '' && isSub !== SUBPARSE) { + dollar = '$' + } + var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast + re = newRe + } + + // if the re is not "" at this point, then we need to make sure + // it doesn't match against an empty path part. + // Otherwise a/* will match a/, which it should not. + if (re !== '' && hasMagic) { + re = '(?=.)' + re + } + + if (addPatternStart) { + re = patternStart + re + } + + // parsing just a piece of a larger pattern. + if (isSub === SUBPARSE) { + return [re, hasMagic] + } + + // skip the regexp for non-magical patterns + // unescape anything in it, though, so that it'll be + // an exact match against a file etc. + if (!hasMagic) { + return globUnescape(pattern) + } + + var flags = options.nocase ? 'i' : '' + try { + var regExp = new RegExp('^' + re + '$', flags) + } catch (er) { + // If it was an invalid regular expression, then it can't match + // anything. This trick looks for a character after the end of + // the string, which is of course impossible, except in multi-line + // mode, but it's not a /m regex. + return new RegExp('$.') + } + + regExp._glob = pattern + regExp._src = re + + return regExp +} + +minimatch.makeRe = function (pattern, options) { + return new Minimatch(pattern, options || {}).makeRe() +} + +Minimatch.prototype.makeRe = makeRe +function makeRe () { + if (this.regexp || this.regexp === false) return this.regexp + + // at this point, this.set is a 2d array of partial + // pattern strings, or "**". + // + // It's better to use .match(). This function shouldn't + // be used, really, but it's pretty convenient sometimes, + // when you just want to work with a regex. + var set = this.set + + if (!set.length) { + this.regexp = false + return this.regexp + } + var options = this.options + + var twoStar = options.noglobstar ? star + : options.dot ? twoStarDot + : twoStarNoDot + var flags = options.nocase ? 'i' : '' + + var re = set.map(function (pattern) { + return pattern.map(function (p) { + return (p === GLOBSTAR) ? twoStar + : (typeof p === 'string') ? regExpEscape(p) + : p._src + }).join('\\\/') + }).join('|') + + // must match entire pattern + // ending in a * or ** will make it less strict. + re = '^(?:' + re + ')$' + + // can match anything, as long as it's not this. + if (this.negate) re = '^(?!' + re + ').*$' + + try { + this.regexp = new RegExp(re, flags) + } catch (ex) { + this.regexp = false + } + return this.regexp +} + +minimatch.match = function (list, pattern, options) { + options = options || {} + var mm = new Minimatch(pattern, options) + list = list.filter(function (f) { + return mm.match(f) + }) + if (mm.options.nonull && !list.length) { + list.push(pattern) + } + return list +} + +Minimatch.prototype.match = match +function match (f, partial) { + this.debug('match', f, this.pattern) + // short-circuit in the case of busted things. + // comments, etc. + if (this.comment) return false + if (this.empty) return f === '' + + if (f === '/' && partial) return true + + var options = this.options + + // windows: need to use /, not \ + if (path.sep !== '/') { + f = f.split(path.sep).join('/') + } + + // treat the test path as a set of pathparts. + f = f.split(slashSplit) + this.debug(this.pattern, 'split', f) + + // just ONE of the pattern sets in this.set needs to match + // in order for it to be valid. If negating, then just one + // match means that we have failed. + // Either way, return on the first hit. + + var set = this.set + this.debug(this.pattern, 'set', set) + + // Find the basename of the path by looking for the last non-empty segment + var filename + var i + for (i = f.length - 1; i >= 0; i--) { + filename = f[i] + if (filename) break + } + + for (i = 0; i < set.length; i++) { + var pattern = set[i] + var file = f + if (options.matchBase && pattern.length === 1) { + file = [filename] + } + var hit = this.matchOne(file, pattern, partial) + if (hit) { + if (options.flipNegate) return true + return !this.negate + } + } + + // didn't get any hits. this is success if it's a negative + // pattern, failure otherwise. + if (options.flipNegate) return false + return this.negate +} + +// set partial to true to test if, for example, +// "/a/b" matches the start of "/*/b/*/d" +// Partial means, if you run out of file before you run +// out of pattern, then that's fine, as long as all +// the parts match. +Minimatch.prototype.matchOne = function (file, pattern, partial) { + var options = this.options + + this.debug('matchOne', + { 'this': this, file: file, pattern: pattern }) + + this.debug('matchOne', file.length, pattern.length) + + for (var fi = 0, + pi = 0, + fl = file.length, + pl = pattern.length + ; (fi < fl) && (pi < pl) + ; fi++, pi++) { + this.debug('matchOne loop') + var p = pattern[pi] + var f = file[fi] + + this.debug(pattern, p, f) + + // should be impossible. + // some invalid regexp stuff in the set. + if (p === false) return false + + if (p === GLOBSTAR) { + this.debug('GLOBSTAR', [pattern, p, f]) + + // "**" + // a/**/b/**/c would match the following: + // a/b/x/y/z/c + // a/x/y/z/b/c + // a/b/x/b/x/c + // a/b/c + // To do this, take the rest of the pattern after + // the **, and see if it would match the file remainder. + // If so, return success. + // If not, the ** "swallows" a segment, and try again. + // This is recursively awful. + // + // a/**/b/**/c matching a/b/x/y/z/c + // - a matches a + // - doublestar + // - matchOne(b/x/y/z/c, b/**/c) + // - b matches b + // - doublestar + // - matchOne(x/y/z/c, c) -> no + // - matchOne(y/z/c, c) -> no + // - matchOne(z/c, c) -> no + // - matchOne(c, c) yes, hit + var fr = fi + var pr = pi + 1 + if (pr === pl) { + this.debug('** at the end') + // a ** at the end will just swallow the rest. + // We have found a match. + // however, it will not swallow /.x, unless + // options.dot is set. + // . and .. are *never* matched by **, for explosively + // exponential reasons. + for (; fi < fl; fi++) { + if (file[fi] === '.' || file[fi] === '..' || + (!options.dot && file[fi].charAt(0) === '.')) return false + } + return true + } + + // ok, let's see if we can swallow whatever we can. + while (fr < fl) { + var swallowee = file[fr] + + this.debug('\nglobstar while', file, fr, pattern, pr, swallowee) + + // XXX remove this slice. Just pass the start index. + if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { + this.debug('globstar found match!', fr, fl, swallowee) + // found a match. + return true + } else { + // can't swallow "." or ".." ever. + // can only swallow ".foo" when explicitly asked. + if (swallowee === '.' || swallowee === '..' || + (!options.dot && swallowee.charAt(0) === '.')) { + this.debug('dot detected!', file, fr, pattern, pr) + break + } + + // ** swallows a segment, and continue. + this.debug('globstar swallow a segment, and continue') + fr++ + } + } + + // no match was found. + // However, in partial mode, we can't say this is necessarily over. + // If there's more *pattern* left, then + if (partial) { + // ran out of file + this.debug('\n>>> no match, partial?', file, fr, pattern, pr) + if (fr === fl) return true + } + return false + } + + // something other than ** + // non-magic patterns just have to match exactly + // patterns with magic have been turned into regexps. + var hit + if (typeof p === 'string') { + if (options.nocase) { + hit = f.toLowerCase() === p.toLowerCase() + } else { + hit = f === p + } + this.debug('string match', p, f, hit) + } else { + hit = f.match(p) + this.debug('pattern match', p, f, hit) + } + + if (!hit) return false + } + + // Note: ending in / means that we'll get a final "" + // at the end of the pattern. This can only match a + // corresponding "" at the end of the file. + // If the file ends in /, then it can only match a + // a pattern that ends in /, unless the pattern just + // doesn't have any more for it. But, a/b/ should *not* + // match "a/b/*", even though "" matches against the + // [^/]*? pattern, except in partial mode, where it might + // simply not be reached yet. + // However, a/b/ should still satisfy a/* + + // now either we fell off the end of the pattern, or we're done. + if (fi === fl && pi === pl) { + // ran out of pattern and filename at the same time. + // an exact hit! + return true + } else if (fi === fl) { + // ran out of file, but still had pattern left. + // this is ok if we're doing the match as part of + // a glob fs traversal. + return partial + } else if (pi === pl) { + // ran out of pattern, still have file left. + // this is only acceptable if we're on the very last + // empty segment of a file with a trailing slash. + // a/* should match a/b/ + var emptyFileEnd = (fi === fl - 1) && (file[fi] === '') + return emptyFileEnd + } + + // should be unreachable. + throw new Error('wtf?') +} + +// replace stuff like \* with * +function globUnescape (s) { + return s.replace(/\\(.)/g, '$1') +} + +function regExpEscape (s) { + return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') +} + +},{"brace-expansion":11,"path":22}],21:[function(require,module,exports){ +var wrappy = require('wrappy') +module.exports = wrappy(once) +module.exports.strict = wrappy(onceStrict) + +once.proto = once(function () { + Object.defineProperty(Function.prototype, 'once', { + value: function () { + return once(this) + }, + configurable: true + }) + + Object.defineProperty(Function.prototype, 'onceStrict', { + value: function () { + return onceStrict(this) + }, + configurable: true + }) +}) + +function once (fn) { + var f = function () { + if (f.called) return f.value + f.called = true + return f.value = fn.apply(this, arguments) + } + f.called = false + return f +} + +function onceStrict (fn) { + var f = function () { + if (f.called) + throw new Error(f.onceError) + f.called = true + return f.value = fn.apply(this, arguments) + } + var name = fn.name || 'Function wrapped with `once`' + f.onceError = name + " shouldn't be called more than once" + f.called = false + return f +} + +},{"wrappy":29}],22:[function(require,module,exports){ +(function (process){ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +// resolves . and .. elements in a path array with directory names there +// must be no slashes, empty elements, or device names (c:\) in the array +// (so also no leading and trailing slashes - it does not distinguish +// relative and absolute paths) +function normalizeArray(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last === '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); + } + } + + return parts; +} + +// Split a filename into [root, dir, basename, ext], unix version +// 'root' is just a slash, or nothing. +var splitPathRe = + /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; +var splitPath = function(filename) { + return splitPathRe.exec(filename).slice(1); +}; + +// path.resolve([from ...], to) +// posix version +exports.resolve = function() { + var resolvedPath = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + var path = (i >= 0) ? arguments[i] : process.cwd(); + + // Skip empty and invalid entries + if (typeof path !== 'string') { + throw new TypeError('Arguments to path.resolve must be strings'); + } else if (!path) { + continue; + } + + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; +}; + +// path.normalize(path) +// posix version +exports.normalize = function(path) { + var isAbsolute = exports.isAbsolute(path), + trailingSlash = substr(path, -1) === '/'; + + // Normalize the path + path = normalizeArray(filter(path.split('/'), function(p) { + return !!p; + }), !isAbsolute).join('/'); + + if (!path && !isAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + + return (isAbsolute ? '/' : '') + path; +}; + +// posix version +exports.isAbsolute = function(path) { + return path.charAt(0) === '/'; +}; + +// posix version +exports.join = function() { + var paths = Array.prototype.slice.call(arguments, 0); + return exports.normalize(filter(paths, function(p, index) { + if (typeof p !== 'string') { + throw new TypeError('Arguments to path.join must be strings'); + } + return p; + }).join('/')); +}; + + +// path.relative(from, to) +// posix version +exports.relative = function(from, to) { + from = exports.resolve(from).substr(1); + to = exports.resolve(to).substr(1); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); + + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('/'); +}; + +exports.sep = '/'; +exports.delimiter = ':'; + +exports.dirname = function(path) { + var result = splitPath(path), + root = result[0], + dir = result[1]; + + if (!root && !dir) { + // No dirname whatsoever + return '.'; + } + + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substr(0, dir.length - 1); + } + + return root + dir; +}; + + +exports.basename = function(path, ext) { + var f = splitPath(path)[2]; + // TODO: make this comparison case-insensitive on windows? + if (ext && f.substr(-1 * ext.length) === ext) { + f = f.substr(0, f.length - ext.length); + } + return f; +}; + + +exports.extname = function(path) { + return splitPath(path)[3]; +}; + +function filter (xs, f) { + if (xs.filter) return xs.filter(f); + var res = []; + for (var i = 0; i < xs.length; i++) { + if (f(xs[i], i, xs)) res.push(xs[i]); + } + return res; +} + +// String.prototype.substr - negative index don't work in IE8 +var substr = 'ab'.substr(-1) === 'b' + ? function (str, start, len) { return str.substr(start, len) } + : function (str, start, len) { + if (start < 0) start = str.length + start; + return str.substr(start, len); + } +; + +}).call(this,require('_process')) +},{"_process":24}],23:[function(require,module,exports){ +(function (process){ +'use strict'; + +function posix(path) { + return path.charAt(0) === '/'; +} + +function win32(path) { + // https://github.com/nodejs/node/blob/b3fcc245fb25539909ef1d5eaa01dbf92e168633/lib/path.js#L56 + var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; + var result = splitDeviceRe.exec(path); + var device = result[1] || ''; + var isUnc = Boolean(device && device.charAt(1) !== ':'); + + // UNC paths are always absolute + return Boolean(result[2] || isUnc); +} + +module.exports = process.platform === 'win32' ? win32 : posix; +module.exports.posix = posix; +module.exports.win32 = win32; + +}).call(this,require('_process')) +},{"_process":24}],24:[function(require,module,exports){ +// shim for using process in browser +var process = module.exports = {}; + +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. + +var cachedSetTimeout; +var cachedClearTimeout; + +function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); +} +function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); +} +(function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } +} ()) +function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + +} +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; +process.prependListener = noop; +process.prependOnceListener = noop; + +process.listeners = function (name) { return [] } + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],25:[function(require,module,exports){ +// Underscore.js 1.8.3 +// http://underscorejs.org +// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// Underscore may be freely distributed under the MIT license. + +(function() { + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `exports` on the server. + var root = this; + + // Save the previous value of the `_` variable. + var previousUnderscore = root._; + + // Save bytes in the minified (but not gzipped) version: + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + + // Create quick reference variables for speed access to core prototypes. + var + push = ArrayProto.push, + slice = ArrayProto.slice, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + + // All **ECMAScript 5** native function implementations that we hope to use + // are declared here. + var + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind, + nativeCreate = Object.create; + + // Naked function reference for surrogate-prototype-swapping. + var Ctor = function(){}; + + // Create a safe reference to the Underscore object for use below. + var _ = function(obj) { + if (obj instanceof _) return obj; + if (!(this instanceof _)) return new _(obj); + this._wrapped = obj; + }; + + // Export the Underscore object for **Node.js**, with + // backwards-compatibility for the old `require()` API. If we're in + // the browser, add `_` as a global object. + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = _; + } + exports._ = _; + } else { + root._ = _; + } + + // Current version. + _.VERSION = '1.8.3'; + + // Internal function that returns an efficient (for current engines) version + // of the passed-in callback, to be repeatedly applied in other Underscore + // functions. + var optimizeCb = function(func, context, argCount) { + if (context === void 0) return func; + switch (argCount == null ? 3 : argCount) { + case 1: return function(value) { + return func.call(context, value); + }; + case 2: return function(value, other) { + return func.call(context, value, other); + }; + case 3: return function(value, index, collection) { + return func.call(context, value, index, collection); + }; + case 4: return function(accumulator, value, index, collection) { + return func.call(context, accumulator, value, index, collection); + }; + } + return function() { + return func.apply(context, arguments); + }; + }; + + // A mostly-internal function to generate callbacks that can be applied + // to each element in a collection, returning the desired result — either + // identity, an arbitrary callback, a property matcher, or a property accessor. + var cb = function(value, context, argCount) { + if (value == null) return _.identity; + if (_.isFunction(value)) return optimizeCb(value, context, argCount); + if (_.isObject(value)) return _.matcher(value); + return _.property(value); + }; + _.iteratee = function(value, context) { + return cb(value, context, Infinity); + }; + + // An internal function for creating assigner functions. + var createAssigner = function(keysFunc, undefinedOnly) { + return function(obj) { + var length = arguments.length; + if (length < 2 || obj == null) return obj; + for (var index = 1; index < length; index++) { + var source = arguments[index], + keys = keysFunc(source), + l = keys.length; + for (var i = 0; i < l; i++) { + var key = keys[i]; + if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key]; + } + } + return obj; + }; + }; + + // An internal function for creating a new object that inherits from another. + var baseCreate = function(prototype) { + if (!_.isObject(prototype)) return {}; + if (nativeCreate) return nativeCreate(prototype); + Ctor.prototype = prototype; + var result = new Ctor; + Ctor.prototype = null; + return result; + }; + + var property = function(key) { + return function(obj) { + return obj == null ? void 0 : obj[key]; + }; + }; + + // Helper for collection methods to determine whether a collection + // should be iterated as an array or as an object + // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength + // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094 + var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; + var getLength = property('length'); + var isArrayLike = function(collection) { + var length = getLength(collection); + return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX; + }; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles raw objects in addition to array-likes. Treats all + // sparse array-likes as if they were dense. + _.each = _.forEach = function(obj, iteratee, context) { + iteratee = optimizeCb(iteratee, context); + var i, length; + if (isArrayLike(obj)) { + for (i = 0, length = obj.length; i < length; i++) { + iteratee(obj[i], i, obj); + } + } else { + var keys = _.keys(obj); + for (i = 0, length = keys.length; i < length; i++) { + iteratee(obj[keys[i]], keys[i], obj); + } + } + return obj; + }; + + // Return the results of applying the iteratee to each element. + _.map = _.collect = function(obj, iteratee, context) { + iteratee = cb(iteratee, context); + var keys = !isArrayLike(obj) && _.keys(obj), + length = (keys || obj).length, + results = Array(length); + for (var index = 0; index < length; index++) { + var currentKey = keys ? keys[index] : index; + results[index] = iteratee(obj[currentKey], currentKey, obj); + } + return results; + }; + + // Create a reducing function iterating left or right. + function createReduce(dir) { + // Optimized iterator function as using arguments.length + // in the main function will deoptimize the, see #1991. + function iterator(obj, iteratee, memo, keys, index, length) { + for (; index >= 0 && index < length; index += dir) { + var currentKey = keys ? keys[index] : index; + memo = iteratee(memo, obj[currentKey], currentKey, obj); + } + return memo; + } + + return function(obj, iteratee, memo, context) { + iteratee = optimizeCb(iteratee, context, 4); + var keys = !isArrayLike(obj) && _.keys(obj), + length = (keys || obj).length, + index = dir > 0 ? 0 : length - 1; + // Determine the initial value if none is provided. + if (arguments.length < 3) { + memo = obj[keys ? keys[index] : index]; + index += dir; + } + return iterator(obj, iteratee, memo, keys, index, length); + }; + } + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. + _.reduce = _.foldl = _.inject = createReduce(1); + + // The right-associative version of reduce, also known as `foldr`. + _.reduceRight = _.foldr = createReduce(-1); + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, predicate, context) { + var key; + if (isArrayLike(obj)) { + key = _.findIndex(obj, predicate, context); + } else { + key = _.findKey(obj, predicate, context); + } + if (key !== void 0 && key !== -1) return obj[key]; + }; + + // Return all the elements that pass a truth test. + // Aliased as `select`. + _.filter = _.select = function(obj, predicate, context) { + var results = []; + predicate = cb(predicate, context); + _.each(obj, function(value, index, list) { + if (predicate(value, index, list)) results.push(value); + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, predicate, context) { + return _.filter(obj, _.negate(cb(predicate)), context); + }; + + // Determine whether all of the elements match a truth test. + // Aliased as `all`. + _.every = _.all = function(obj, predicate, context) { + predicate = cb(predicate, context); + var keys = !isArrayLike(obj) && _.keys(obj), + length = (keys || obj).length; + for (var index = 0; index < length; index++) { + var currentKey = keys ? keys[index] : index; + if (!predicate(obj[currentKey], currentKey, obj)) return false; + } + return true; + }; + + // Determine if at least one element in the object matches a truth test. + // Aliased as `any`. + _.some = _.any = function(obj, predicate, context) { + predicate = cb(predicate, context); + var keys = !isArrayLike(obj) && _.keys(obj), + length = (keys || obj).length; + for (var index = 0; index < length; index++) { + var currentKey = keys ? keys[index] : index; + if (predicate(obj[currentKey], currentKey, obj)) return true; + } + return false; + }; + + // Determine if the array or object contains a given item (using `===`). + // Aliased as `includes` and `include`. + _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) { + if (!isArrayLike(obj)) obj = _.values(obj); + if (typeof fromIndex != 'number' || guard) fromIndex = 0; + return _.indexOf(obj, item, fromIndex) >= 0; + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + var isFunc = _.isFunction(method); + return _.map(obj, function(value) { + var func = isFunc ? method : value[method]; + return func == null ? func : func.apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, _.property(key)); + }; + + // Convenience version of a common use case of `filter`: selecting only objects + // containing specific `key:value` pairs. + _.where = function(obj, attrs) { + return _.filter(obj, _.matcher(attrs)); + }; + + // Convenience version of a common use case of `find`: getting the first object + // containing specific `key:value` pairs. + _.findWhere = function(obj, attrs) { + return _.find(obj, _.matcher(attrs)); + }; + + // Return the maximum element (or element-based computation). + _.max = function(obj, iteratee, context) { + var result = -Infinity, lastComputed = -Infinity, + value, computed; + if (iteratee == null && obj != null) { + obj = isArrayLike(obj) ? obj : _.values(obj); + for (var i = 0, length = obj.length; i < length; i++) { + value = obj[i]; + if (value > result) { + result = value; + } + } + } else { + iteratee = cb(iteratee, context); + _.each(obj, function(value, index, list) { + computed = iteratee(value, index, list); + if (computed > lastComputed || computed === -Infinity && result === -Infinity) { + result = value; + lastComputed = computed; + } + }); + } + return result; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iteratee, context) { + var result = Infinity, lastComputed = Infinity, + value, computed; + if (iteratee == null && obj != null) { + obj = isArrayLike(obj) ? obj : _.values(obj); + for (var i = 0, length = obj.length; i < length; i++) { + value = obj[i]; + if (value < result) { + result = value; + } + } + } else { + iteratee = cb(iteratee, context); + _.each(obj, function(value, index, list) { + computed = iteratee(value, index, list); + if (computed < lastComputed || computed === Infinity && result === Infinity) { + result = value; + lastComputed = computed; + } + }); + } + return result; + }; + + // Shuffle a collection, using the modern version of the + // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). + _.shuffle = function(obj) { + var set = isArrayLike(obj) ? obj : _.values(obj); + var length = set.length; + var shuffled = Array(length); + for (var index = 0, rand; index < length; index++) { + rand = _.random(0, index); + if (rand !== index) shuffled[index] = shuffled[rand]; + shuffled[rand] = set[index]; + } + return shuffled; + }; + + // Sample **n** random values from a collection. + // If **n** is not specified, returns a single random element. + // The internal `guard` argument allows it to work with `map`. + _.sample = function(obj, n, guard) { + if (n == null || guard) { + if (!isArrayLike(obj)) obj = _.values(obj); + return obj[_.random(obj.length - 1)]; + } + return _.shuffle(obj).slice(0, Math.max(0, n)); + }; + + // Sort the object's values by a criterion produced by an iteratee. + _.sortBy = function(obj, iteratee, context) { + iteratee = cb(iteratee, context); + return _.pluck(_.map(obj, function(value, index, list) { + return { + value: value, + index: index, + criteria: iteratee(value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria; + var b = right.criteria; + if (a !== b) { + if (a > b || a === void 0) return 1; + if (a < b || b === void 0) return -1; + } + return left.index - right.index; + }), 'value'); + }; + + // An internal function used for aggregate "group by" operations. + var group = function(behavior) { + return function(obj, iteratee, context) { + var result = {}; + iteratee = cb(iteratee, context); + _.each(obj, function(value, index) { + var key = iteratee(value, index, obj); + behavior(result, value, key); + }); + return result; + }; + }; + + // Groups the object's values by a criterion. Pass either a string attribute + // to group by, or a function that returns the criterion. + _.groupBy = group(function(result, value, key) { + if (_.has(result, key)) result[key].push(value); else result[key] = [value]; + }); + + // Indexes the object's values by a criterion, similar to `groupBy`, but for + // when you know that your index values will be unique. + _.indexBy = group(function(result, value, key) { + result[key] = value; + }); + + // Counts instances of an object that group by a certain criterion. Pass + // either a string attribute to count by, or a function that returns the + // criterion. + _.countBy = group(function(result, value, key) { + if (_.has(result, key)) result[key]++; else result[key] = 1; + }); + + // Safely create a real, live array from anything iterable. + _.toArray = function(obj) { + if (!obj) return []; + if (_.isArray(obj)) return slice.call(obj); + if (isArrayLike(obj)) return _.map(obj, _.identity); + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + if (obj == null) return 0; + return isArrayLike(obj) ? obj.length : _.keys(obj).length; + }; + + // Split a collection into two arrays: one whose elements all satisfy the given + // predicate, and one whose elements all do not satisfy the predicate. + _.partition = function(obj, predicate, context) { + predicate = cb(predicate, context); + var pass = [], fail = []; + _.each(obj, function(value, key, obj) { + (predicate(value, key, obj) ? pass : fail).push(value); + }); + return [pass, fail]; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { + if (array == null) return void 0; + if (n == null || guard) return array[0]; + return _.initial(array, array.length - n); + }; + + // Returns everything but the last entry of the array. Especially useful on + // the arguments object. Passing **n** will return all the values in + // the array, excluding the last N. + _.initial = function(array, n, guard) { + return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n))); + }; + + // Get the last element of an array. Passing **n** will return the last N + // values in the array. + _.last = function(array, n, guard) { + if (array == null) return void 0; + if (n == null || guard) return array[array.length - 1]; + return _.rest(array, Math.max(0, array.length - n)); + }; + + // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. + // Especially useful on the arguments object. Passing an **n** will return + // the rest N values in the array. + _.rest = _.tail = _.drop = function(array, n, guard) { + return slice.call(array, n == null || guard ? 1 : n); + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, _.identity); + }; + + // Internal implementation of a recursive `flatten` function. + var flatten = function(input, shallow, strict, startIndex) { + var output = [], idx = 0; + for (var i = startIndex || 0, length = getLength(input); i < length; i++) { + var value = input[i]; + if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) { + //flatten current level of array or arguments object + if (!shallow) value = flatten(value, shallow, strict); + var j = 0, len = value.length; + output.length += len; + while (j < len) { + output[idx++] = value[j++]; + } + } else if (!strict) { + output[idx++] = value; + } + } + return output; + }; + + // Flatten out an array, either recursively (by default), or just one level. + _.flatten = function(array, shallow) { + return flatten(array, shallow, false); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted, iteratee, context) { + if (!_.isBoolean(isSorted)) { + context = iteratee; + iteratee = isSorted; + isSorted = false; + } + if (iteratee != null) iteratee = cb(iteratee, context); + var result = []; + var seen = []; + for (var i = 0, length = getLength(array); i < length; i++) { + var value = array[i], + computed = iteratee ? iteratee(value, i, array) : value; + if (isSorted) { + if (!i || seen !== computed) result.push(value); + seen = computed; + } else if (iteratee) { + if (!_.contains(seen, computed)) { + seen.push(computed); + result.push(value); + } + } else if (!_.contains(result, value)) { + result.push(value); + } + } + return result; + }; + + // Produce an array that contains the union: each distinct element from all of + // the passed-in arrays. + _.union = function() { + return _.uniq(flatten(arguments, true, true)); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersection = function(array) { + var result = []; + var argsLength = arguments.length; + for (var i = 0, length = getLength(array); i < length; i++) { + var item = array[i]; + if (_.contains(result, item)) continue; + for (var j = 1; j < argsLength; j++) { + if (!_.contains(arguments[j], item)) break; + } + if (j === argsLength) result.push(item); + } + return result; + }; + + // Take the difference between one array and a number of other arrays. + // Only the elements present in just the first array will remain. + _.difference = function(array) { + var rest = flatten(arguments, true, true, 1); + return _.filter(array, function(value){ + return !_.contains(rest, value); + }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function() { + return _.unzip(arguments); + }; + + // Complement of _.zip. Unzip accepts an array of arrays and groups + // each array's elements on shared indices + _.unzip = function(array) { + var length = array && _.max(array, getLength).length || 0; + var result = Array(length); + + for (var index = 0; index < length; index++) { + result[index] = _.pluck(array, index); + } + return result; + }; + + // Converts lists into objects. Pass either a single array of `[key, value]` + // pairs, or two parallel arrays of the same length -- one of keys, and one of + // the corresponding values. + _.object = function(list, values) { + var result = {}; + for (var i = 0, length = getLength(list); i < length; i++) { + if (values) { + result[list[i]] = values[i]; + } else { + result[list[i][0]] = list[i][1]; + } + } + return result; + }; + + // Generator function to create the findIndex and findLastIndex functions + function createPredicateIndexFinder(dir) { + return function(array, predicate, context) { + predicate = cb(predicate, context); + var length = getLength(array); + var index = dir > 0 ? 0 : length - 1; + for (; index >= 0 && index < length; index += dir) { + if (predicate(array[index], index, array)) return index; + } + return -1; + }; + } + + // Returns the first index on an array-like that passes a predicate test + _.findIndex = createPredicateIndexFinder(1); + _.findLastIndex = createPredicateIndexFinder(-1); + + // Use a comparator function to figure out the smallest index at which + // an object should be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iteratee, context) { + iteratee = cb(iteratee, context, 1); + var value = iteratee(obj); + var low = 0, high = getLength(array); + while (low < high) { + var mid = Math.floor((low + high) / 2); + if (iteratee(array[mid]) < value) low = mid + 1; else high = mid; + } + return low; + }; + + // Generator function to create the indexOf and lastIndexOf functions + function createIndexFinder(dir, predicateFind, sortedIndex) { + return function(array, item, idx) { + var i = 0, length = getLength(array); + if (typeof idx == 'number') { + if (dir > 0) { + i = idx >= 0 ? idx : Math.max(idx + length, i); + } else { + length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1; + } + } else if (sortedIndex && idx && length) { + idx = sortedIndex(array, item); + return array[idx] === item ? idx : -1; + } + if (item !== item) { + idx = predicateFind(slice.call(array, i, length), _.isNaN); + return idx >= 0 ? idx + i : -1; + } + for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) { + if (array[idx] === item) return idx; + } + return -1; + }; + } + + // Return the position of the first occurrence of an item in an array, + // or -1 if the item is not included in the array. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex); + _.lastIndexOf = createIndexFinder(-1, _.findLastIndex); + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (stop == null) { + stop = start || 0; + start = 0; + } + step = step || 1; + + var length = Math.max(Math.ceil((stop - start) / step), 0); + var range = Array(length); + + for (var idx = 0; idx < length; idx++, start += step) { + range[idx] = start; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Determines whether to execute a function as a constructor + // or a normal function with the provided arguments + var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) { + if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args); + var self = baseCreate(sourceFunc.prototype); + var result = sourceFunc.apply(self, args); + if (_.isObject(result)) return result; + return self; + }; + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if + // available. + _.bind = function(func, context) { + if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); + var args = slice.call(arguments, 2); + var bound = function() { + return executeBound(func, bound, context, this, args.concat(slice.call(arguments))); + }; + return bound; + }; + + // Partially apply a function by creating a version that has had some of its + // arguments pre-filled, without changing its dynamic `this` context. _ acts + // as a placeholder, allowing any combination of arguments to be pre-filled. + _.partial = function(func) { + var boundArgs = slice.call(arguments, 1); + var bound = function() { + var position = 0, length = boundArgs.length; + var args = Array(length); + for (var i = 0; i < length; i++) { + args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i]; + } + while (position < arguments.length) args.push(arguments[position++]); + return executeBound(func, bound, this, this, args); + }; + return bound; + }; + + // Bind a number of an object's methods to that object. Remaining arguments + // are the method names to be bound. Useful for ensuring that all callbacks + // defined on an object belong to it. + _.bindAll = function(obj) { + var i, length = arguments.length, key; + if (length <= 1) throw new Error('bindAll must be passed function names'); + for (i = 1; i < length; i++) { + key = arguments[i]; + obj[key] = _.bind(obj[key], obj); + } + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memoize = function(key) { + var cache = memoize.cache; + var address = '' + (hasher ? hasher.apply(this, arguments) : key); + if (!_.has(cache, address)) cache[address] = func.apply(this, arguments); + return cache[address]; + }; + memoize.cache = {}; + return memoize; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ + return func.apply(null, args); + }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = _.partial(_.delay, _, 1); + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. Normally, the throttled function will run + // as much as it can, without ever going more than once per `wait` duration; + // but if you'd like to disable the execution on the leading edge, pass + // `{leading: false}`. To disable execution on the trailing edge, ditto. + _.throttle = function(func, wait, options) { + var context, args, result; + var timeout = null; + var previous = 0; + if (!options) options = {}; + var later = function() { + previous = options.leading === false ? 0 : _.now(); + timeout = null; + result = func.apply(context, args); + if (!timeout) context = args = null; + }; + return function() { + var now = _.now(); + if (!previous && options.leading === false) previous = now; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0 || remaining > wait) { + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + previous = now; + result = func.apply(context, args); + if (!timeout) context = args = null; + } else if (!timeout && options.trailing !== false) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout, args, context, timestamp, result; + + var later = function() { + var last = _.now() - timestamp; + + if (last < wait && last >= 0) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + if (!timeout) context = args = null; + } + } + }; + + return function() { + context = this; + args = arguments; + timestamp = _.now(); + var callNow = immediate && !timeout; + if (!timeout) timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + context = args = null; + } + + return result; + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return _.partial(wrapper, func); + }; + + // Returns a negated version of the passed-in predicate. + _.negate = function(predicate) { + return function() { + return !predicate.apply(this, arguments); + }; + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var args = arguments; + var start = args.length - 1; + return function() { + var i = start; + var result = args[start].apply(this, arguments); + while (i--) result = args[i].call(this, result); + return result; + }; + }; + + // Returns a function that will only be executed on and after the Nth call. + _.after = function(times, func) { + return function() { + if (--times < 1) { + return func.apply(this, arguments); + } + }; + }; + + // Returns a function that will only be executed up to (but not including) the Nth call. + _.before = function(times, func) { + var memo; + return function() { + if (--times > 0) { + memo = func.apply(this, arguments); + } + if (times <= 1) func = null; + return memo; + }; + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = _.partial(_.before, 2); + + // Object Functions + // ---------------- + + // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed. + var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString'); + var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString', + 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString']; + + function collectNonEnumProps(obj, keys) { + var nonEnumIdx = nonEnumerableProps.length; + var constructor = obj.constructor; + var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto; + + // Constructor is a special case. + var prop = 'constructor'; + if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop); + + while (nonEnumIdx--) { + prop = nonEnumerableProps[nonEnumIdx]; + if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) { + keys.push(prop); + } + } + } + + // Retrieve the names of an object's own properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = function(obj) { + if (!_.isObject(obj)) return []; + if (nativeKeys) return nativeKeys(obj); + var keys = []; + for (var key in obj) if (_.has(obj, key)) keys.push(key); + // Ahem, IE < 9. + if (hasEnumBug) collectNonEnumProps(obj, keys); + return keys; + }; + + // Retrieve all the property names of an object. + _.allKeys = function(obj) { + if (!_.isObject(obj)) return []; + var keys = []; + for (var key in obj) keys.push(key); + // Ahem, IE < 9. + if (hasEnumBug) collectNonEnumProps(obj, keys); + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + var keys = _.keys(obj); + var length = keys.length; + var values = Array(length); + for (var i = 0; i < length; i++) { + values[i] = obj[keys[i]]; + } + return values; + }; + + // Returns the results of applying the iteratee to each element of the object + // In contrast to _.map it returns an object + _.mapObject = function(obj, iteratee, context) { + iteratee = cb(iteratee, context); + var keys = _.keys(obj), + length = keys.length, + results = {}, + currentKey; + for (var index = 0; index < length; index++) { + currentKey = keys[index]; + results[currentKey] = iteratee(obj[currentKey], currentKey, obj); + } + return results; + }; + + // Convert an object into a list of `[key, value]` pairs. + _.pairs = function(obj) { + var keys = _.keys(obj); + var length = keys.length; + var pairs = Array(length); + for (var i = 0; i < length; i++) { + pairs[i] = [keys[i], obj[keys[i]]]; + } + return pairs; + }; + + // Invert the keys and values of an object. The values must be serializable. + _.invert = function(obj) { + var result = {}; + var keys = _.keys(obj); + for (var i = 0, length = keys.length; i < length; i++) { + result[obj[keys[i]]] = keys[i]; + } + return result; + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) names.push(key); + } + return names.sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = createAssigner(_.allKeys); + + // Assigns a given object with all the own properties in the passed-in object(s) + // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) + _.extendOwn = _.assign = createAssigner(_.keys); + + // Returns the first key on an object that passes a predicate test + _.findKey = function(obj, predicate, context) { + predicate = cb(predicate, context); + var keys = _.keys(obj), key; + for (var i = 0, length = keys.length; i < length; i++) { + key = keys[i]; + if (predicate(obj[key], key, obj)) return key; + } + }; + + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(object, oiteratee, context) { + var result = {}, obj = object, iteratee, keys; + if (obj == null) return result; + if (_.isFunction(oiteratee)) { + keys = _.allKeys(obj); + iteratee = optimizeCb(oiteratee, context); + } else { + keys = flatten(arguments, false, false, 1); + iteratee = function(value, key, obj) { return key in obj; }; + obj = Object(obj); + } + for (var i = 0, length = keys.length; i < length; i++) { + var key = keys[i]; + var value = obj[key]; + if (iteratee(value, key, obj)) result[key] = value; + } + return result; + }; + + // Return a copy of the object without the blacklisted properties. + _.omit = function(obj, iteratee, context) { + if (_.isFunction(iteratee)) { + iteratee = _.negate(iteratee); + } else { + var keys = _.map(flatten(arguments, false, false, 1), String); + iteratee = function(value, key) { + return !_.contains(keys, key); + }; + } + return _.pick(obj, iteratee, context); + }; + + // Fill in a given object with default properties. + _.defaults = createAssigner(_.allKeys, true); + + // Creates an object that inherits from the given prototype object. + // If additional properties are provided then they will be added to the + // created object. + _.create = function(prototype, props) { + var result = baseCreate(prototype); + if (props) _.extendOwn(result, props); + return result; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + if (!_.isObject(obj)) return obj; + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Returns whether an object has a given set of `key:value` pairs. + _.isMatch = function(object, attrs) { + var keys = _.keys(attrs), length = keys.length; + if (object == null) return !length; + var obj = Object(object); + for (var i = 0; i < length; i++) { + var key = keys[i]; + if (attrs[key] !== obj[key] || !(key in obj)) return false; + } + return true; + }; + + + // Internal recursive comparison function for `isEqual`. + var eq = function(a, b, aStack, bStack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). + if (a === b) return a !== 0 || 1 / a === 1 / b; + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) return a === b; + // Unwrap any wrapped objects. + if (a instanceof _) a = a._wrapped; + if (b instanceof _) b = b._wrapped; + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className !== toString.call(b)) return false; + switch (className) { + // Strings, numbers, regular expressions, dates, and booleans are compared by value. + case '[object RegExp]': + // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i') + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return '' + a === '' + b; + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. + // Object(NaN) is equivalent to NaN + if (+a !== +a) return +b !== +b; + // An `egal` comparison is performed for other numeric values. + return +a === 0 ? 1 / +a === 1 / b : +a === +b; + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a === +b; + } + + var areArrays = className === '[object Array]'; + if (!areArrays) { + if (typeof a != 'object' || typeof b != 'object') return false; + + // Objects with different constructors are not equivalent, but `Object`s or `Array`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor && + _.isFunction(bCtor) && bCtor instanceof bCtor) + && ('constructor' in a && 'constructor' in b)) { + return false; + } + } + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + + // Initializing stack of traversed objects. + // It's done here since we only need them for objects and arrays comparison. + aStack = aStack || []; + bStack = bStack || []; + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] === a) return bStack[length] === b; + } + + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + + // Recursively compare objects and arrays. + if (areArrays) { + // Compare array lengths to determine if a deep comparison is necessary. + length = a.length; + if (length !== b.length) return false; + // Deep compare the contents, ignoring non-numeric properties. + while (length--) { + if (!eq(a[length], b[length], aStack, bStack)) return false; + } + } else { + // Deep compare objects. + var keys = _.keys(a), key; + length = keys.length; + // Ensure that both objects contain the same number of properties before comparing deep equality. + if (_.keys(b).length !== length) return false; + while (length--) { + // Deep compare each member + key = keys[length]; + if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return true; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + return eq(a, b); + }; + + // Is a given array, string, or object empty? + // An "empty" object has no enumerable own-properties. + _.isEmpty = function(obj) { + if (obj == null) return true; + if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0; + return _.keys(obj).length === 0; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType === 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) === '[object Array]'; + }; + + // Is a given variable an object? + _.isObject = function(obj) { + var type = typeof obj; + return type === 'function' || type === 'object' && !!obj; + }; + + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError. + _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) { + _['is' + name] = function(obj) { + return toString.call(obj) === '[object ' + name + ']'; + }; + }); + + // Define a fallback version of the method in browsers (ahem, IE < 9), where + // there isn't any inspectable "Arguments" type. + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return _.has(obj, 'callee'); + }; + } + + // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8, + // IE 11 (#1621), and in Safari 8 (#1929). + if (typeof /./ != 'function' && typeof Int8Array != 'object') { + _.isFunction = function(obj) { + return typeof obj == 'function' || false; + }; + } + + // Is a given object a finite number? + _.isFinite = function(obj) { + return isFinite(obj) && !isNaN(parseFloat(obj)); + }; + + // Is the given value `NaN`? (NaN is the only number which does not equal itself). + _.isNaN = function(obj) { + return _.isNumber(obj) && obj !== +obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Shortcut function for checking if an object has a given property directly + // on itself (in other words, not on a prototype). + _.has = function(obj, key) { + return obj != null && hasOwnProperty.call(obj, key); + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iteratees. + _.identity = function(value) { + return value; + }; + + // Predicate-generating functions. Often useful outside of Underscore. + _.constant = function(value) { + return function() { + return value; + }; + }; + + _.noop = function(){}; + + _.property = property; + + // Generates a function for a given object that returns a given property. + _.propertyOf = function(obj) { + return obj == null ? function(){} : function(key) { + return obj[key]; + }; + }; + + // Returns a predicate for checking whether an object has a given set of + // `key:value` pairs. + _.matcher = _.matches = function(attrs) { + attrs = _.extendOwn({}, attrs); + return function(obj) { + return _.isMatch(obj, attrs); + }; + }; + + // Run a function **n** times. + _.times = function(n, iteratee, context) { + var accum = Array(Math.max(0, n)); + iteratee = optimizeCb(iteratee, context, 1); + for (var i = 0; i < n; i++) accum[i] = iteratee(i); + return accum; + }; + + // Return a random integer between min and max (inclusive). + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + Math.floor(Math.random() * (max - min + 1)); + }; + + // A (possibly faster) way to get the current timestamp as an integer. + _.now = Date.now || function() { + return new Date().getTime(); + }; + + // List of HTML entities for escaping. + var escapeMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`' + }; + var unescapeMap = _.invert(escapeMap); + + // Functions for escaping and unescaping strings to/from HTML interpolation. + var createEscaper = function(map) { + var escaper = function(match) { + return map[match]; + }; + // Regexes for identifying a key that needs to be escaped + var source = '(?:' + _.keys(map).join('|') + ')'; + var testRegexp = RegExp(source); + var replaceRegexp = RegExp(source, 'g'); + return function(string) { + string = string == null ? '' : '' + string; + return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; + }; + }; + _.escape = createEscaper(escapeMap); + _.unescape = createEscaper(unescapeMap); + + // If the value of the named `property` is a function then invoke it with the + // `object` as context; otherwise, return it. + _.result = function(object, property, fallback) { + var value = object == null ? void 0 : object[property]; + if (value === void 0) { + value = fallback; + } + return _.isFunction(value) ? value.call(object) : value; + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = ++idCounter + ''; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\u2028|\u2029/g; + + var escapeChar = function(match) { + return '\\' + escapes[match]; + }; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + // NB: `oldSettings` only exists for backwards compatibility. + _.template = function(text, settings, oldSettings) { + if (!settings && oldSettings) settings = oldSettings; + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset).replace(escaper, escapeChar); + index = offset + match.length; + + if (escape) { + source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; + } else if (interpolate) { + source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; + } else if (evaluate) { + source += "';\n" + evaluate + "\n__p+='"; + } + + // Adobe VMs need the match returned to produce the correct offest. + return match; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + 'return __p;\n'; + + try { + var render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled source as a convenience for precompilation. + var argument = settings.variable || 'obj'; + template.source = 'function(' + argument + '){\n' + source + '}'; + + return template; + }; + + // Add a "chain" function. Start chaining a wrapped Underscore object. + _.chain = function(obj) { + var instance = _(obj); + instance._chain = true; + return instance; + }; + + // OOP + // --------------- + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + + // Helper function to continue chaining intermediate results. + var result = function(instance, obj) { + return instance._chain ? _(obj).chain() : obj; + }; + + // Add your own custom functions to the Underscore object. + _.mixin = function(obj) { + _.each(_.functions(obj), function(name) { + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return result(this, func.apply(_, args)); + }; + }); + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + var obj = this._wrapped; + method.apply(obj, arguments); + if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; + return result(this, obj); + }; + }); + + // Add all accessor Array functions to the wrapper. + _.each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + return result(this, method.apply(this._wrapped, arguments)); + }; + }); + + // Extracts the result from a wrapped and chained object. + _.prototype.value = function() { + return this._wrapped; + }; + + // Provide unwrapping proxy for some methods used in engine operations + // such as arithmetic and JSON stringification. + _.prototype.valueOf = _.prototype.toJSON = _.prototype.value; + + _.prototype.toString = function() { + return '' + this._wrapped; + }; + + // AMD registration happens at the end for compatibility with AMD loaders + // that may not enforce next-turn semantics on modules. Even though general + // practice for AMD registration is to be anonymous, underscore registers + // as a named module because, like jQuery, it is a base library that is + // popular enough to be bundled in a third party lib, but not be part of + // an AMD load request. Those cases could generate an error when an + // anonymous define() is called outside of a loader request. + if (typeof define === 'function' && define.amd) { + define('underscore', [], function() { + return _; + }); + } +}.call(this)); + +},{}],26:[function(require,module,exports){ +arguments[4][19][0].apply(exports,arguments) +},{"dup":19}],27:[function(require,module,exports){ +module.exports = function isBuffer(arg) { + return arg && typeof arg === 'object' + && typeof arg.copy === 'function' + && typeof arg.fill === 'function' + && typeof arg.readUInt8 === 'function'; +} +},{}],28:[function(require,module,exports){ +(function (process,global){ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +var formatRegExp = /%[sdj%]/g; +exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; +}; + + +// Mark that a method should not be used. +// Returns a modified function which warns once by default. +// If --no-deprecation is set, then it is a no-op. +exports.deprecate = function(fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function() { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +}; + + +var debugs = {}; +var debugEnviron; +exports.debuglog = function(set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; + } + } + return debugs[set]; +}; + + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ +/* legacy: obj, showHidden, depth, colors*/ +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); +} +exports.inspect = inspect; + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; + +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + + +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } +} + + +function stylizeNoColor(str, styleType) { + return str; +} + + +function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = require('./support/isBuffer'); + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +} + + +var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + +// 26 Feb 16:19:34 +function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); +} + + +// log is just a thin wrapper to console.log that prepends a timestamp +exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ +exports.inherits = require('inherits'); + +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +}; + +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./support/isBuffer":27,"_process":24,"inherits":26}],29:[function(require,module,exports){ +// Returns a wrapper function that returns a wrapped callback +// The wrapper function should do some stuff, and return a +// presumably different callback function. +// This makes sure that own properties are retained, so that +// decorations and such are not lost along the way. +module.exports = wrappy +function wrappy (fn, cb) { + if (fn && cb) return wrappy(fn)(cb) + + if (typeof fn !== 'function') + throw new TypeError('need wrapper function') + + Object.keys(fn).forEach(function (k) { + wrapper[k] = fn[k] + }) + + return wrapper + + function wrapper() { + var args = new Array(arguments.length) + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i] + } + var ret = fn.apply(this, args) + var cb = args[args.length-1] + if (typeof ret === 'function' && ret !== cb) { + Object.keys(cb).forEach(function (k) { + ret[k] = cb[k] + }) + } + return ret + } +} + +},{}]},{},[7])(7) +}); \ No newline at end of file diff --git a/assets/javascripts/workers/search.c7c1ca2c.min.js b/assets/javascripts/workers/search.c7c1ca2c.min.js new file mode 100644 index 0000000000..2d6f767082 --- /dev/null +++ b/assets/javascripts/workers/search.c7c1ca2c.min.js @@ -0,0 +1,2 @@ +"use strict";(()=>{var xe=Object.create;var G=Object.defineProperty,ve=Object.defineProperties,Se=Object.getOwnPropertyDescriptor,Te=Object.getOwnPropertyDescriptors,Qe=Object.getOwnPropertyNames,J=Object.getOwnPropertySymbols,Ee=Object.getPrototypeOf,Z=Object.prototype.hasOwnProperty,be=Object.prototype.propertyIsEnumerable;var K=Math.pow,X=(t,e,r)=>e in t?G(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,_=(t,e)=>{for(var r in e||(e={}))Z.call(e,r)&&X(t,r,e[r]);if(J)for(var r of J(e))be.call(e,r)&&X(t,r,e[r]);return t},B=(t,e)=>ve(t,Te(e));var Le=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var we=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Qe(e))!Z.call(t,i)&&i!==r&&G(t,i,{get:()=>e[i],enumerable:!(n=Se(e,i))||n.enumerable});return t};var Pe=(t,e,r)=>(r=t!=null?xe(Ee(t)):{},we(e||!t||!t.__esModule?G(r,"default",{value:t,enumerable:!0}):r,t));var W=(t,e,r)=>new Promise((n,i)=>{var s=u=>{try{a(r.next(u))}catch(c){i(c)}},o=u=>{try{a(r.throw(u))}catch(c){i(c)}},a=u=>u.done?n(u.value):Promise.resolve(u.value).then(s,o);a((r=r.apply(t,e)).next())});var re=Le((ee,te)=>{(function(){var t=function(e){var r=new t.Builder;return r.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),r.searchPipeline.add(t.stemmer),e.call(r,r),r.build()};t.version="2.3.9";t.utils={},t.utils.warn=function(e){return function(r){e.console&&console.warn&&console.warn(r)}}(this),t.utils.asString=function(e){return e==null?"":e.toString()},t.utils.clone=function(e){if(e==null)return e;for(var r=Object.create(null),n=Object.keys(e),i=0;i0){var f=t.utils.clone(r)||{};f.position=[a,c],f.index=s.length,s.push(new t.Token(n.slice(a,o),f))}a=o+1}}return s},t.tokenizer.separator=/[\s\-]+/;t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions=Object.create(null),t.Pipeline.registerFunction=function(e,r){r in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+r),e.label=r,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var r=e.label&&e.label in this.registeredFunctions;r||t.utils.warn(`Function is not registered with pipeline. This may cause problems when serialising the index. +`,e)},t.Pipeline.load=function(e){var r=new t.Pipeline;return e.forEach(function(n){var i=t.Pipeline.registeredFunctions[n];if(i)r.add(i);else throw new Error("Cannot load unregistered function: "+n)}),r},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(r){t.Pipeline.warnIfFunctionNotRegistered(r),this._stack.push(r)},this)},t.Pipeline.prototype.after=function(e,r){t.Pipeline.warnIfFunctionNotRegistered(r);var n=this._stack.indexOf(e);if(n==-1)throw new Error("Cannot find existingFn");n=n+1,this._stack.splice(n,0,r)},t.Pipeline.prototype.before=function(e,r){t.Pipeline.warnIfFunctionNotRegistered(r);var n=this._stack.indexOf(e);if(n==-1)throw new Error("Cannot find existingFn");this._stack.splice(n,0,r)},t.Pipeline.prototype.remove=function(e){var r=this._stack.indexOf(e);r!=-1&&this._stack.splice(r,1)},t.Pipeline.prototype.run=function(e){for(var r=this._stack.length,n=0;n1&&(oe&&(n=s),o!=e);)i=n-r,s=r+Math.floor(i/2),o=this.elements[s*2];if(o==e||o>e)return s*2;if(ou?f+=2:a==u&&(r+=n[c+1]*i[f+1],c+=2,f+=2);return r},t.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},t.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),r=1,n=0;r0){var o=s.str.charAt(0),a;o in s.node.edges?a=s.node.edges[o]:(a=new t.TokenSet,s.node.edges[o]=a),s.str.length==1&&(a.final=!0),i.push({node:a,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(s.editsRemaining!=0){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new t.TokenSet;s.node.edges["*"]=u}if(s.str.length==0&&(u.final=!0),i.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&i.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),s.str.length==1&&(s.node.final=!0),s.str.length>=1){if("*"in s.node.edges)var c=s.node.edges["*"];else{var c=new t.TokenSet;s.node.edges["*"]=c}s.str.length==1&&(c.final=!0),i.push({node:c,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var f=s.str.charAt(0),g=s.str.charAt(1),l;g in s.node.edges?l=s.node.edges[g]:(l=new t.TokenSet,s.node.edges[g]=l),s.str.length==1&&(l.final=!0),i.push({node:l,editsRemaining:s.editsRemaining-1,str:f+s.str.slice(2)})}}}return n},t.TokenSet.fromString=function(e){for(var r=new t.TokenSet,n=r,i=0,s=e.length;i=e;r--){var n=this.uncheckedNodes[r],i=n.child.toString();i in this.minimizedNodes?n.parent.edges[n.char]=this.minimizedNodes[i]:(n.child._str=i,this.minimizedNodes[i]=n.child),this.uncheckedNodes.pop()}};t.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},t.Index.prototype.search=function(e){return this.query(function(r){var n=new t.QueryParser(e,r);n.parse()})},t.Index.prototype.query=function(e){for(var r=new t.Query(this.fields),n=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u1?this._b=1:this._b=e},t.Builder.prototype.k1=function(e){this._k1=e},t.Builder.prototype.add=function(e,r){var n=e[this._ref],i=Object.keys(this._fields);this._documents[n]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return t.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},t.QueryLexer.prototype.width=function(){return this.pos-this.start},t.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},t.QueryLexer.prototype.backup=function(){this.pos-=1},t.QueryLexer.prototype.acceptDigitRun=function(){var e,r;do e=this.next(),r=e.charCodeAt(0);while(r>47&&r<58);e!=t.QueryLexer.EOS&&this.backup()},t.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(t.QueryLexer.TERM)),e.ignore(),e.more())return t.QueryLexer.lexText},t.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.EDIT_DISTANCE),t.QueryLexer.lexText},t.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.BOOST),t.QueryLexer.lexText},t.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(t.QueryLexer.TERM)},t.QueryLexer.termSeparator=t.tokenizer.separator,t.QueryLexer.lexText=function(e){for(;;){var r=e.next();if(r==t.QueryLexer.EOS)return t.QueryLexer.lexEOS;if(r.charCodeAt(0)==92){e.escapeCharacter();continue}if(r==":")return t.QueryLexer.lexField;if(r=="~")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexEditDistance;if(r=="^")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexBoost;if(r=="+"&&e.width()===1||r=="-"&&e.width()===1)return e.emit(t.QueryLexer.PRESENCE),t.QueryLexer.lexText;if(r.match(t.QueryLexer.termSeparator))return t.QueryLexer.lexTerm}},t.QueryParser=function(e,r){this.lexer=new t.QueryLexer(e),this.query=r,this.currentClause={},this.lexemeIdx=0},t.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=t.QueryParser.parseClause;e;)e=e(this);return this.query},t.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},t.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},t.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},t.QueryParser.parseClause=function(e){var r=e.peekLexeme();if(r!=null)switch(r.type){case t.QueryLexer.PRESENCE:return t.QueryParser.parsePresence;case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var n="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(n+=" with value '"+r.str+"'"),new t.QueryParseError(n,r.start,r.end)}},t.QueryParser.parsePresence=function(e){var r=e.consumeLexeme();if(r!=null){switch(r.str){case"-":e.currentClause.presence=t.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=t.Query.presence.REQUIRED;break;default:var n="unrecognised presence operator'"+r.str+"'";throw new t.QueryParseError(n,r.start,r.end)}var i=e.peekLexeme();if(i==null){var n="expecting term or field, found nothing";throw new t.QueryParseError(n,r.start,r.end)}switch(i.type){case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var n="expecting term or field, found '"+i.type+"'";throw new t.QueryParseError(n,i.start,i.end)}}},t.QueryParser.parseField=function(e){var r=e.consumeLexeme();if(r!=null){if(e.query.allFields.indexOf(r.str)==-1){var n=e.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+r.str+"', possible fields: "+n;throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.fields=[r.str];var s=e.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new t.QueryParseError(i,r.start,r.end)}switch(s.type){case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var i="expecting term, found '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseTerm=function(e){var r=e.consumeLexeme();if(r!=null){e.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(e.currentClause.usePipeline=!1);var n=e.peekLexeme();if(n==null){e.nextClause();return}switch(n.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+n.type+"'";throw new t.QueryParseError(i,n.start,n.end)}}},t.QueryParser.parseEditDistance=function(e){var r=e.consumeLexeme();if(r!=null){var n=parseInt(r.str,10);if(isNaN(n)){var i="edit distance must be numeric";throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.editDistance=n;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseBoost=function(e){var r=e.consumeLexeme();if(r!=null){var n=parseInt(r.str,10);if(isNaN(n)){var i="boost must be numeric";throw new t.QueryParseError(i,r.start,r.end)}e.currentClause.boost=n;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},function(e,r){typeof define=="function"&&define.amd?define(r):typeof ee=="object"?te.exports=r():e.lunr=r()}(this,function(){return t})})()});var Y=Pe(re());function ne(t,e=document){let r=ke(t,e);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${t}" to be present`);return r}function ke(t,e=document){return e.querySelector(t)||void 0}Object.entries||(Object.entries=function(t){let e=[];for(let r of Object.keys(t))e.push([r,t[r]]);return e});Object.values||(Object.values=function(t){let e=[];for(let r of Object.keys(t))e.push(t[r]);return e});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(t,e){typeof t=="object"?(this.scrollLeft=t.left,this.scrollTop=t.top):(this.scrollLeft=t,this.scrollTop=e)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...t){let e=this.parentNode;if(e){t.length===0&&e.removeChild(this);for(let r=t.length-1;r>=0;r--){let n=t[r];typeof n=="string"?n=document.createTextNode(n):n.parentNode&&n.parentNode.removeChild(n),r?e.insertBefore(this.previousSibling,n):e.replaceChild(n,this)}}}));function ie(t){let e=new Map;for(let r of t){let[n]=r.location.split("#"),i=e.get(n);typeof i=="undefined"?e.set(n,r):(e.set(r.location,r),r.parent=i)}return e}function H(t,e,r){var s;e=new RegExp(e,"g");let n,i=0;do{n=e.exec(t);let o=(s=n==null?void 0:n.index)!=null?s:t.length;if(in?e(r,1,n,n=i):t.charAt(i)===">"&&(t.charAt(n+1)==="/"?--s===0&&e(r++,2,n,i+1):t.charAt(i-1)!=="/"&&s++===0&&e(r,0,n,i+1),n=i+1);i>n&&e(r,1,n,i)}function oe(t,e,r,n=!1){return q([t],e,r,n).pop()}function q(t,e,r,n=!1){let i=[0];for(let s=1;s>>2&1023,c=a[0]>>>12;i.push(+(u>c)+i[i.length-1])}return t.map((s,o)=>{let a=0,u=new Map;for(let f of r.sort((g,l)=>g-l)){let g=f&1048575,l=f>>>20;if(i[l]!==o)continue;let m=u.get(l);typeof m=="undefined"&&u.set(l,m=[]),m.push(g)}if(u.size===0)return s;let c=[];for(let[f,g]of u){let l=e[f],m=l[0]>>>12,x=l[l.length-1]>>>12,v=l[l.length-1]>>>2&1023;n&&m>a&&c.push(s.slice(a,m));let d=s.slice(m,x+v);for(let y of g.sort((b,E)=>E-b)){let b=(l[y]>>>12)-m,E=(l[y]>>>2&1023)+b;d=[d.slice(0,b),"",d.slice(b,E),"",d.slice(E)].join("")}if(a=x+v,c.push(d)===2)break}return n&&a{var f;switch(i[f=o+=s]||(i[f]=[]),a){case 0:case 2:i[o].push(u<<12|c-u<<2|a);break;case 1:let g=r[n].slice(u,c);H(g,lunr.tokenizer.separator,(l,m)=>{if(typeof lunr.segmenter!="undefined"){let x=g.slice(l,m);if(/^[MHIK]$/.test(lunr.segmenter.ctype_(x))){let v=lunr.segmenter.segment(x);for(let d=0,y=0;dr){return t.trim().split(/"([^"]+)"/g).map((r,n)=>n&1?r.replace(/^\b|^(?![^\x00-\x7F]|$)|\s+/g," +"):r).join("").replace(/"|(?:^|\s+)[*+\-:^~]+(?=\s+|$)/g,"").split(/\s+/g).reduce((r,n)=>{let i=e(n);return[...r,...Array.isArray(i)?i:[i]]},[]).map(r=>/([~^]$)/.test(r)?`${r}1`:r).map(r=>/(^[+-]|[~^]\d+$)/.test(r)?r:`${r}*`).join(" ")}function ce(t){return ue(t,e=>{let r=[],n=new lunr.QueryLexer(e);n.run();for(let{type:i,str:s,start:o,end:a}of n.lexemes)switch(i){case"FIELD":["title","text","tags"].includes(s)||(e=[e.slice(0,a)," ",e.slice(a+1)].join(""));break;case"TERM":H(s,lunr.tokenizer.separator,(...u)=>{r.push([e.slice(0,o),s.slice(...u),e.slice(a)].join(""))})}return r})}function le(t){let e=new lunr.Query(["title","text","tags"]);new lunr.QueryParser(t,e).parse();for(let n of e.clauses)n.usePipeline=!0,n.term.startsWith("*")&&(n.wildcard=lunr.Query.wildcard.LEADING,n.term=n.term.slice(1)),n.term.endsWith("*")&&(n.wildcard=lunr.Query.wildcard.TRAILING,n.term=n.term.slice(0,-1));return e.clauses}function he(t,e){var i;let r=new Set(t),n={};for(let s=0;s0;){let o=i[--s];for(let u=1;un[o]-u&&(r.add(t.slice(o,o+u)),i[s++]=o+u);let a=o+n[o];n[a]&&ar=>{if(typeof r[e]=="undefined")return;let n=[r.location,e].join(":");return t.set(n,lunr.tokenizer.table=[]),r[e]}}function Re(t,e){let[r,n]=[new Set(t),new Set(e)];return[...new Set([...r].filter(i=>!n.has(i)))]}var U=class{constructor({config:e,docs:r,options:n}){let i=Oe(this.table=new Map);this.map=ie(r),this.options=n,this.index=lunr(function(){this.metadataWhitelist=["position"],this.b(0),e.lang.length===1&&e.lang[0]!=="en"?this.use(lunr[e.lang[0]]):e.lang.length>1&&this.use(lunr.multiLanguage(...e.lang)),this.tokenizer=ae,lunr.tokenizer.separator=new RegExp(e.separator),lunr.segmenter="TinySegmenter"in lunr?new lunr.TinySegmenter:void 0;let s=Re(["trimmer","stopWordFilter","stemmer"],e.pipeline);for(let o of e.lang.map(a=>a==="en"?lunr:lunr[a]))for(let a of s)this.pipeline.remove(o[a]),this.searchPipeline.remove(o[a]);this.ref("location");for(let[o,a]of Object.entries(e.fields))this.field(o,B(_({},a),{extractor:i(o)}));for(let o of r)this.add(o,{boost:o.boost})})}search(e){if(e=e.replace(new RegExp("\\p{sc=Han}+","gu"),s=>[...fe(s,this.index.invertedIndex)].join("* ")),e=ce(e),!e)return{items:[]};let r=le(e).filter(s=>s.presence!==lunr.Query.presence.PROHIBITED),n=this.index.search(e).reduce((s,{ref:o,score:a,matchData:u})=>{let c=this.map.get(o);if(typeof c!="undefined"){c=_({},c),c.tags&&(c.tags=[...c.tags]);let f=he(r,Object.keys(u.metadata));for(let l of this.index.fields){if(typeof c[l]=="undefined")continue;let m=[];for(let d of Object.values(u.metadata))typeof d[l]!="undefined"&&m.push(...d[l].position);if(!m.length)continue;let x=this.table.get([c.location,l].join(":")),v=Array.isArray(c[l])?q:oe;c[l]=v(c[l],x,m,l!=="text")}let g=+!c.parent+Object.values(f).filter(l=>l).length/Object.keys(f).length;s.push(B(_({},c),{score:a*(1+K(g,2)),terms:f}))}return s},[]).sort((s,o)=>o.score-s.score).reduce((s,o)=>{let a=this.map.get(o.location);if(typeof a!="undefined"){let u=a.parent?a.parent.location:a.location;s.set(u,[...s.get(u)||[],o])}return s},new Map);for(let[s,o]of n)if(!o.find(a=>a.location===s)){let a=this.map.get(s);o.push(B(_({},a),{score:0,terms:{}}))}let i;if(this.options.suggest){let s=this.index.query(o=>{for(let a of r)o.term(a.term,{fields:["title"],presence:lunr.Query.presence.REQUIRED,wildcard:lunr.Query.wildcard.TRAILING})});i=s.length?Object.keys(s[0].matchData.metadata):[]}return _({items:[...n.values()]},typeof i!="undefined"&&{suggest:i})}};var de;function Ie(t){return W(this,null,function*(){let e="../lunr";if(typeof parent!="undefined"&&"IFrameWorker"in parent){let n=ne("script[src]"),[i]=n.src.split("/worker");e=e.replace("..",i)}let r=[];for(let n of t.lang){switch(n){case"ja":r.push(`${e}/tinyseg.js`);break;case"hi":case"th":r.push(`${e}/wordcut.js`);break}n!=="en"&&r.push(`${e}/min/lunr.${n}.min.js`)}t.lang.length>1&&r.push(`${e}/min/lunr.multi.min.js`),r.length&&(yield importScripts(`${e}/min/lunr.stemmer.support.min.js`,...r))})}function Fe(t){return W(this,null,function*(){switch(t.type){case 0:return yield Ie(t.data.config),de=new U(t.data),{type:1};case 2:let e=t.data;try{return{type:3,data:de.search(e)}}catch(r){return console.warn(`Invalid query: ${e} \u2013 see https://bit.ly/2s3ChXG`),console.warn(r),{type:3,data:{items:[]}}}default:throw new TypeError("Invalid message type")}})}self.lunr=Y.default;Y.default.utils.warn=console.warn;addEventListener("message",t=>W(void 0,null,function*(){postMessage(yield Fe(t.data))}));})(); diff --git a/assets/stylesheets/main.12320a83.min.css b/assets/stylesheets/main.12320a83.min.css new file mode 100644 index 0000000000..b33c690219 --- /dev/null +++ b/assets/stylesheets/main.12320a83.min.css @@ -0,0 +1 @@ +@charset "UTF-8";html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;text-size-adjust:none;box-sizing:border-box}*,:after,:before{box-sizing:inherit}@media (prefers-reduced-motion){*,:after,:before{transition:none!important}}body{margin:0}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}hr{border:0;box-sizing:initial;display:block;height:.05rem;overflow:visible;padding:0}small{font-size:80%}sub,sup{line-height:1em}img{border-style:none}table{border-collapse:initial;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{background:#0000;border:0;font-family:inherit;font-size:inherit;margin:0;padding:0}input{border:0;outline:none}:root{--md-primary-fg-color:#4051b5;--md-primary-fg-color--light:#5d6cc0;--md-primary-fg-color--dark:#303fa1;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3;--md-accent-fg-color:#526cfe;--md-accent-fg-color--transparent:#526cfe1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-scheme=default]{color-scheme:light}[data-md-color-scheme=default] img[src$="#gh-dark-mode-only"],[data-md-color-scheme=default] img[src$="#only-dark"]{display:none}:root,[data-md-color-scheme=default]{--md-hue:225deg;--md-default-fg-color:#000000de;--md-default-fg-color--light:#0000008a;--md-default-fg-color--lighter:#00000052;--md-default-fg-color--lightest:#00000012;--md-default-bg-color:#fff;--md-default-bg-color--light:#ffffffb3;--md-default-bg-color--lighter:#ffffff4d;--md-default-bg-color--lightest:#ffffff1f;--md-code-fg-color:#36464e;--md-code-bg-color:#f5f5f5;--md-code-bg-color--light:#f5f5f5b3;--md-code-bg-color--lighter:#f5f5f54d;--md-code-hl-color:#4287ff;--md-code-hl-color--light:#4287ff1a;--md-code-hl-number-color:#d52a2a;--md-code-hl-special-color:#db1457;--md-code-hl-function-color:#a846b9;--md-code-hl-constant-color:#6e59d9;--md-code-hl-keyword-color:#3f6ec6;--md-code-hl-string-color:#1c7d4d;--md-code-hl-name-color:var(--md-code-fg-color);--md-code-hl-operator-color:var(--md-default-fg-color--light);--md-code-hl-punctuation-color:var(--md-default-fg-color--light);--md-code-hl-comment-color:var(--md-default-fg-color--light);--md-code-hl-generic-color:var(--md-default-fg-color--light);--md-code-hl-variable-color:var(--md-default-fg-color--light);--md-typeset-color:var(--md-default-fg-color);--md-typeset-a-color:var(--md-primary-fg-color);--md-typeset-del-color:#f5503d26;--md-typeset-ins-color:#0bd57026;--md-typeset-kbd-color:#fafafa;--md-typeset-kbd-accent-color:#fff;--md-typeset-kbd-border-color:#b8b8b8;--md-typeset-mark-color:#ffff0080;--md-typeset-table-color:#0000001f;--md-typeset-table-color--light:rgba(0,0,0,.035);--md-admonition-fg-color:var(--md-default-fg-color);--md-admonition-bg-color:var(--md-default-bg-color);--md-warning-fg-color:#000000de;--md-warning-bg-color:#ff9;--md-footer-fg-color:#fff;--md-footer-fg-color--light:#ffffffb3;--md-footer-fg-color--lighter:#ffffff73;--md-footer-bg-color:#000000de;--md-footer-bg-color--dark:#00000052;--md-shadow-z1:0 0.2rem 0.5rem #0000000d,0 0 0.05rem #0000001a;--md-shadow-z2:0 0.2rem 0.5rem #0000001a,0 0 0.05rem #00000040;--md-shadow-z3:0 0.2rem 0.5rem #0003,0 0 0.05rem #00000059}.md-icon svg{fill:currentcolor;display:block;height:1.2rem;width:1.2rem}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;--md-text-font-family:var(--md-text-font,_),-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif;--md-code-font-family:var(--md-code-font,_),SFMono-Regular,Consolas,Menlo,monospace}aside,body,input{font-feature-settings:"kern","liga";color:var(--md-typeset-color);font-family:var(--md-text-font-family)}code,kbd,pre{font-feature-settings:"kern";font-family:var(--md-code-font-family)}:root{--md-typeset-table-sort-icon:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--asc:url('data:image/svg+xml;charset=utf-8,');--md-typeset-table-sort-icon--desc:url('data:image/svg+xml;charset=utf-8,')}.md-typeset{-webkit-print-color-adjust:exact;color-adjust:exact;font-size:.8rem;line-height:1.6}@media print{.md-typeset{font-size:.68rem}}.md-typeset blockquote,.md-typeset dl,.md-typeset figure,.md-typeset ol,.md-typeset pre,.md-typeset ul{margin-bottom:1em;margin-top:1em}.md-typeset h1{color:var(--md-default-fg-color--light);font-size:2em;line-height:1.3;margin:0 0 1.25em}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{font-size:1.5625em;line-height:1.4;margin:1.6em 0 .64em}.md-typeset h3{font-size:1.25em;font-weight:400;letter-spacing:-.01em;line-height:1.5;margin:1.6em 0 .8em}.md-typeset h2+h3{margin-top:.8em}.md-typeset h4{font-weight:700;letter-spacing:-.01em;margin:1em 0}.md-typeset h5,.md-typeset h6{color:var(--md-default-fg-color--light);font-size:.8em;font-weight:700;letter-spacing:-.01em;margin:1.25em 0}.md-typeset h5{text-transform:uppercase}.md-typeset hr{border-bottom:.05rem solid var(--md-default-fg-color--lightest);display:flow-root;margin:1.5em 0}.md-typeset a{color:var(--md-typeset-a-color);word-break:break-word}.md-typeset a,.md-typeset a:before{transition:color 125ms}.md-typeset a:focus,.md-typeset a:hover{color:var(--md-accent-fg-color)}.md-typeset a:focus code,.md-typeset a:hover code{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-typeset a code{color:var(--md-typeset-a-color)}.md-typeset a.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset code,.md-typeset kbd,.md-typeset pre{color:var(--md-code-fg-color);direction:ltr;font-variant-ligatures:none;transition:background-color 125ms}@media print{.md-typeset code,.md-typeset kbd,.md-typeset pre{white-space:pre-wrap}}.md-typeset code{background-color:var(--md-code-bg-color);border-radius:.1rem;-webkit-box-decoration-break:clone;box-decoration-break:clone;font-size:.85em;padding:0 .2941176471em;transition:color 125ms,background-color 125ms;word-break:break-word}.md-typeset code:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-typeset pre{display:flow-root;line-height:1.4;position:relative}.md-typeset pre>code{-webkit-box-decoration-break:slice;box-decoration-break:slice;box-shadow:none;display:block;margin:0;outline-color:var(--md-accent-fg-color);overflow:auto;padding:.7720588235em 1.1764705882em;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin;touch-action:auto;word-break:normal}.md-typeset pre>code:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-typeset pre>code::-webkit-scrollbar{height:.2rem;width:.2rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}.md-typeset kbd{background-color:var(--md-typeset-kbd-color);border-radius:.1rem;box-shadow:0 .1rem 0 .05rem var(--md-typeset-kbd-border-color),0 .1rem 0 var(--md-typeset-kbd-border-color),0 -.1rem .2rem var(--md-typeset-kbd-accent-color) inset;color:var(--md-default-fg-color);display:inline-block;font-size:.75em;padding:0 .6666666667em;vertical-align:text-top;word-break:break-word}.md-typeset mark{background-color:var(--md-typeset-mark-color);-webkit-box-decoration-break:clone;box-decoration-break:clone;color:inherit;word-break:break-word}.md-typeset abbr{cursor:help;text-decoration:none}.md-typeset [data-preview],.md-typeset abbr{border-bottom:.05rem dotted var(--md-default-fg-color--light)}.md-typeset small{opacity:.75}[dir=ltr] .md-typeset sub,[dir=ltr] .md-typeset sup{margin-left:.078125em}[dir=rtl] .md-typeset sub,[dir=rtl] .md-typeset sup{margin-right:.078125em}[dir=ltr] .md-typeset blockquote{padding-left:.6rem}[dir=rtl] .md-typeset blockquote{padding-right:.6rem}[dir=ltr] .md-typeset blockquote{border-left:.2rem solid var(--md-default-fg-color--lighter)}[dir=rtl] .md-typeset blockquote{border-right:.2rem solid var(--md-default-fg-color--lighter)}.md-typeset blockquote{color:var(--md-default-fg-color--light);margin-left:0;margin-right:0}.md-typeset ul{list-style-type:disc}.md-typeset ul[type]{list-style-type:revert-layer}[dir=ltr] .md-typeset ol,[dir=ltr] .md-typeset ul{margin-left:.625em}[dir=rtl] .md-typeset ol,[dir=rtl] .md-typeset ul{margin-right:.625em}.md-typeset ol,.md-typeset ul{padding:0}.md-typeset ol:not([hidden]),.md-typeset ul:not([hidden]){display:flow-root}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}.md-typeset ol ol ol ol,.md-typeset ul ol ol ol{list-style-type:upper-alpha}.md-typeset ol ol ol ol ol,.md-typeset ul ol ol ol ol{list-style-type:upper-roman}.md-typeset ol[type],.md-typeset ul[type]{list-style-type:revert-layer}[dir=ltr] .md-typeset ol li,[dir=ltr] .md-typeset ul li{margin-left:1.25em}[dir=rtl] .md-typeset ol li,[dir=rtl] .md-typeset ul li{margin-right:1.25em}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}[dir=ltr] .md-typeset ol li ol,[dir=ltr] .md-typeset ol li ul,[dir=ltr] .md-typeset ul li ol,[dir=ltr] .md-typeset ul li ul{margin-left:.625em}[dir=rtl] .md-typeset ol li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ul li ul{margin-right:.625em}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin-bottom:.5em;margin-top:.5em}[dir=ltr] .md-typeset dd{margin-left:1.875em}[dir=rtl] .md-typeset dd{margin-right:1.875em}.md-typeset dd{margin-bottom:1.5em;margin-top:1em}.md-typeset img,.md-typeset svg,.md-typeset video{height:auto;max-width:100%}.md-typeset img[align=left]{margin:1em 1em 1em 0}.md-typeset img[align=right]{margin:1em 0 1em 1em}.md-typeset img[align]:only-child{margin-top:0}.md-typeset figure{display:flow-root;margin:1em auto;max-width:100%;text-align:center;width:-moz-fit-content;width:fit-content}.md-typeset figure img{display:block;margin:0 auto}.md-typeset figcaption{font-style:italic;margin:1em auto;max-width:24rem}.md-typeset iframe{max-width:100%}.md-typeset table:not([class]){background-color:var(--md-default-bg-color);border:.05rem solid var(--md-typeset-table-color);border-radius:.1rem;display:inline-block;font-size:.64rem;max-width:100%;overflow:auto;touch-action:auto}@media print{.md-typeset table:not([class]){display:table}}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td>:first-child,.md-typeset table:not([class]) th>:first-child{margin-top:0}.md-typeset table:not([class]) td>:last-child,.md-typeset table:not([class]) th>:last-child{margin-bottom:0}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) td:not([align]),[dir=rtl] .md-typeset table:not([class]) th:not([align]){text-align:right}.md-typeset table:not([class]) th{font-weight:700;min-width:5rem;padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) td{border-top:.05rem solid var(--md-typeset-table-color);padding:.9375em 1.25em;vertical-align:top}.md-typeset table:not([class]) tbody tr{transition:background-color 125ms}.md-typeset table:not([class]) tbody tr:hover{background-color:var(--md-typeset-table-color--light);box-shadow:0 .05rem 0 var(--md-default-bg-color) inset}.md-typeset table:not([class]) a{word-break:normal}.md-typeset table th[role=columnheader]{cursor:pointer}[dir=ltr] .md-typeset table th[role=columnheader]:after{margin-left:.5em}[dir=rtl] .md-typeset table th[role=columnheader]:after{margin-right:.5em}.md-typeset table th[role=columnheader]:after{content:"";display:inline-block;height:1.2em;-webkit-mask-image:var(--md-typeset-table-sort-icon);mask-image:var(--md-typeset-table-sort-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset table th[role=columnheader]:hover:after{background-color:var(--md-default-fg-color--lighter)}.md-typeset table th[role=columnheader][aria-sort=ascending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--asc);mask-image:var(--md-typeset-table-sort-icon--asc)}.md-typeset table th[role=columnheader][aria-sort=descending]:after{background-color:var(--md-default-fg-color--light);-webkit-mask-image:var(--md-typeset-table-sort-icon--desc);mask-image:var(--md-typeset-table-sort-icon--desc)}.md-typeset__scrollwrap{margin:1em -.8rem;overflow-x:auto;touch-action:auto}.md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 .8rem}@media print{.md-typeset__table{display:block}}html .md-typeset__table table{display:table;margin:0;overflow:hidden;width:100%}@media screen and (max-width:44.984375em){.md-content__inner>pre{margin:1em -.8rem}.md-content__inner>pre code{border-radius:0}}.md-typeset .md-author{border-radius:100%;display:block;flex-shrink:0;height:1.6rem;overflow:hidden;position:relative;transition:color 125ms,transform 125ms;width:1.6rem}.md-typeset .md-author img{display:block}.md-typeset .md-author--more{background:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--lighter);font-size:.6rem;font-weight:700;line-height:1.6rem;text-align:center}.md-typeset .md-author--long{height:2.4rem;width:2.4rem}.md-typeset a.md-author{transform:scale(1)}.md-typeset a.md-author img{border-radius:100%;filter:grayscale(100%) opacity(75%);transition:filter 125ms}.md-typeset a.md-author:focus,.md-typeset a.md-author:hover{transform:scale(1.1);z-index:1}.md-typeset a.md-author:focus img,.md-typeset a.md-author:hover img{filter:grayscale(0)}.md-banner{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color);overflow:auto}@media print{.md-banner{display:none}}.md-banner--warning{background-color:var(--md-warning-bg-color);color:var(--md-warning-fg-color)}.md-banner__inner{font-size:.7rem;margin:.6rem auto;padding:0 .8rem}[dir=ltr] .md-banner__button{float:right}[dir=rtl] .md-banner__button{float:left}.md-banner__button{color:inherit;cursor:pointer;transition:opacity .25s}.no-js .md-banner__button{display:none}.md-banner__button:hover{opacity:.7}html{font-size:125%;height:100%;overflow-x:hidden}@media screen and (min-width:100em){html{font-size:137.5%}}@media screen and (min-width:125em){html{font-size:150%}}body{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;font-size:.5rem;min-height:100%;position:relative;width:100%}@media print{body{display:block}}@media screen and (max-width:59.984375em){body[data-md-scrolllock]{position:fixed}}.md-grid{margin-left:auto;margin-right:auto;max-width:61rem}.md-container{display:flex;flex-direction:column;flex-grow:1}@media print{.md-container{display:block}}.md-main{flex-grow:1}.md-main__inner{display:flex;height:100%;margin-top:1.5rem}.md-ellipsis{overflow:hidden;text-overflow:ellipsis}.md-toggle{display:none}.md-option{height:0;opacity:0;position:absolute;width:0}.md-option:checked+label:not([hidden]){display:block}.md-option.focus-visible+label{outline-color:var(--md-accent-fg-color);outline-style:auto}.md-skip{background-color:var(--md-default-fg-color);border-radius:.1rem;color:var(--md-default-bg-color);font-size:.64rem;margin:.5rem;opacity:0;outline-color:var(--md-accent-fg-color);padding:.3rem .5rem;position:fixed;transform:translateY(.4rem);z-index:-1}.md-skip:focus{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 175ms 75ms;z-index:10}@page{margin:25mm}:root{--md-clipboard-icon:url('data:image/svg+xml;charset=utf-8,')}.md-clipboard{border-radius:.1rem;color:var(--md-default-fg-color--lightest);cursor:pointer;height:1.5em;outline-color:var(--md-accent-fg-color);outline-offset:.1rem;transition:color .25s;width:1.5em;z-index:1}@media print{.md-clipboard{display:none}}.md-clipboard:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}:hover>.md-clipboard{color:var(--md-default-fg-color--light)}.md-clipboard:focus,.md-clipboard:hover{color:var(--md-accent-fg-color)}.md-clipboard:after{background-color:currentcolor;content:"";display:block;height:1.125em;margin:0 auto;-webkit-mask-image:var(--md-clipboard-icon);mask-image:var(--md-clipboard-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:1.125em}.md-clipboard--inline{cursor:pointer}.md-clipboard--inline code{transition:color .25s,background-color .25s}.md-clipboard--inline:focus code,.md-clipboard--inline:hover code{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}:root{--md-code-select-icon:url('data:image/svg+xml;charset=utf-8,');--md-code-copy-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .md-code__content{display:grid}.md-code__nav{background-color:var(--md-code-bg-color--lighter);border-radius:.1rem;display:flex;gap:.2rem;padding:.2rem;position:absolute;right:.25em;top:.25em;transition:background-color .25s;z-index:1}:hover>.md-code__nav{background-color:var(--md-code-bg-color--light)}.md-code__button{color:var(--md-default-fg-color--lightest);cursor:pointer;display:block;height:1.5em;outline-color:var(--md-accent-fg-color);outline-offset:.1rem;transition:color .25s;width:1.5em}:hover>*>.md-code__button{color:var(--md-default-fg-color--light)}.md-code__button.focus-visible,.md-code__button:hover{color:var(--md-accent-fg-color)}.md-code__button--active{color:var(--md-default-fg-color)!important}.md-code__button:after{background-color:currentcolor;content:"";display:block;height:1.125em;margin:0 auto;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:1.125em}.md-code__button[data-md-type=select]:after{-webkit-mask-image:var(--md-code-select-icon);mask-image:var(--md-code-select-icon)}.md-code__button[data-md-type=copy]:after{-webkit-mask-image:var(--md-code-copy-icon);mask-image:var(--md-code-copy-icon)}@keyframes consent{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}@keyframes overlay{0%{opacity:0}to{opacity:1}}.md-consent__overlay{animation:overlay .25s both;-webkit-backdrop-filter:blur(.1rem);backdrop-filter:blur(.1rem);background-color:#0000008a;height:100%;opacity:1;position:fixed;top:0;width:100%;z-index:5}.md-consent__inner{animation:consent .5s cubic-bezier(.1,.7,.1,1) both;background-color:var(--md-default-bg-color);border:0;border-radius:.1rem;bottom:0;box-shadow:0 0 .2rem #0000001a,0 .2rem .4rem #0003;max-height:100%;overflow:auto;padding:0;position:fixed;width:100%;z-index:5}.md-consent__form{padding:.8rem}.md-consent__settings{display:none;margin:1em 0}input:checked+.md-consent__settings{display:block}.md-consent__controls{margin-bottom:.8rem}.md-typeset .md-consent__controls .md-button{display:inline}@media screen and (max-width:44.984375em){.md-typeset .md-consent__controls .md-button{display:block;margin-top:.4rem;text-align:center;width:100%}}.md-consent label{cursor:pointer}.md-content{flex-grow:1;min-width:0}.md-content__inner{margin:0 .8rem 1.2rem;padding-top:.6rem}@media screen and (min-width:76.25em){[dir=ltr] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}[dir=ltr] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner,[dir=rtl] .md-sidebar--primary:not([hidden])~.md-content>.md-content__inner{margin-right:1.2rem}[dir=rtl] .md-sidebar--secondary:not([hidden])~.md-content>.md-content__inner{margin-left:1.2rem}}.md-content__inner:before{content:"";display:block;height:.4rem}.md-content__inner>:last-child{margin-bottom:0}[dir=ltr] .md-content__button{float:right}[dir=rtl] .md-content__button{float:left}[dir=ltr] .md-content__button{margin-left:.4rem}[dir=rtl] .md-content__button{margin-right:.4rem}.md-content__button{margin:.4rem 0;padding:0}@media print{.md-content__button{display:none}}.md-typeset .md-content__button{color:var(--md-default-fg-color--lighter)}.md-content__button svg{display:inline;vertical-align:top}[dir=rtl] .md-content__button svg{transform:scaleX(-1)}[dir=ltr] .md-dialog{right:.8rem}[dir=rtl] .md-dialog{left:.8rem}.md-dialog{background-color:var(--md-default-fg-color);border-radius:.1rem;bottom:.8rem;box-shadow:var(--md-shadow-z3);min-width:11.1rem;opacity:0;padding:.4rem .6rem;pointer-events:none;position:fixed;transform:translateY(100%);transition:transform 0ms .4s,opacity .4s;z-index:4}@media print{.md-dialog{display:none}}.md-dialog--active{opacity:1;pointer-events:auto;transform:translateY(0);transition:transform .4s cubic-bezier(.075,.85,.175,1),opacity .4s}.md-dialog__inner{color:var(--md-default-bg-color);font-size:.7rem}.md-feedback{margin:2em 0 1em;text-align:center}.md-feedback fieldset{border:none;margin:0;padding:0}.md-feedback__title{font-weight:700;margin:1em auto}.md-feedback__inner{position:relative}.md-feedback__list{display:flex;flex-wrap:wrap;place-content:baseline center;position:relative}.md-feedback__list:hover .md-icon:not(:disabled){color:var(--md-default-fg-color--lighter)}:disabled .md-feedback__list{min-height:1.8rem}.md-feedback__icon{color:var(--md-default-fg-color--light);cursor:pointer;flex-shrink:0;margin:0 .1rem;transition:color 125ms}.md-feedback__icon:not(:disabled).md-icon:hover{color:var(--md-accent-fg-color)}.md-feedback__icon:disabled{color:var(--md-default-fg-color--lightest);pointer-events:none}.md-feedback__note{opacity:0;position:relative;transform:translateY(.4rem);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-feedback__note>*{margin:0 auto;max-width:16rem}:disabled .md-feedback__note{opacity:1;transform:translateY(0)}@media print{.md-feedback{display:none}}.md-footer{background-color:var(--md-footer-bg-color);color:var(--md-footer-fg-color)}@media print{.md-footer{display:none}}.md-footer__inner{justify-content:space-between;overflow:auto;padding:.2rem}.md-footer__inner:not([hidden]){display:flex}.md-footer__link{align-items:end;display:flex;flex-grow:0.01;margin-bottom:.4rem;margin-top:1rem;max-width:100%;outline-color:var(--md-accent-fg-color);overflow:hidden;transition:opacity .25s}.md-footer__link:focus,.md-footer__link:hover{opacity:.7}[dir=rtl] .md-footer__link svg{transform:scaleX(-1)}@media screen and (max-width:44.984375em){.md-footer__link--prev{flex-shrink:0}.md-footer__link--prev .md-footer__title{display:none}}[dir=ltr] .md-footer__link--next{margin-left:auto}[dir=rtl] .md-footer__link--next{margin-right:auto}.md-footer__link--next{text-align:right}[dir=rtl] .md-footer__link--next{text-align:left}.md-footer__title{flex-grow:1;font-size:.9rem;margin-bottom:.7rem;max-width:calc(100% - 2.4rem);padding:0 1rem;white-space:nowrap}.md-footer__button{margin:.2rem;padding:.4rem}.md-footer__direction{font-size:.64rem;opacity:.7}.md-footer-meta{background-color:var(--md-footer-bg-color--dark)}.md-footer-meta__inner{display:flex;flex-wrap:wrap;justify-content:space-between;padding:.2rem}html .md-footer-meta.md-typeset a{color:var(--md-footer-fg-color--light)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:var(--md-footer-fg-color)}.md-copyright{color:var(--md-footer-fg-color--lighter);font-size:.64rem;margin:auto .6rem;padding:.4rem 0;width:100%}@media screen and (min-width:45em){.md-copyright{width:auto}}.md-copyright__highlight{color:var(--md-footer-fg-color--light)}.md-social{display:inline-flex;gap:.2rem;margin:0 .4rem;padding:.2rem 0 .6rem}@media screen and (min-width:45em){.md-social{padding:.6rem 0}}.md-social__link{display:inline-block;height:1.6rem;text-align:center;width:1.6rem}.md-social__link:before{line-height:1.9}.md-social__link svg{fill:currentcolor;max-height:.8rem;vertical-align:-25%}.md-typeset .md-button{border:.1rem solid;border-radius:.1rem;color:var(--md-primary-fg-color);cursor:pointer;display:inline-block;font-weight:700;padding:.625em 2em;transition:color 125ms,background-color 125ms,border-color 125ms}.md-typeset .md-button--primary{background-color:var(--md-primary-fg-color);border-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color)}.md-typeset .md-button:focus,.md-typeset .md-button:hover{background-color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}[dir=ltr] .md-typeset .md-input{border-top-left-radius:.1rem}[dir=ltr] .md-typeset .md-input,[dir=rtl] .md-typeset .md-input{border-top-right-radius:.1rem}[dir=rtl] .md-typeset .md-input{border-top-left-radius:.1rem}.md-typeset .md-input{border-bottom:.1rem solid var(--md-default-fg-color--lighter);box-shadow:var(--md-shadow-z1);font-size:.8rem;height:1.8rem;padding:0 .6rem;transition:border .25s,box-shadow .25s}.md-typeset .md-input:focus,.md-typeset .md-input:hover{border-bottom-color:var(--md-accent-fg-color);box-shadow:var(--md-shadow-z2)}.md-typeset .md-input--stretch{width:100%}.md-header{background-color:var(--md-primary-fg-color);box-shadow:0 0 .2rem #0000,0 .2rem .4rem #0000;color:var(--md-primary-bg-color);display:block;left:0;position:sticky;right:0;top:0;z-index:4}@media print{.md-header{display:none}}.md-header[hidden]{transform:translateY(-100%);transition:transform .25s cubic-bezier(.8,0,.6,1),box-shadow .25s}.md-header--shadow{box-shadow:0 0 .2rem #0000001a,0 .2rem .4rem #0003;transition:transform .25s cubic-bezier(.1,.7,.1,1),box-shadow .25s}.md-header__inner{align-items:center;display:flex;padding:0 .2rem}.md-header__button{color:currentcolor;cursor:pointer;margin:.2rem;outline-color:var(--md-accent-fg-color);padding:.4rem;position:relative;transition:opacity .25s;vertical-align:middle;z-index:1}.md-header__button:hover{opacity:.7}.md-header__button:not([hidden]){display:inline-block}.md-header__button:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}.md-header__button.md-logo{margin:.2rem;padding:.4rem}@media screen and (max-width:76.234375em){.md-header__button.md-logo{display:none}}.md-header__button.md-logo img,.md-header__button.md-logo svg{fill:currentcolor;display:block;height:1.2rem;width:auto}@media screen and (min-width:60em){.md-header__button[for=__search]{display:none}}.no-js .md-header__button[for=__search]{display:none}[dir=rtl] .md-header__button[for=__search] svg{transform:scaleX(-1)}@media screen and (min-width:76.25em){.md-header__button[for=__drawer]{display:none}}.md-header__topic{display:flex;max-width:100%;position:absolute;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;white-space:nowrap}.md-header__topic+.md-header__topic{opacity:0;pointer-events:none;transform:translateX(1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__topic+.md-header__topic{transform:translateX(-1.25rem)}.md-header__topic:first-child{font-weight:700}[dir=ltr] .md-header__title{margin-left:1rem;margin-right:.4rem}[dir=rtl] .md-header__title{margin-left:.4rem;margin-right:1rem}.md-header__title{flex-grow:1;font-size:.9rem;height:2.4rem;line-height:2.4rem}.md-header__title--active .md-header__topic{opacity:0;pointer-events:none;transform:translateX(-1.25rem);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;z-index:-1}[dir=rtl] .md-header__title--active .md-header__topic{transform:translateX(1.25rem)}.md-header__title--active .md-header__topic+.md-header__topic{opacity:1;pointer-events:auto;transform:translateX(0);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;z-index:0}.md-header__title>.md-header__ellipsis{height:100%;position:relative;width:100%}.md-header__option{display:flex;flex-shrink:0;max-width:100%;transition:max-width 0ms .25s,opacity .25s .25s;white-space:nowrap}[data-md-toggle=search]:checked~.md-header .md-header__option{max-width:0;opacity:0;transition:max-width 0ms,opacity 0ms}.md-header__option>input{bottom:0}.md-header__source{display:none}@media screen and (min-width:60em){[dir=ltr] .md-header__source{margin-left:1rem}[dir=rtl] .md-header__source{margin-right:1rem}.md-header__source{display:block;max-width:11.7rem;width:11.7rem}}@media screen and (min-width:76.25em){[dir=ltr] .md-header__source{margin-left:1.4rem}[dir=rtl] .md-header__source{margin-right:1.4rem}}.md-meta{color:var(--md-default-fg-color--light);font-size:.7rem;line-height:1.3}.md-meta__list{display:inline-flex;flex-wrap:wrap;list-style:none;margin:0;padding:0}.md-meta__item:not(:last-child):after{content:"·";margin-left:.2rem;margin-right:.2rem}.md-meta__link{color:var(--md-typeset-a-color)}.md-meta__link:focus,.md-meta__link:hover{color:var(--md-accent-fg-color)}.md-draft{background-color:#ff1744;border-radius:.125em;color:#fff;display:inline-block;font-weight:700;padding-left:.5714285714em;padding-right:.5714285714em}:root{--md-nav-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-nav-icon--next:url('data:image/svg+xml;charset=utf-8,');--md-toc-icon:url('data:image/svg+xml;charset=utf-8,')}.md-nav{font-size:.7rem;line-height:1.3}.md-nav__title{color:var(--md-default-fg-color--light);display:block;font-weight:700;overflow:hidden;padding:0 .6rem;text-overflow:ellipsis}.md-nav__title .md-nav__button{display:none}.md-nav__title .md-nav__button img{height:100%;width:auto}.md-nav__title .md-nav__button.md-logo img,.md-nav__title .md-nav__button.md-logo svg{fill:currentcolor;display:block;height:2.4rem;max-width:100%;object-fit:contain;width:auto}.md-nav__list{list-style:none;margin:0;padding:0}.md-nav__link{align-items:flex-start;display:flex;gap:.4rem;margin-top:.625em;scroll-snap-align:start;transition:color 125ms}.md-nav__link--passed,.md-nav__link--passed code{color:var(--md-default-fg-color--light)}.md-nav__item .md-nav__link--active,.md-nav__item .md-nav__link--active code{color:var(--md-typeset-a-color)}.md-nav__link .md-ellipsis{position:relative}.md-nav__link .md-ellipsis code{word-break:normal}[dir=ltr] .md-nav__link .md-icon:last-child{margin-left:auto}[dir=rtl] .md-nav__link .md-icon:last-child{margin-right:auto}.md-nav__link .md-typeset{font-size:.7rem;line-height:1.3}.md-nav__link svg{fill:currentcolor;flex-shrink:0;height:1.3em;position:relative}.md-nav__link[for]:focus,.md-nav__link[for]:hover,.md-nav__link[href]:focus,.md-nav__link[href]:hover{color:var(--md-accent-fg-color);cursor:pointer}.md-nav__link[for]:focus code,.md-nav__link[for]:hover code,.md-nav__link[href]:focus code,.md-nav__link[href]:hover code{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-nav__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-nav--primary .md-nav__link[for=__toc]{display:none}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{background-color:currentcolor;display:block;height:100%;-webkit-mask-image:var(--md-toc-icon);mask-image:var(--md-toc-icon);width:100%}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:none}.md-nav__container>.md-nav__link{margin-top:0}.md-nav__container>.md-nav__link:first-child{flex-grow:1;min-width:0}.md-nav__icon{flex-shrink:0}.md-nav__source{display:none}@media screen and (max-width:76.234375em){.md-nav--primary,.md-nav--primary .md-nav{background-color:var(--md-default-bg-color);display:flex;flex-direction:column;height:100%;left:0;position:absolute;right:0;top:0;z-index:1}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:.8rem;line-height:1.5}.md-nav--primary .md-nav__title{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);cursor:pointer;height:5.6rem;line-height:2.4rem;padding:3rem .8rem .2rem;position:relative;white-space:nowrap}[dir=ltr] .md-nav--primary .md-nav__title .md-nav__icon{left:.4rem}[dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon{right:.4rem}.md-nav--primary .md-nav__title .md-nav__icon{display:block;height:1.2rem;margin:.2rem;position:absolute;top:.4rem;width:1.2rem}.md-nav--primary .md-nav__title .md-nav__icon:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--prev);mask-image:var(--md-nav-icon--prev);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}.md-nav--primary .md-nav__title~.md-nav__list{background-color:var(--md-default-bg-color);box-shadow:0 .05rem 0 var(--md-default-fg-color--lightest) inset;overflow-y:auto;scroll-snap-type:y mandatory;touch-action:pan-y}.md-nav--primary .md-nav__title~.md-nav__list>:first-child{border-top:0}.md-nav--primary .md-nav__title[for=__drawer]{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);font-weight:700}.md-nav--primary .md-nav__title .md-logo{display:block;left:.2rem;margin:.2rem;padding:.4rem;position:absolute;right:.2rem;top:.2rem}.md-nav--primary .md-nav__list{flex:1}.md-nav--primary .md-nav__item{border-top:.05rem solid var(--md-default-fg-color--lightest)}.md-nav--primary .md-nav__item--active>.md-nav__link{color:var(--md-typeset-a-color)}.md-nav--primary .md-nav__item--active>.md-nav__link:focus,.md-nav--primary .md-nav__item--active>.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav--primary .md-nav__link{margin-top:0;padding:.6rem .8rem}.md-nav--primary .md-nav__link svg{margin-top:.1em}.md-nav--primary .md-nav__link>.md-nav__link{padding:0}[dir=ltr] .md-nav--primary .md-nav__link .md-nav__icon{margin-right:-.2rem}[dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon{margin-left:-.2rem}.md-nav--primary .md-nav__link .md-nav__icon{font-size:1.2rem;height:1.2rem;width:1.2rem}.md-nav--primary .md-nav__link .md-nav__icon:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-nav--primary .md-nav__icon:after{transform:scale(-1)}.md-nav--primary .md-nav--secondary .md-nav{background-color:initial;position:static}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:1.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-right:1.4rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-right:2rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:2.6rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-right:2.6rem}[dir=ltr] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:3.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-right:3.2rem}.md-nav--secondary{background-color:initial}.md-nav__toggle~.md-nav{display:flex;opacity:0;transform:translateX(100%);transition:transform .25s cubic-bezier(.8,0,.6,1),opacity 125ms 50ms}[dir=rtl] .md-nav__toggle~.md-nav{transform:translateX(-100%)}.md-nav__toggle:checked~.md-nav{opacity:1;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity 125ms 125ms}.md-nav__toggle:checked~.md-nav>.md-nav__list{-webkit-backface-visibility:hidden;backface-visibility:hidden}}@media screen and (max-width:59.984375em){.md-nav--primary .md-nav__link[for=__toc]{display:flex}.md-nav--primary .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--primary .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--primary .md-nav__link[for=__toc]~.md-nav{display:flex}.md-nav__source{background-color:var(--md-primary-fg-color--dark);color:var(--md-primary-bg-color);display:block;padding:0 .2rem}}@media screen and (min-width:60em) and (max-width:76.234375em){.md-nav--integrated .md-nav__link[for=__toc]{display:flex}.md-nav--integrated .md-nav__link[for=__toc] .md-icon:after{content:""}.md-nav--integrated .md-nav__link[for=__toc]+.md-nav__link{display:none}.md-nav--integrated .md-nav__link[for=__toc]~.md-nav{display:flex}}@media screen and (min-width:60em){.md-nav{margin-bottom:-.4rem}.md-nav--secondary .md-nav__title{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);position:sticky;top:0;z-index:1}.md-nav--secondary .md-nav__title[for=__toc]{scroll-snap-align:start}.md-nav--secondary .md-nav__title .md-nav__icon{display:none}[dir=ltr] .md-nav--secondary .md-nav__list{padding-left:.6rem}[dir=rtl] .md-nav--secondary .md-nav__list{padding-right:.6rem}.md-nav--secondary .md-nav__list{padding-bottom:.4rem}[dir=ltr] .md-nav--secondary .md-nav__item>.md-nav__link{margin-right:.4rem}[dir=rtl] .md-nav--secondary .md-nav__item>.md-nav__link{margin-left:.4rem}}@media screen and (min-width:76.25em){.md-nav{margin-bottom:-.4rem;transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav--primary .md-nav__title{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);position:sticky;top:0;z-index:1}.md-nav--primary .md-nav__title[for=__drawer]{scroll-snap-align:start}.md-nav--primary .md-nav__title .md-nav__icon{display:none}[dir=ltr] .md-nav--primary .md-nav__list{padding-left:.6rem}[dir=rtl] .md-nav--primary .md-nav__list{padding-right:.6rem}.md-nav--primary .md-nav__list{padding-bottom:.4rem}[dir=ltr] .md-nav--primary .md-nav__item>.md-nav__link{margin-right:.4rem}[dir=rtl] .md-nav--primary .md-nav__item>.md-nav__link{margin-left:.4rem}.md-nav__toggle~.md-nav{display:grid;grid-template-rows:0fr;opacity:0;transition:grid-template-rows .25s cubic-bezier(.86,0,.07,1),opacity .25s,visibility 0ms .25s;visibility:collapse}.md-nav__toggle~.md-nav>.md-nav__list{overflow:hidden}.md-nav__toggle.md-toggle--indeterminate~.md-nav,.md-nav__toggle:checked~.md-nav{grid-template-rows:1fr;opacity:1;transition:grid-template-rows .25s cubic-bezier(.86,0,.07,1),opacity .15s .1s,visibility 0ms;visibility:visible}.md-nav__toggle.md-toggle--indeterminate~.md-nav{transition:none}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--section{display:block;margin:1.25em 0}.md-nav__item--section:last-child{margin-bottom:0}.md-nav__item--section>.md-nav__link{font-weight:700}.md-nav__item--section>.md-nav__link[for]{color:var(--md-default-fg-color--light)}.md-nav__item--section>.md-nav__link:not(.md-nav__container){pointer-events:none}.md-nav__item--section>.md-nav__link .md-icon,.md-nav__item--section>.md-nav__link>[for]{display:none}[dir=ltr] .md-nav__item--section>.md-nav{margin-left:-.6rem}[dir=rtl] .md-nav__item--section>.md-nav{margin-right:-.6rem}.md-nav__item--section>.md-nav{display:block;opacity:1;visibility:visible}.md-nav__item--section>.md-nav>.md-nav__list>.md-nav__item{padding:0}.md-nav__icon{border-radius:100%;height:.9rem;transition:background-color .25s;width:.9rem}.md-nav__icon:hover{background-color:var(--md-accent-fg-color--transparent)}.md-nav__icon:after{background-color:currentcolor;border-radius:100%;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:transform .25s;vertical-align:-.1rem;width:100%}[dir=rtl] .md-nav__icon:after{transform:rotate(180deg)}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link .md-nav__icon:after,.md-nav__item--nested .md-toggle--indeterminate~.md-nav__link .md-nav__icon:after{transform:rotate(90deg)}.md-nav--lifted>.md-nav__list>.md-nav__item,.md-nav--lifted>.md-nav__title{display:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active{display:block}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link{background:var(--md-default-bg-color);box-shadow:0 0 .4rem .4rem var(--md-default-bg-color);margin-top:0;position:sticky;top:0;z-index:1}.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link:not(.md-nav__container){pointer-events:none}.md-nav--lifted>.md-nav__list>.md-nav__item--active.md-nav__item--section{margin:0}[dir=ltr] .md-nav--lifted>.md-nav__list>.md-nav__item>.md-nav:not(.md-nav--secondary){margin-left:-.6rem}[dir=rtl] .md-nav--lifted>.md-nav__list>.md-nav__item>.md-nav:not(.md-nav--secondary){margin-right:-.6rem}.md-nav--lifted>.md-nav__list>.md-nav__item>[for]{color:var(--md-default-fg-color--light)}.md-nav--lifted .md-nav[data-md-level="1"]{grid-template-rows:1fr;opacity:1;visibility:visible}[dir=ltr] .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{border-left:.05rem solid var(--md-primary-fg-color)}[dir=rtl] .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{border-right:.05rem solid var(--md-primary-fg-color)}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary{display:block;margin-bottom:1.25em;opacity:1;visibility:visible}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary>.md-nav__list{overflow:visible;padding-bottom:0}.md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary>.md-nav__title{display:none}}.md-pagination{font-size:.8rem;font-weight:700;gap:.4rem}.md-pagination,.md-pagination>*{align-items:center;display:flex;justify-content:center}.md-pagination>*{border-radius:.2rem;height:1.8rem;min-width:1.8rem;text-align:center}.md-pagination__current{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light)}.md-pagination__link{transition:color 125ms,background-color 125ms}.md-pagination__link:focus,.md-pagination__link:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-pagination__link:focus svg,.md-pagination__link:hover svg{color:var(--md-accent-fg-color)}.md-pagination__link.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-pagination__link svg{fill:currentcolor;color:var(--md-default-fg-color--lighter);display:block;max-height:100%;width:1.2rem}:root{--md-path-icon:url('data:image/svg+xml;charset=utf-8,')}.md-path{font-size:.7rem;margin:0 .8rem;overflow:auto;padding-top:1.2rem}.md-path:not([hidden]){display:block}@media screen and (min-width:76.25em){.md-path{margin:0 1.2rem}}.md-path__list{align-items:center;display:flex;gap:.2rem;list-style:none;margin:0;padding:0}.md-path__item:not(:first-child){display:inline-flex;gap:.2rem;white-space:nowrap}.md-path__item:not(:first-child):before{background-color:var(--md-default-fg-color--lighter);content:"";display:inline;height:.8rem;-webkit-mask-image:var(--md-path-icon);mask-image:var(--md-path-icon);width:.8rem}.md-path__link{align-items:center;color:var(--md-default-fg-color--light);display:flex}.md-path__link:focus,.md-path__link:hover{color:var(--md-accent-fg-color)}:root{--md-post-pin-icon:url('data:image/svg+xml;charset=utf-8,')}.md-post__back{border-bottom:.05rem solid var(--md-default-fg-color--lightest);margin-bottom:1.2rem;padding-bottom:1.2rem}@media screen and (max-width:76.234375em){.md-post__back{display:none}}[dir=rtl] .md-post__back svg{transform:scaleX(-1)}.md-post__authors{display:flex;flex-direction:column;gap:.6rem;margin:0 .6rem 1.2rem}.md-post .md-post__meta a{transition:color 125ms}.md-post .md-post__meta a:focus,.md-post .md-post__meta a:hover{color:var(--md-accent-fg-color)}.md-post__title{color:var(--md-default-fg-color--light);font-weight:700}.md-post--excerpt{margin-bottom:3.2rem}.md-post--excerpt .md-post__header{align-items:center;display:flex;gap:.6rem;min-height:1.6rem}.md-post--excerpt .md-post__authors{align-items:center;display:inline-flex;flex-direction:row;gap:.2rem;margin:0;min-height:2.4rem}[dir=ltr] .md-post--excerpt .md-post__meta .md-meta__list{margin-right:.4rem}[dir=rtl] .md-post--excerpt .md-post__meta .md-meta__list{margin-left:.4rem}.md-post--excerpt .md-post__content>:first-child{--md-scroll-margin:6rem;margin-top:0}.md-post>.md-nav--secondary{margin:1em 0}.md-pin{background:var(--md-default-fg-color--lightest);border-radius:1rem;margin-top:-.05rem;padding:.2rem}.md-pin:after{background-color:currentcolor;content:"";display:block;height:.6rem;margin:0 auto;-webkit-mask-image:var(--md-post-pin-icon);mask-image:var(--md-post-pin-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.6rem}.md-profile{align-items:center;display:flex;font-size:.7rem;gap:.6rem;line-height:1.4;width:100%}.md-profile__description{flex-grow:1}.md-content--post{display:flex}@media screen and (max-width:76.234375em){.md-content--post{flex-flow:column-reverse}}.md-content--post>.md-content__inner{min-width:0}@media screen and (min-width:76.25em){[dir=ltr] .md-content--post>.md-content__inner{margin-left:1.2rem}[dir=rtl] .md-content--post>.md-content__inner{margin-right:1.2rem}}@media screen and (max-width:76.234375em){.md-sidebar.md-sidebar--post{padding:0;position:static;width:100%}.md-sidebar.md-sidebar--post .md-sidebar__scrollwrap{overflow:visible}.md-sidebar.md-sidebar--post .md-sidebar__inner{padding:0}.md-sidebar.md-sidebar--post .md-post__meta{margin-left:.6rem;margin-right:.6rem}.md-sidebar.md-sidebar--post .md-nav__item{border:none;display:inline}.md-sidebar.md-sidebar--post .md-nav__list{display:inline-flex;flex-wrap:wrap;gap:.6rem;padding-bottom:.6rem;padding-top:.6rem}.md-sidebar.md-sidebar--post .md-nav__link{padding:0}.md-sidebar.md-sidebar--post .md-nav{height:auto;margin-bottom:0;position:static}}:root{--md-progress-value:0;--md-progress-delay:400ms}.md-progress{background:var(--md-primary-bg-color);height:.075rem;opacity:min(clamp(0,var(--md-progress-value),1),clamp(0,100 - var(--md-progress-value),1));position:fixed;top:0;transform:scaleX(calc(var(--md-progress-value)*1%));transform-origin:left;transition:transform .5s cubic-bezier(.19,1,.22,1),opacity .25s var(--md-progress-delay);width:100%;z-index:4}:root{--md-search-result-icon:url('data:image/svg+xml;charset=utf-8,')}.md-search{position:relative}@media screen and (min-width:60em){.md-search{padding:.2rem 0}}.no-js .md-search{display:none}.md-search__overlay{opacity:0;z-index:1}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__overlay{left:-2.2rem}[dir=rtl] .md-search__overlay{right:-2.2rem}.md-search__overlay{background-color:var(--md-default-bg-color);border-radius:1rem;height:2rem;overflow:hidden;pointer-events:none;position:absolute;top:-1rem;transform-origin:center;transition:transform .3s .1s,opacity .2s .2s;width:2rem}[data-md-toggle=search]:checked~.md-header .md-search__overlay{opacity:1;transition:transform .4s,opacity .1s}}@media screen and (min-width:60em){[dir=ltr] .md-search__overlay{left:0}[dir=rtl] .md-search__overlay{right:0}.md-search__overlay{background-color:#0000008a;cursor:pointer;height:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0}[data-md-toggle=search]:checked~.md-header .md-search__overlay{height:200vh;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@media screen and (max-width:29.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(45)}}@media screen and (min-width:30em) and (max-width:44.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(60)}}@media screen and (min-width:45em) and (max-width:59.984375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(75)}}.md-search__inner{-webkit-backface-visibility:hidden;backface-visibility:hidden}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__inner{left:0}[dir=rtl] .md-search__inner{right:0}.md-search__inner{height:0;opacity:0;overflow:hidden;position:fixed;top:0;transform:translateX(5%);transition:width 0ms .3s,height 0ms .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s;width:0;z-index:2}[dir=rtl] .md-search__inner{transform:translateX(-5%)}[data-md-toggle=search]:checked~.md-header .md-search__inner{height:100%;opacity:1;transform:translateX(0);transition:width 0ms 0ms,height 0ms 0ms,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s;width:100%}}@media screen and (min-width:60em){[dir=ltr] .md-search__inner{float:right}[dir=rtl] .md-search__inner{float:left}.md-search__inner{padding:.1rem 0;position:relative;transition:width .25s cubic-bezier(.1,.7,.1,1);width:11.7rem}}@media screen and (min-width:60em) and (max-width:76.234375em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:23.4rem}}@media screen and (min-width:76.25em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:34.4rem}}.md-search__form{background-color:var(--md-default-bg-color);box-shadow:0 0 .6rem #0000;height:2.4rem;position:relative;transition:color .25s,background-color .25s;z-index:2}@media screen and (min-width:60em){.md-search__form{background-color:#00000042;border-radius:.1rem;height:1.8rem}.md-search__form:hover{background-color:#ffffff1f}}[data-md-toggle=search]:checked~.md-header .md-search__form{background-color:var(--md-default-bg-color);border-radius:.1rem .1rem 0 0;box-shadow:0 0 .6rem #00000012;color:var(--md-default-fg-color)}[dir=ltr] .md-search__input{padding-left:3.6rem;padding-right:2.2rem}[dir=rtl] .md-search__input{padding-left:2.2rem;padding-right:3.6rem}.md-search__input{background:#0000;font-size:.9rem;height:100%;position:relative;text-overflow:ellipsis;width:100%;z-index:2}.md-search__input::placeholder{transition:color .25s}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:var(--md-default-fg-color--light)}.md-search__input::-ms-clear{display:none}@media screen and (max-width:59.984375em){.md-search__input{font-size:.9rem;height:2.4rem;width:100%}}@media screen and (min-width:60em){[dir=ltr] .md-search__input{padding-left:2.2rem}[dir=rtl] .md-search__input{padding-right:2.2rem}.md-search__input{color:inherit;font-size:.8rem}.md-search__input::placeholder{color:var(--md-primary-bg-color--light)}.md-search__input+.md-search__icon{color:var(--md-primary-bg-color)}[data-md-toggle=search]:checked~.md-header .md-search__input{text-overflow:clip}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:#0000}}.md-search__icon{cursor:pointer;display:inline-block;height:1.2rem;transition:color .25s,opacity .25s;width:1.2rem}.md-search__icon:hover{opacity:.7}[dir=ltr] .md-search__icon[for=__search]{left:.5rem}[dir=rtl] .md-search__icon[for=__search]{right:.5rem}.md-search__icon[for=__search]{position:absolute;top:.3rem;z-index:2}[dir=rtl] .md-search__icon[for=__search] svg{transform:scaleX(-1)}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__icon[for=__search]{left:.8rem}[dir=rtl] .md-search__icon[for=__search]{right:.8rem}.md-search__icon[for=__search]{top:.6rem}.md-search__icon[for=__search] svg:first-child{display:none}}@media screen and (min-width:60em){.md-search__icon[for=__search]{pointer-events:none}.md-search__icon[for=__search] svg:last-child{display:none}}[dir=ltr] .md-search__options{right:.5rem}[dir=rtl] .md-search__options{left:.5rem}.md-search__options{pointer-events:none;position:absolute;top:.3rem;z-index:2}@media screen and (max-width:59.984375em){[dir=ltr] .md-search__options{right:.8rem}[dir=rtl] .md-search__options{left:.8rem}.md-search__options{top:.6rem}}[dir=ltr] .md-search__options>.md-icon{margin-left:.2rem}[dir=rtl] .md-search__options>.md-icon{margin-right:.2rem}.md-search__options>.md-icon{color:var(--md-default-fg-color--light);opacity:0;transform:scale(.75);transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s}.md-search__options>.md-icon:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>.md-icon{opacity:1;pointer-events:auto;transform:scale(1)}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__options>.md-icon:hover{opacity:.7}[dir=ltr] .md-search__suggest{padding-left:3.6rem;padding-right:2.2rem}[dir=rtl] .md-search__suggest{padding-left:2.2rem;padding-right:3.6rem}.md-search__suggest{align-items:center;color:var(--md-default-fg-color--lighter);display:flex;font-size:.9rem;height:100%;opacity:0;position:absolute;top:0;transition:opacity 50ms;white-space:nowrap;width:100%}@media screen and (min-width:60em){[dir=ltr] .md-search__suggest{padding-left:2.2rem}[dir=rtl] .md-search__suggest{padding-right:2.2rem}.md-search__suggest{font-size:.8rem}}[data-md-toggle=search]:checked~.md-header .md-search__suggest{opacity:1;transition:opacity .3s .1s}[dir=ltr] .md-search__output{border-bottom-left-radius:.1rem}[dir=ltr] .md-search__output,[dir=rtl] .md-search__output{border-bottom-right-radius:.1rem}[dir=rtl] .md-search__output{border-bottom-left-radius:.1rem}.md-search__output{overflow:hidden;position:absolute;width:100%;z-index:1}@media screen and (max-width:59.984375em){.md-search__output{bottom:0;top:2.4rem}}@media screen and (min-width:60em){.md-search__output{opacity:0;top:1.9rem;transition:opacity .4s}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:var(--md-shadow-z3);opacity:1}}.md-search__scrollwrap{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);height:100%;overflow-y:auto;touch-action:pan-y}@media (-webkit-max-device-pixel-ratio:1),(max-resolution:1dppx){.md-search__scrollwrap{transform:translateZ(0)}}@media screen and (min-width:60em) and (max-width:76.234375em){.md-search__scrollwrap{width:23.4rem}}@media screen and (min-width:76.25em){.md-search__scrollwrap{width:34.4rem}}@media screen and (min-width:60em){.md-search__scrollwrap{max-height:0;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-search__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}}.md-search-result{color:var(--md-default-fg-color);word-break:break-word}.md-search-result__meta{background-color:var(--md-default-fg-color--lightest);color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.8rem;padding:0 .8rem;scroll-snap-align:start}@media screen and (min-width:60em){[dir=ltr] .md-search-result__meta{padding-left:2.2rem}[dir=rtl] .md-search-result__meta{padding-right:2.2rem}}.md-search-result__list{list-style:none;margin:0;padding:0;-webkit-user-select:none;user-select:none}.md-search-result__item{box-shadow:0 -.05rem var(--md-default-fg-color--lightest)}.md-search-result__item:first-child{box-shadow:none}.md-search-result__link{display:block;outline:none;scroll-snap-align:start;transition:background-color .25s}.md-search-result__link:focus,.md-search-result__link:hover{background-color:var(--md-accent-fg-color--transparent)}.md-search-result__link:last-child p:last-child{margin-bottom:.6rem}.md-search-result__more>summary{cursor:pointer;display:block;outline:none;position:sticky;scroll-snap-align:start;top:0;z-index:1}.md-search-result__more>summary::marker{display:none}.md-search-result__more>summary::-webkit-details-marker{display:none}.md-search-result__more>summary>div{color:var(--md-typeset-a-color);font-size:.64rem;padding:.75em .8rem;transition:color .25s,background-color .25s}@media screen and (min-width:60em){[dir=ltr] .md-search-result__more>summary>div{padding-left:2.2rem}[dir=rtl] .md-search-result__more>summary>div{padding-right:2.2rem}}.md-search-result__more>summary:focus>div,.md-search-result__more>summary:hover>div{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-search-result__more[open]>summary{background-color:var(--md-default-bg-color)}.md-search-result__article{overflow:hidden;padding:0 .8rem;position:relative}@media screen and (min-width:60em){[dir=ltr] .md-search-result__article{padding-left:2.2rem}[dir=rtl] .md-search-result__article{padding-right:2.2rem}}[dir=ltr] .md-search-result__icon{left:0}[dir=rtl] .md-search-result__icon{right:0}.md-search-result__icon{color:var(--md-default-fg-color--light);height:1.2rem;margin:.5rem;position:absolute;width:1.2rem}@media screen and (max-width:59.984375em){.md-search-result__icon{display:none}}.md-search-result__icon:after{background-color:currentcolor;content:"";display:inline-block;height:100%;-webkit-mask-image:var(--md-search-result-icon);mask-image:var(--md-search-result-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:100%}[dir=rtl] .md-search-result__icon:after{transform:scaleX(-1)}.md-search-result .md-typeset{color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.6}.md-search-result .md-typeset h1{color:var(--md-default-fg-color);font-size:.8rem;font-weight:400;line-height:1.4;margin:.55rem 0}.md-search-result .md-typeset h1 mark{text-decoration:none}.md-search-result .md-typeset h2{color:var(--md-default-fg-color);font-size:.64rem;font-weight:700;line-height:1.6;margin:.5em 0}.md-search-result .md-typeset h2 mark{text-decoration:none}.md-search-result__terms{color:var(--md-default-fg-color);display:block;font-size:.64rem;font-style:italic;margin:.5em 0}.md-search-result mark{background-color:initial;color:var(--md-accent-fg-color);text-decoration:underline}.md-select{position:relative;z-index:1}.md-select__inner{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);left:50%;margin-top:.2rem;max-height:0;opacity:0;position:absolute;top:calc(100% - .2rem);transform:translate3d(-50%,.3rem,0);transition:transform .25s 375ms,opacity .25s .25s,max-height 0ms .5s}.md-select:focus-within .md-select__inner,.md-select:hover .md-select__inner{max-height:10rem;opacity:1;transform:translate3d(-50%,0,0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,max-height 0ms}.md-select__inner:after{border-bottom:.2rem solid #0000;border-bottom-color:var(--md-default-bg-color);border-left:.2rem solid #0000;border-right:.2rem solid #0000;border-top:0;content:"";height:0;left:50%;margin-left:-.2rem;margin-top:-.2rem;position:absolute;top:0;width:0}.md-select__list{border-radius:.1rem;font-size:.8rem;list-style-type:none;margin:0;max-height:inherit;overflow:auto;padding:0}.md-select__item{line-height:1.8rem}[dir=ltr] .md-select__link{padding-left:.6rem;padding-right:1.2rem}[dir=rtl] .md-select__link{padding-left:1.2rem;padding-right:.6rem}.md-select__link{cursor:pointer;display:block;outline:none;scroll-snap-align:start;transition:background-color .25s,color .25s;width:100%}.md-select__link:focus,.md-select__link:hover{color:var(--md-accent-fg-color)}.md-select__link:focus{background-color:var(--md-default-fg-color--lightest)}.md-sidebar{align-self:flex-start;flex-shrink:0;padding:1.2rem 0;position:sticky;top:2.4rem;width:12.1rem}@media print{.md-sidebar{display:none}}@media screen and (max-width:76.234375em){[dir=ltr] .md-sidebar--primary{left:-12.1rem}[dir=rtl] .md-sidebar--primary{right:-12.1rem}.md-sidebar--primary{background-color:var(--md-default-bg-color);display:block;height:100%;position:fixed;top:0;transform:translateX(0);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s;width:12.1rem;z-index:5}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:var(--md-shadow-z3);transform:translateX(12.1rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{transform:translateX(-12.1rem)}.md-sidebar--primary .md-sidebar__scrollwrap{bottom:0;left:0;margin:0;overflow:hidden;position:absolute;right:0;scroll-snap-type:none;top:0}}@media screen and (min-width:76.25em){.md-sidebar{height:0}.no-js .md-sidebar{height:auto}.md-header--lifted~.md-container .md-sidebar{top:4.8rem}}.md-sidebar--secondary{display:none;order:2}@media screen and (min-width:60em){.md-sidebar--secondary{height:0}.no-js .md-sidebar--secondary{height:auto}.md-sidebar--secondary:not([hidden]){display:block}.md-sidebar--secondary .md-sidebar__scrollwrap{touch-action:pan-y}}.md-sidebar__scrollwrap{scrollbar-gutter:stable;-webkit-backface-visibility:hidden;backface-visibility:hidden;margin:0 .2rem;overflow-y:auto;scrollbar-color:var(--md-default-fg-color--lighter) #0000;scrollbar-width:thin}.md-sidebar__scrollwrap::-webkit-scrollbar{height:.2rem;width:.2rem}.md-sidebar__scrollwrap:focus-within,.md-sidebar__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) #0000}.md-sidebar__scrollwrap:focus-within::-webkit-scrollbar-thumb,.md-sidebar__scrollwrap:hover::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-sidebar__scrollwrap:focus-within::-webkit-scrollbar-thumb:hover,.md-sidebar__scrollwrap:hover::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@supports selector(::-webkit-scrollbar){.md-sidebar__scrollwrap{scrollbar-gutter:auto}[dir=ltr] .md-sidebar__inner{padding-right:calc(100% - 11.5rem)}[dir=rtl] .md-sidebar__inner{padding-left:calc(100% - 11.5rem)}}@media screen and (max-width:76.234375em){.md-overlay{background-color:#0000008a;height:0;opacity:0;position:fixed;top:0;transition:width 0ms .25s,height 0ms .25s,opacity .25s;width:0;z-index:5}[data-md-toggle=drawer]:checked~.md-overlay{height:100%;opacity:1;transition:width 0ms,height 0ms,opacity .25s;width:100%}}@keyframes facts{0%{height:0}to{height:.65rem}}@keyframes fact{0%{opacity:0;transform:translateY(100%)}50%{opacity:0}to{opacity:1;transform:translateY(0)}}:root{--md-source-forks-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-repositories-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-stars-icon:url('data:image/svg+xml;charset=utf-8,');--md-source-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-source{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:block;font-size:.65rem;line-height:1.2;outline-color:var(--md-accent-fg-color);transition:opacity .25s;white-space:nowrap}.md-source:hover{opacity:.7}.md-source__icon{display:inline-block;height:2.4rem;vertical-align:middle;width:2rem}[dir=ltr] .md-source__icon svg{margin-left:.6rem}[dir=rtl] .md-source__icon svg{margin-right:.6rem}.md-source__icon svg{margin-top:.6rem}[dir=ltr] .md-source__icon+.md-source__repository{padding-left:2rem}[dir=rtl] .md-source__icon+.md-source__repository{padding-right:2rem}[dir=ltr] .md-source__icon+.md-source__repository{margin-left:-2rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-right:-2rem}[dir=ltr] .md-source__repository{margin-left:.6rem}[dir=rtl] .md-source__repository{margin-right:.6rem}.md-source__repository{display:inline-block;max-width:calc(100% - 1.2rem);overflow:hidden;text-overflow:ellipsis;vertical-align:middle}.md-source__facts{display:flex;font-size:.55rem;gap:.4rem;list-style-type:none;margin:.1rem 0 0;opacity:.75;overflow:hidden;padding:0;width:100%}.md-source__repository--active .md-source__facts{animation:facts .25s ease-in}.md-source__fact{overflow:hidden;text-overflow:ellipsis}.md-source__repository--active .md-source__fact{animation:fact .4s ease-out}[dir=ltr] .md-source__fact:before{margin-right:.1rem}[dir=rtl] .md-source__fact:before{margin-left:.1rem}.md-source__fact:before{background-color:currentcolor;content:"";display:inline-block;height:.6rem;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-top;width:.6rem}.md-source__fact:nth-child(1n+2){flex-shrink:0}.md-source__fact--version:before{-webkit-mask-image:var(--md-source-version-icon);mask-image:var(--md-source-version-icon)}.md-source__fact--stars:before{-webkit-mask-image:var(--md-source-stars-icon);mask-image:var(--md-source-stars-icon)}.md-source__fact--forks:before{-webkit-mask-image:var(--md-source-forks-icon);mask-image:var(--md-source-forks-icon)}.md-source__fact--repositories:before{-webkit-mask-image:var(--md-source-repositories-icon);mask-image:var(--md-source-repositories-icon)}.md-source-file{margin:1em 0}[dir=ltr] .md-source-file__fact{margin-right:.6rem}[dir=rtl] .md-source-file__fact{margin-left:.6rem}.md-source-file__fact{align-items:center;color:var(--md-default-fg-color--light);display:inline-flex;font-size:.68rem;gap:.3rem}.md-source-file__fact .md-icon{flex-shrink:0;margin-bottom:.05rem}[dir=ltr] .md-source-file__fact .md-author{float:left}[dir=rtl] .md-source-file__fact .md-author{float:right}.md-source-file__fact .md-author{margin-right:.2rem}.md-source-file__fact svg{width:.9rem}:root{--md-status:url('data:image/svg+xml;charset=utf-8,');--md-status--new:url('data:image/svg+xml;charset=utf-8,');--md-status--deprecated:url('data:image/svg+xml;charset=utf-8,');--md-status--encrypted:url('data:image/svg+xml;charset=utf-8,')}.md-status:after{background-color:var(--md-default-fg-color--light);content:"";display:inline-block;height:1.125em;-webkit-mask-image:var(--md-status);mask-image:var(--md-status);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;vertical-align:text-bottom;width:1.125em}.md-status:hover:after{background-color:currentcolor}.md-status--new:after{-webkit-mask-image:var(--md-status--new);mask-image:var(--md-status--new)}.md-status--deprecated:after{-webkit-mask-image:var(--md-status--deprecated);mask-image:var(--md-status--deprecated)}.md-status--encrypted:after{-webkit-mask-image:var(--md-status--encrypted);mask-image:var(--md-status--encrypted)}.md-tabs{background-color:var(--md-primary-fg-color);color:var(--md-primary-bg-color);display:block;line-height:1.3;overflow:auto;width:100%;z-index:3}@media print{.md-tabs{display:none}}@media screen and (max-width:76.234375em){.md-tabs{display:none}}.md-tabs[hidden]{pointer-events:none}[dir=ltr] .md-tabs__list{margin-left:.2rem}[dir=rtl] .md-tabs__list{margin-right:.2rem}.md-tabs__list{contain:content;display:flex;list-style:none;margin:0;overflow:auto;padding:0;scrollbar-width:none;white-space:nowrap}.md-tabs__list::-webkit-scrollbar{display:none}.md-tabs__item{height:2.4rem;padding-left:.6rem;padding-right:.6rem}.md-tabs__item--active .md-tabs__link{color:inherit;opacity:1}.md-tabs__link{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:flex;font-size:.7rem;margin-top:.8rem;opacity:.7;outline-color:var(--md-accent-fg-color);outline-offset:.2rem;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s}.md-tabs__link:focus,.md-tabs__link:hover{color:inherit;opacity:1}[dir=ltr] .md-tabs__link svg{margin-right:.4rem}[dir=rtl] .md-tabs__link svg{margin-left:.4rem}.md-tabs__link svg{fill:currentcolor;height:1.3em}.md-tabs__item:nth-child(2) .md-tabs__link{transition-delay:20ms}.md-tabs__item:nth-child(3) .md-tabs__link{transition-delay:40ms}.md-tabs__item:nth-child(4) .md-tabs__link{transition-delay:60ms}.md-tabs__item:nth-child(5) .md-tabs__link{transition-delay:80ms}.md-tabs__item:nth-child(6) .md-tabs__link{transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{transition-delay:.3s}.md-tabs[hidden] .md-tabs__link{opacity:0;transform:translateY(50%);transition:transform 0ms .1s,opacity .1s}:root{--md-tag-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .md-tags:not([hidden]){display:inline-flex;flex-wrap:wrap;gap:.5em;margin-bottom:.75em;margin-top:-.125em}.md-typeset .md-tag{align-items:center;background:var(--md-default-fg-color--lightest);border-radius:2.4rem;display:inline-flex;font-size:.64rem;font-size:min(.8em,.64rem);font-weight:700;gap:.5em;letter-spacing:normal;line-height:1.6;padding:.3125em .78125em}.md-typeset .md-tag[href]{-webkit-tap-highlight-color:transparent;color:inherit;outline:none;transition:color 125ms,background-color 125ms}.md-typeset .md-tag[href]:focus,.md-typeset .md-tag[href]:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}[id]>.md-typeset .md-tag{vertical-align:text-top}.md-typeset .md-tag-shadow{opacity:.5}.md-typeset .md-tag-icon:before{background-color:var(--md-default-fg-color--lighter);content:"";display:inline-block;height:1.2em;-webkit-mask-image:var(--md-tag-icon);mask-image:var(--md-tag-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color 125ms;vertical-align:text-bottom;width:1.2em}.md-typeset .md-tag-icon[href]:focus:before,.md-typeset .md-tag-icon[href]:hover:before{background-color:var(--md-accent-bg-color)}@keyframes pulse{0%{transform:scale(.95)}75%{transform:scale(1)}to{transform:scale(.95)}}:root{--md-annotation-bg-icon:url('data:image/svg+xml;charset=utf-8,');--md-annotation-icon:url('data:image/svg+xml;charset=utf-8,')}.md-tooltip{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);font-family:var(--md-text-font-family);left:clamp(var(--md-tooltip-0,0rem) + .8rem,var(--md-tooltip-x),100vw + var(--md-tooltip-0,0rem) + .8rem - var(--md-tooltip-width) - 2 * .8rem);max-width:calc(100vw - 1.6rem);opacity:0;position:absolute;top:var(--md-tooltip-y);transform:translateY(-.4rem);transition:transform 0ms .25s,opacity .25s,z-index .25s;width:var(--md-tooltip-width);z-index:0}.md-tooltip--active{opacity:1;transform:translateY(0);transition:transform .25s cubic-bezier(.1,.7,.1,1),opacity .25s,z-index 0ms;z-index:2}.md-tooltip--inline{font-weight:700;-webkit-user-select:none;user-select:none;width:auto}.md-tooltip--inline:not(.md-tooltip--active){transform:translateY(.2rem) scale(.9)}.md-tooltip--inline .md-tooltip__inner{font-size:.5rem;padding:.2rem .4rem}[hidden]+.md-tooltip--inline{display:none}.focus-visible>.md-tooltip,.md-tooltip:target{outline:var(--md-accent-fg-color) auto}.md-tooltip__inner{font-size:.64rem;padding:.8rem}.md-tooltip__inner.md-typeset>:first-child{margin-top:0}.md-tooltip__inner.md-typeset>:last-child{margin-bottom:0}.md-annotation{font-style:normal;font-weight:400;outline:none;text-align:initial;vertical-align:text-bottom;white-space:normal}[dir=rtl] .md-annotation{direction:rtl}code .md-annotation{font-family:var(--md-code-font-family);font-size:inherit}.md-annotation:not([hidden]){display:inline-block;line-height:1.25}.md-annotation__index{border-radius:.01px;cursor:pointer;display:inline-block;margin-left:.4ch;margin-right:.4ch;outline:none;overflow:hidden;position:relative;-webkit-user-select:none;user-select:none;vertical-align:text-top;z-index:0}.md-annotation .md-annotation__index{transition:z-index .25s}@media screen{.md-annotation__index{width:2.2ch}[data-md-visible]>.md-annotation__index{animation:pulse 2s infinite}.md-annotation__index:before{background:var(--md-default-bg-color);-webkit-mask-image:var(--md-annotation-bg-icon);mask-image:var(--md-annotation-bg-icon)}.md-annotation__index:after,.md-annotation__index:before{content:"";height:2.2ch;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:-.1ch;width:2.2ch;z-index:-1}.md-annotation__index:after{background-color:var(--md-default-fg-color--lighter);-webkit-mask-image:var(--md-annotation-icon);mask-image:var(--md-annotation-icon);transform:scale(1.0001);transition:background-color .25s,transform .25s}.md-tooltip--active+.md-annotation__index:after{transform:rotate(45deg)}.md-tooltip--active+.md-annotation__index:after,:hover>.md-annotation__index:after{background-color:var(--md-accent-fg-color)}}.md-tooltip--active+.md-annotation__index{animation-play-state:paused;transition-duration:0ms;z-index:2}.md-annotation__index [data-md-annotation-id]{display:inline-block}@media print{.md-annotation__index [data-md-annotation-id]{background:var(--md-default-fg-color--lighter);border-radius:2ch;color:var(--md-default-bg-color);font-weight:700;padding:0 .6ch;white-space:nowrap}.md-annotation__index [data-md-annotation-id]:after{content:attr(data-md-annotation-id)}}.md-typeset .md-annotation-list{counter-reset:xxx;list-style:none}.md-typeset .md-annotation-list li{position:relative}[dir=ltr] .md-typeset .md-annotation-list li:before{left:-2.125em}[dir=rtl] .md-typeset .md-annotation-list li:before{right:-2.125em}.md-typeset .md-annotation-list li:before{background:var(--md-default-fg-color--lighter);border-radius:2ch;color:var(--md-default-bg-color);content:counter(xxx);counter-increment:xxx;font-size:.8875em;font-weight:700;height:2ch;line-height:1.25;min-width:2ch;padding:0 .6ch;position:absolute;text-align:center;top:.25em}:root{--md-tooltip-width:20rem;--md-tooltip-tail:0.3rem}.md-tooltip2{-webkit-backface-visibility:hidden;backface-visibility:hidden;color:var(--md-default-fg-color);font-family:var(--md-text-font-family);opacity:0;pointer-events:none;position:absolute;top:calc(var(--md-tooltip-host-y) + var(--md-tooltip-y));transform:translateY(-.4rem);transform-origin:calc(var(--md-tooltip-host-x) + var(--md-tooltip-x)) 0;transition:transform 0ms .25s,opacity .25s,z-index .25s;width:100%;z-index:0}.md-tooltip2:before{border-left:var(--md-tooltip-tail) solid #0000;border-right:var(--md-tooltip-tail) solid #0000;content:"";display:block;left:clamp(1.5 * .8rem,var(--md-tooltip-host-x) + var(--md-tooltip-x) - var(--md-tooltip-tail),100vw - 2 * var(--md-tooltip-tail) - 1.5 * .8rem);position:absolute;z-index:1}.md-tooltip2--top:before{border-top:var(--md-tooltip-tail) solid var(--md-default-bg-color);bottom:calc(var(--md-tooltip-tail)*-1 + .025rem);filter:drop-shadow(0 1px 0 hsla(0,0%,0%,.05))}.md-tooltip2--bottom:before{border-bottom:var(--md-tooltip-tail) solid var(--md-default-bg-color);filter:drop-shadow(0 -1px 0 hsla(0,0%,0%,.05));top:calc(var(--md-tooltip-tail)*-1 + .025rem)}.md-tooltip2--active{opacity:1;transform:translateY(0);transition:transform .4s cubic-bezier(0,1,.5,1),opacity .25s,z-index 0ms;z-index:2}.md-tooltip2__inner{scrollbar-gutter:stable;background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);left:clamp(.8rem,var(--md-tooltip-host-x) - .8rem,100vw - var(--md-tooltip-width) - .8rem);max-height:40vh;max-width:calc(100vw - 1.6rem);position:relative;scrollbar-width:thin}.md-tooltip2__inner::-webkit-scrollbar{height:.2rem;width:.2rem}.md-tooltip2__inner::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-tooltip2__inner::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}[role=dialog]>.md-tooltip2__inner{font-size:.64rem;overflow:auto;padding:0 .8rem;pointer-events:auto;width:var(--md-tooltip-width)}[role=dialog]>.md-tooltip2__inner:after,[role=dialog]>.md-tooltip2__inner:before{content:"";display:block;height:.8rem;position:sticky;width:100%;z-index:10}[role=dialog]>.md-tooltip2__inner:before{background:linear-gradient(var(--md-default-bg-color),#0000 75%);top:0}[role=dialog]>.md-tooltip2__inner:after{background:linear-gradient(#0000,var(--md-default-bg-color) 75%);bottom:0}[role=tooltip]>.md-tooltip2__inner{font-size:.5rem;font-weight:700;left:clamp(.8rem,var(--md-tooltip-host-x) + var(--md-tooltip-x) - var(--md-tooltip-width)/2,100vw - var(--md-tooltip-width) - .8rem);max-width:min(100vw - 2 * .8rem,400px);padding:.2rem .4rem;-webkit-user-select:none;user-select:none;width:-moz-fit-content;width:fit-content}.md-tooltip2__inner.md-typeset>:first-child{margin-top:0}.md-tooltip2__inner.md-typeset>:last-child{margin-bottom:0}[dir=ltr] .md-top{margin-left:50%}[dir=rtl] .md-top{margin-right:50%}.md-top{background-color:var(--md-default-bg-color);border-radius:1.6rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color--light);cursor:pointer;display:block;font-size:.7rem;outline:none;padding:.4rem .8rem;position:fixed;top:3.2rem;transform:translate(-50%);transition:color 125ms,background-color 125ms,transform 125ms cubic-bezier(.4,0,.2,1),opacity 125ms;z-index:2}@media print{.md-top{display:none}}[dir=rtl] .md-top{transform:translate(50%)}.md-top[hidden]{opacity:0;pointer-events:none;transform:translate(-50%,.2rem);transition-duration:0ms}[dir=rtl] .md-top[hidden]{transform:translate(50%,.2rem)}.md-top:focus,.md-top:hover{background-color:var(--md-accent-fg-color);color:var(--md-accent-bg-color)}.md-top svg{display:inline-block;vertical-align:-.5em}@keyframes hoverfix{0%{pointer-events:none}}:root{--md-version-icon:url('data:image/svg+xml;charset=utf-8,')}.md-version{flex-shrink:0;font-size:.8rem;height:2.4rem}[dir=ltr] .md-version__current{margin-left:1.4rem;margin-right:.4rem}[dir=rtl] .md-version__current{margin-left:.4rem;margin-right:1.4rem}.md-version__current{color:inherit;cursor:pointer;outline:none;position:relative;top:.05rem}[dir=ltr] .md-version__current:after{margin-left:.4rem}[dir=rtl] .md-version__current:after{margin-right:.4rem}.md-version__current:after{background-color:currentcolor;content:"";display:inline-block;height:.6rem;-webkit-mask-image:var(--md-version-icon);mask-image:var(--md-version-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.4rem}.md-version__alias{margin-left:.3rem;opacity:.7}.md-version__list{background-color:var(--md-default-bg-color);border-radius:.1rem;box-shadow:var(--md-shadow-z2);color:var(--md-default-fg-color);list-style-type:none;margin:.2rem .8rem;max-height:0;opacity:0;overflow:auto;padding:0;position:absolute;scroll-snap-type:y mandatory;top:.15rem;transition:max-height 0ms .5s,opacity .25s .25s;z-index:3}.md-version:focus-within .md-version__list,.md-version:hover .md-version__list{max-height:10rem;opacity:1;transition:max-height 0ms,opacity .25s}@media (hover:none),(pointer:coarse){.md-version:hover .md-version__list{animation:hoverfix .25s forwards}.md-version:focus-within .md-version__list{animation:none}}.md-version__item{line-height:1.8rem}[dir=ltr] .md-version__link{padding-left:.6rem;padding-right:1.2rem}[dir=rtl] .md-version__link{padding-left:1.2rem;padding-right:.6rem}.md-version__link{cursor:pointer;display:block;outline:none;scroll-snap-align:start;transition:color .25s,background-color .25s;white-space:nowrap;width:100%}.md-version__link:focus,.md-version__link:hover{color:var(--md-accent-fg-color)}.md-version__link:focus{background-color:var(--md-default-fg-color--lightest)}:root{--md-admonition-icon--note:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--abstract:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--info:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--tip:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--success:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--question:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--warning:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--failure:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--danger:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--bug:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--example:url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--quote:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .admonition,.md-typeset details{background-color:var(--md-admonition-bg-color);border:.075rem solid #448aff;border-radius:.2rem;box-shadow:var(--md-shadow-z1);color:var(--md-admonition-fg-color);display:flow-root;font-size:.64rem;margin:1.5625em 0;padding:0 .6rem;page-break-inside:avoid;transition:box-shadow 125ms}@media print{.md-typeset .admonition,.md-typeset details{box-shadow:none}}.md-typeset .admonition:focus-within,.md-typeset details:focus-within{box-shadow:0 0 0 .2rem #448aff1a}.md-typeset .admonition>*,.md-typeset details>*{box-sizing:border-box}.md-typeset .admonition .admonition,.md-typeset .admonition details,.md-typeset details .admonition,.md-typeset details details{margin-bottom:1em;margin-top:1em}.md-typeset .admonition .md-typeset__scrollwrap,.md-typeset details .md-typeset__scrollwrap{margin:1em -.6rem}.md-typeset .admonition .md-typeset__table,.md-typeset details .md-typeset__table{padding:0 .6rem}.md-typeset .admonition>.tabbed-set:only-child,.md-typeset details>.tabbed-set:only-child{margin-top:0}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:.6rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{padding-left:2rem;padding-right:.6rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{padding-left:.6rem;padding-right:2rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{border-left-width:.2rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-right-width:.2rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary{border-top-left-radius:.1rem}[dir=ltr] .md-typeset .admonition-title,[dir=ltr] .md-typeset summary,[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-top-right-radius:.1rem}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{border-top-left-radius:.1rem}.md-typeset .admonition-title,.md-typeset summary{background-color:#448aff1a;border:none;font-weight:700;margin:0 -.6rem;padding-bottom:.4rem;padding-top:.4rem;position:relative}html .md-typeset .admonition-title:last-child,html .md-typeset summary:last-child{margin-bottom:0}[dir=ltr] .md-typeset .admonition-title:before,[dir=ltr] .md-typeset summary:before{left:.6rem}[dir=rtl] .md-typeset .admonition-title:before,[dir=rtl] .md-typeset summary:before{right:.6rem}.md-typeset .admonition-title:before,.md-typeset summary:before{background-color:#448aff;content:"";height:1rem;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.625em;width:1rem}.md-typeset .admonition-title code,.md-typeset summary code{box-shadow:0 0 0 .05rem var(--md-default-fg-color--lightest)}.md-typeset .admonition.note,.md-typeset details.note{border-color:#448aff}.md-typeset .admonition.note:focus-within,.md-typeset details.note:focus-within{box-shadow:0 0 0 .2rem #448aff1a}.md-typeset .note>.admonition-title,.md-typeset .note>summary{background-color:#448aff1a}.md-typeset .note>.admonition-title:before,.md-typeset .note>summary:before{background-color:#448aff;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note)}.md-typeset .note>.admonition-title:after,.md-typeset .note>summary:after{color:#448aff}.md-typeset .admonition.abstract,.md-typeset details.abstract{border-color:#00b0ff}.md-typeset .admonition.abstract:focus-within,.md-typeset details.abstract:focus-within{box-shadow:0 0 0 .2rem #00b0ff1a}.md-typeset .abstract>.admonition-title,.md-typeset .abstract>summary{background-color:#00b0ff1a}.md-typeset .abstract>.admonition-title:before,.md-typeset .abstract>summary:before{background-color:#00b0ff;-webkit-mask-image:var(--md-admonition-icon--abstract);mask-image:var(--md-admonition-icon--abstract)}.md-typeset .abstract>.admonition-title:after,.md-typeset .abstract>summary:after{color:#00b0ff}.md-typeset .admonition.info,.md-typeset details.info{border-color:#00b8d4}.md-typeset .admonition.info:focus-within,.md-typeset details.info:focus-within{box-shadow:0 0 0 .2rem #00b8d41a}.md-typeset .info>.admonition-title,.md-typeset .info>summary{background-color:#00b8d41a}.md-typeset .info>.admonition-title:before,.md-typeset .info>summary:before{background-color:#00b8d4;-webkit-mask-image:var(--md-admonition-icon--info);mask-image:var(--md-admonition-icon--info)}.md-typeset .info>.admonition-title:after,.md-typeset .info>summary:after{color:#00b8d4}.md-typeset .admonition.tip,.md-typeset details.tip{border-color:#00bfa5}.md-typeset .admonition.tip:focus-within,.md-typeset details.tip:focus-within{box-shadow:0 0 0 .2rem #00bfa51a}.md-typeset .tip>.admonition-title,.md-typeset .tip>summary{background-color:#00bfa51a}.md-typeset .tip>.admonition-title:before,.md-typeset .tip>summary:before{background-color:#00bfa5;-webkit-mask-image:var(--md-admonition-icon--tip);mask-image:var(--md-admonition-icon--tip)}.md-typeset .tip>.admonition-title:after,.md-typeset .tip>summary:after{color:#00bfa5}.md-typeset .admonition.success,.md-typeset details.success{border-color:#00c853}.md-typeset .admonition.success:focus-within,.md-typeset details.success:focus-within{box-shadow:0 0 0 .2rem #00c8531a}.md-typeset .success>.admonition-title,.md-typeset .success>summary{background-color:#00c8531a}.md-typeset .success>.admonition-title:before,.md-typeset .success>summary:before{background-color:#00c853;-webkit-mask-image:var(--md-admonition-icon--success);mask-image:var(--md-admonition-icon--success)}.md-typeset .success>.admonition-title:after,.md-typeset .success>summary:after{color:#00c853}.md-typeset .admonition.question,.md-typeset details.question{border-color:#64dd17}.md-typeset .admonition.question:focus-within,.md-typeset details.question:focus-within{box-shadow:0 0 0 .2rem #64dd171a}.md-typeset .question>.admonition-title,.md-typeset .question>summary{background-color:#64dd171a}.md-typeset .question>.admonition-title:before,.md-typeset .question>summary:before{background-color:#64dd17;-webkit-mask-image:var(--md-admonition-icon--question);mask-image:var(--md-admonition-icon--question)}.md-typeset .question>.admonition-title:after,.md-typeset .question>summary:after{color:#64dd17}.md-typeset .admonition.warning,.md-typeset details.warning{border-color:#ff9100}.md-typeset .admonition.warning:focus-within,.md-typeset details.warning:focus-within{box-shadow:0 0 0 .2rem #ff91001a}.md-typeset .warning>.admonition-title,.md-typeset .warning>summary{background-color:#ff91001a}.md-typeset .warning>.admonition-title:before,.md-typeset .warning>summary:before{background-color:#ff9100;-webkit-mask-image:var(--md-admonition-icon--warning);mask-image:var(--md-admonition-icon--warning)}.md-typeset .warning>.admonition-title:after,.md-typeset .warning>summary:after{color:#ff9100}.md-typeset .admonition.failure,.md-typeset details.failure{border-color:#ff5252}.md-typeset .admonition.failure:focus-within,.md-typeset details.failure:focus-within{box-shadow:0 0 0 .2rem #ff52521a}.md-typeset .failure>.admonition-title,.md-typeset .failure>summary{background-color:#ff52521a}.md-typeset .failure>.admonition-title:before,.md-typeset .failure>summary:before{background-color:#ff5252;-webkit-mask-image:var(--md-admonition-icon--failure);mask-image:var(--md-admonition-icon--failure)}.md-typeset .failure>.admonition-title:after,.md-typeset .failure>summary:after{color:#ff5252}.md-typeset .admonition.danger,.md-typeset details.danger{border-color:#ff1744}.md-typeset .admonition.danger:focus-within,.md-typeset details.danger:focus-within{box-shadow:0 0 0 .2rem #ff17441a}.md-typeset .danger>.admonition-title,.md-typeset .danger>summary{background-color:#ff17441a}.md-typeset .danger>.admonition-title:before,.md-typeset .danger>summary:before{background-color:#ff1744;-webkit-mask-image:var(--md-admonition-icon--danger);mask-image:var(--md-admonition-icon--danger)}.md-typeset .danger>.admonition-title:after,.md-typeset .danger>summary:after{color:#ff1744}.md-typeset .admonition.bug,.md-typeset details.bug{border-color:#f50057}.md-typeset .admonition.bug:focus-within,.md-typeset details.bug:focus-within{box-shadow:0 0 0 .2rem #f500571a}.md-typeset .bug>.admonition-title,.md-typeset .bug>summary{background-color:#f500571a}.md-typeset .bug>.admonition-title:before,.md-typeset .bug>summary:before{background-color:#f50057;-webkit-mask-image:var(--md-admonition-icon--bug);mask-image:var(--md-admonition-icon--bug)}.md-typeset .bug>.admonition-title:after,.md-typeset .bug>summary:after{color:#f50057}.md-typeset .admonition.example,.md-typeset details.example{border-color:#7c4dff}.md-typeset .admonition.example:focus-within,.md-typeset details.example:focus-within{box-shadow:0 0 0 .2rem #7c4dff1a}.md-typeset .example>.admonition-title,.md-typeset .example>summary{background-color:#7c4dff1a}.md-typeset .example>.admonition-title:before,.md-typeset .example>summary:before{background-color:#7c4dff;-webkit-mask-image:var(--md-admonition-icon--example);mask-image:var(--md-admonition-icon--example)}.md-typeset .example>.admonition-title:after,.md-typeset .example>summary:after{color:#7c4dff}.md-typeset .admonition.quote,.md-typeset details.quote{border-color:#9e9e9e}.md-typeset .admonition.quote:focus-within,.md-typeset details.quote:focus-within{box-shadow:0 0 0 .2rem #9e9e9e1a}.md-typeset .quote>.admonition-title,.md-typeset .quote>summary{background-color:#9e9e9e1a}.md-typeset .quote>.admonition-title:before,.md-typeset .quote>summary:before{background-color:#9e9e9e;-webkit-mask-image:var(--md-admonition-icon--quote);mask-image:var(--md-admonition-icon--quote)}.md-typeset .quote>.admonition-title:after,.md-typeset .quote>summary:after{color:#9e9e9e}:root{--md-footnotes-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .footnote{color:var(--md-default-fg-color--light);font-size:.64rem}[dir=ltr] .md-typeset .footnote>ol{margin-left:0}[dir=rtl] .md-typeset .footnote>ol{margin-right:0}.md-typeset .footnote>ol>li{transition:color 125ms}.md-typeset .footnote>ol>li:target{color:var(--md-default-fg-color)}.md-typeset .footnote>ol>li:focus-within .footnote-backref{opacity:1;transform:translateX(0);transition:none}.md-typeset .footnote>ol>li:hover .footnote-backref,.md-typeset .footnote>ol>li:target .footnote-backref{opacity:1;transform:translateX(0)}.md-typeset .footnote>ol>li>:first-child{margin-top:0}.md-typeset .footnote-ref{font-size:.75em;font-weight:700}html .md-typeset .footnote-ref{outline-offset:.1rem}.md-typeset [id^="fnref:"]:target>.footnote-ref{outline:auto}.md-typeset .footnote-backref{color:var(--md-typeset-a-color);display:inline-block;font-size:0;opacity:0;transform:translateX(.25rem);transition:color .25s,transform .25s .25s,opacity 125ms .25s;vertical-align:text-bottom}@media print{.md-typeset .footnote-backref{color:var(--md-typeset-a-color);opacity:1;transform:translateX(0)}}[dir=rtl] .md-typeset .footnote-backref{transform:translateX(-.25rem)}.md-typeset .footnote-backref:hover{color:var(--md-accent-fg-color)}.md-typeset .footnote-backref:before{background-color:currentcolor;content:"";display:inline-block;height:.8rem;-webkit-mask-image:var(--md-footnotes-icon);mask-image:var(--md-footnotes-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;width:.8rem}[dir=rtl] .md-typeset .footnote-backref:before svg{transform:scaleX(-1)}[dir=ltr] .md-typeset .headerlink{margin-left:.5rem}[dir=rtl] .md-typeset .headerlink{margin-right:.5rem}.md-typeset .headerlink{color:var(--md-default-fg-color--lighter);display:inline-block;opacity:0;transition:color .25s,opacity 125ms}@media print{.md-typeset .headerlink{display:none}}.md-typeset .headerlink:focus,.md-typeset :hover>.headerlink,.md-typeset :target>.headerlink{opacity:1;transition:color .25s,opacity 125ms}.md-typeset .headerlink:focus,.md-typeset .headerlink:hover,.md-typeset :target>.headerlink{color:var(--md-accent-fg-color)}.md-typeset :target{--md-scroll-margin:3.6rem;--md-scroll-offset:0rem;scroll-margin-top:calc(var(--md-scroll-margin) - var(--md-scroll-offset))}@media screen and (min-width:76.25em){.md-header--lifted~.md-container .md-typeset :target{--md-scroll-margin:6rem}}.md-typeset h1:target,.md-typeset h2:target,.md-typeset h3:target{--md-scroll-offset:0.2rem}.md-typeset h4:target{--md-scroll-offset:0.15rem}.md-typeset div.arithmatex{overflow:auto}@media screen and (max-width:44.984375em){.md-typeset div.arithmatex{margin:0 -.8rem}.md-typeset div.arithmatex>*{width:min-content}}.md-typeset div.arithmatex>*{margin-left:auto!important;margin-right:auto!important;padding:0 .8rem;touch-action:auto}.md-typeset div.arithmatex>* mjx-container{margin:0!important}.md-typeset div.arithmatex mjx-assistive-mml{height:0}.md-typeset del.critic{background-color:var(--md-typeset-del-color)}.md-typeset del.critic,.md-typeset ins.critic{-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset ins.critic{background-color:var(--md-typeset-ins-color)}.md-typeset .critic.comment{-webkit-box-decoration-break:clone;box-decoration-break:clone;color:var(--md-code-hl-comment-color)}.md-typeset .critic.comment:before{content:"/* "}.md-typeset .critic.comment:after{content:" */"}.md-typeset .critic.block{box-shadow:none;display:block;margin:1em 0;overflow:auto;padding-left:.8rem;padding-right:.8rem}.md-typeset .critic.block>:first-child{margin-top:.5em}.md-typeset .critic.block>:last-child{margin-bottom:.5em}:root{--md-details-icon:url('data:image/svg+xml;charset=utf-8,')}.md-typeset details{display:flow-root;overflow:visible;padding-top:0}.md-typeset details[open]>summary:after{transform:rotate(90deg)}.md-typeset details:not([open]){box-shadow:none;padding-bottom:0}.md-typeset details:not([open])>summary{border-radius:.1rem}[dir=ltr] .md-typeset summary{padding-right:1.8rem}[dir=rtl] .md-typeset summary{padding-left:1.8rem}[dir=ltr] .md-typeset summary{border-top-left-radius:.1rem}[dir=ltr] .md-typeset summary,[dir=rtl] .md-typeset summary{border-top-right-radius:.1rem}[dir=rtl] .md-typeset summary{border-top-left-radius:.1rem}.md-typeset summary{cursor:pointer;display:block;min-height:1rem;overflow:hidden}.md-typeset summary.focus-visible{outline-color:var(--md-accent-fg-color);outline-offset:.2rem}.md-typeset summary:not(.focus-visible){-webkit-tap-highlight-color:transparent;outline:none}[dir=ltr] .md-typeset summary:after{right:.4rem}[dir=rtl] .md-typeset summary:after{left:.4rem}.md-typeset summary:after{background-color:currentcolor;content:"";height:1rem;-webkit-mask-image:var(--md-details-icon);mask-image:var(--md-details-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.625em;transform:rotate(0deg);transition:transform .25s;width:1rem}[dir=rtl] .md-typeset summary:after{transform:rotate(180deg)}.md-typeset summary::marker{display:none}.md-typeset summary::-webkit-details-marker{display:none}.md-typeset .emojione,.md-typeset .gemoji,.md-typeset .twemoji{--md-icon-size:1.125em;display:inline-flex;height:var(--md-icon-size);vertical-align:text-top}.md-typeset .emojione svg,.md-typeset .gemoji svg,.md-typeset .twemoji svg{fill:currentcolor;max-height:100%;width:var(--md-icon-size)}.md-typeset .lg,.md-typeset .xl,.md-typeset .xxl,.md-typeset .xxxl{vertical-align:text-bottom}.md-typeset .middle{vertical-align:middle}.md-typeset .lg{--md-icon-size:1.5em}.md-typeset .xl{--md-icon-size:2.25em}.md-typeset .xxl{--md-icon-size:3em}.md-typeset .xxxl{--md-icon-size:4em}.highlight .o,.highlight .ow{color:var(--md-code-hl-operator-color)}.highlight .p{color:var(--md-code-hl-punctuation-color)}.highlight .cpf,.highlight .l,.highlight .s,.highlight .s1,.highlight .s2,.highlight .sb,.highlight .sc,.highlight .si,.highlight .ss{color:var(--md-code-hl-string-color)}.highlight .cp,.highlight .se,.highlight .sh,.highlight .sr,.highlight .sx{color:var(--md-code-hl-special-color)}.highlight .il,.highlight .m,.highlight .mb,.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo{color:var(--md-code-hl-number-color)}.highlight .k,.highlight .kd,.highlight .kn,.highlight .kp,.highlight .kr,.highlight .kt{color:var(--md-code-hl-keyword-color)}.highlight .kc,.highlight .n{color:var(--md-code-hl-name-color)}.highlight .bp,.highlight .nb,.highlight .no{color:var(--md-code-hl-constant-color)}.highlight .nc,.highlight .ne,.highlight .nf,.highlight .nn{color:var(--md-code-hl-function-color)}.highlight .nd,.highlight .ni,.highlight .nl,.highlight .nt{color:var(--md-code-hl-keyword-color)}.highlight .c,.highlight .c1,.highlight .ch,.highlight .cm,.highlight .cs,.highlight .sd{color:var(--md-code-hl-comment-color)}.highlight .na,.highlight .nv,.highlight .vc,.highlight .vg,.highlight .vi{color:var(--md-code-hl-variable-color)}.highlight .ge,.highlight .gh,.highlight .go,.highlight .gp,.highlight .gr,.highlight .gs,.highlight .gt,.highlight .gu{color:var(--md-code-hl-generic-color)}.highlight .gd,.highlight .gi{border-radius:.1rem;margin:0 -.125em;padding:0 .125em}.highlight .gd{background-color:var(--md-typeset-del-color)}.highlight .gi{background-color:var(--md-typeset-ins-color)}.highlight .hll{background-color:var(--md-code-hl-color--light);box-shadow:2px 0 0 0 var(--md-code-hl-color) inset;display:block;margin:0 -1.1764705882em;padding:0 1.1764705882em}.highlight span.filename{background-color:var(--md-code-bg-color);border-bottom:.05rem solid var(--md-default-fg-color--lightest);border-top-left-radius:.1rem;border-top-right-radius:.1rem;display:flow-root;font-size:.85em;font-weight:700;margin-top:1em;padding:.6617647059em 1.1764705882em;position:relative}.highlight span.filename+pre{margin-top:0}.highlight span.filename+pre>code{border-top-left-radius:0;border-top-right-radius:0}.highlight [data-linenos]:before{background-color:var(--md-code-bg-color);box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset;color:var(--md-default-fg-color--light);content:attr(data-linenos);float:left;left:-1.1764705882em;margin-left:-1.1764705882em;margin-right:1.1764705882em;padding-left:1.1764705882em;position:sticky;-webkit-user-select:none;user-select:none;z-index:3}.highlight code a[id]{position:absolute;visibility:hidden}.highlight code[data-md-copying]{display:initial}.highlight code[data-md-copying] .hll{display:contents}.highlight code[data-md-copying] .md-annotation{display:none}.highlighttable{display:flow-root}.highlighttable tbody,.highlighttable td{display:block;padding:0}.highlighttable tr{display:flex}.highlighttable pre{margin:0}.highlighttable th.filename{flex-grow:1;padding:0;text-align:left}.highlighttable th.filename span.filename{margin-top:0}.highlighttable .linenos{background-color:var(--md-code-bg-color);border-bottom-left-radius:.1rem;border-top-left-radius:.1rem;font-size:.85em;padding:.7720588235em 0 .7720588235em 1.1764705882em;-webkit-user-select:none;user-select:none}.highlighttable .linenodiv{box-shadow:-.05rem 0 var(--md-default-fg-color--lightest) inset}.highlighttable .linenodiv pre{color:var(--md-default-fg-color--light);text-align:right}.highlighttable .linenodiv span[class]{padding-right:.5882352941em}.highlighttable .code{flex:1;min-width:0}.linenodiv a{color:inherit}.md-typeset .highlighttable{direction:ltr;margin:1em 0}.md-typeset .highlighttable>tbody>tr>.code>div>pre>code{border-bottom-left-radius:0;border-top-left-radius:0}.md-typeset .highlight+.result{border:.05rem solid var(--md-code-bg-color);border-bottom-left-radius:.1rem;border-bottom-right-radius:.1rem;border-top-width:.1rem;margin-top:-1.125em;overflow:visible;padding:0 1em}.md-typeset .highlight+.result:after{clear:both;content:"";display:block}@media screen and (max-width:44.984375em){.md-content__inner>.highlight{margin:1em -.8rem}.md-content__inner>.highlight>.filename,.md-content__inner>.highlight>.highlighttable>tbody>tr>.code>div>pre>code,.md-content__inner>.highlight>.highlighttable>tbody>tr>.filename span.filename,.md-content__inner>.highlight>.highlighttable>tbody>tr>.linenos,.md-content__inner>.highlight>pre>code{border-radius:0}.md-content__inner>.highlight+.result{border-left-width:0;border-radius:0;border-right-width:0;margin-left:-.8rem;margin-right:-.8rem}}.md-typeset .keys kbd:after,.md-typeset .keys kbd:before{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;color:inherit;margin:0;position:relative}.md-typeset .keys span{color:var(--md-default-fg-color--light);padding:0 .2em}.md-typeset .keys .key-alt:before,.md-typeset .keys .key-left-alt:before,.md-typeset .keys .key-right-alt:before{content:"⎇";padding-right:.4em}.md-typeset .keys .key-command:before,.md-typeset .keys .key-left-command:before,.md-typeset .keys .key-right-command:before{content:"⌘";padding-right:.4em}.md-typeset .keys .key-control:before,.md-typeset .keys .key-left-control:before,.md-typeset .keys .key-right-control:before{content:"⌃";padding-right:.4em}.md-typeset .keys .key-left-meta:before,.md-typeset .keys .key-meta:before,.md-typeset .keys .key-right-meta:before{content:"◆";padding-right:.4em}.md-typeset .keys .key-left-option:before,.md-typeset .keys .key-option:before,.md-typeset .keys .key-right-option:before{content:"⌥";padding-right:.4em}.md-typeset .keys .key-left-shift:before,.md-typeset .keys .key-right-shift:before,.md-typeset .keys .key-shift:before{content:"⇧";padding-right:.4em}.md-typeset .keys .key-left-super:before,.md-typeset .keys .key-right-super:before,.md-typeset .keys .key-super:before{content:"❖";padding-right:.4em}.md-typeset .keys .key-left-windows:before,.md-typeset .keys .key-right-windows:before,.md-typeset .keys .key-windows:before{content:"⊞";padding-right:.4em}.md-typeset .keys .key-arrow-down:before{content:"↓";padding-right:.4em}.md-typeset .keys .key-arrow-left:before{content:"←";padding-right:.4em}.md-typeset .keys .key-arrow-right:before{content:"→";padding-right:.4em}.md-typeset .keys .key-arrow-up:before{content:"↑";padding-right:.4em}.md-typeset .keys .key-backspace:before{content:"⌫";padding-right:.4em}.md-typeset .keys .key-backtab:before{content:"⇤";padding-right:.4em}.md-typeset .keys .key-caps-lock:before{content:"⇪";padding-right:.4em}.md-typeset .keys .key-clear:before{content:"⌧";padding-right:.4em}.md-typeset .keys .key-context-menu:before{content:"☰";padding-right:.4em}.md-typeset .keys .key-delete:before{content:"⌦";padding-right:.4em}.md-typeset .keys .key-eject:before{content:"⏏";padding-right:.4em}.md-typeset .keys .key-end:before{content:"⤓";padding-right:.4em}.md-typeset .keys .key-escape:before{content:"⎋";padding-right:.4em}.md-typeset .keys .key-home:before{content:"⤒";padding-right:.4em}.md-typeset .keys .key-insert:before{content:"⎀";padding-right:.4em}.md-typeset .keys .key-page-down:before{content:"⇟";padding-right:.4em}.md-typeset .keys .key-page-up:before{content:"⇞";padding-right:.4em}.md-typeset .keys .key-print-screen:before{content:"⎙";padding-right:.4em}.md-typeset .keys .key-tab:after{content:"⇥";padding-left:.4em}.md-typeset .keys .key-num-enter:after{content:"⌤";padding-left:.4em}.md-typeset .keys .key-enter:after{content:"⏎";padding-left:.4em}:root{--md-tabbed-icon--prev:url('data:image/svg+xml;charset=utf-8,');--md-tabbed-icon--next:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .tabbed-set{border-radius:.1rem;display:flex;flex-flow:column wrap;margin:1em 0;position:relative}.md-typeset .tabbed-set>input{height:0;opacity:0;position:absolute;width:0}.md-typeset .tabbed-set>input:target{--md-scroll-offset:0.625em}.md-typeset .tabbed-set>input.focus-visible~.tabbed-labels:before{background-color:var(--md-accent-fg-color)}.md-typeset .tabbed-labels{-ms-overflow-style:none;box-shadow:0 -.05rem var(--md-default-fg-color--lightest) inset;display:flex;max-width:100%;overflow:auto;scrollbar-width:none}@media print{.md-typeset .tabbed-labels{display:contents}}@media screen{.js .md-typeset .tabbed-labels{position:relative}.js .md-typeset .tabbed-labels:before{background:var(--md-default-fg-color);bottom:0;content:"";display:block;height:2px;left:0;position:absolute;transform:translateX(var(--md-indicator-x));transition:width 225ms,background-color .25s,transform .25s;transition-timing-function:cubic-bezier(.4,0,.2,1);width:var(--md-indicator-width)}}.md-typeset .tabbed-labels::-webkit-scrollbar{display:none}.md-typeset .tabbed-labels>label{border-bottom:.1rem solid #0000;border-radius:.1rem .1rem 0 0;color:var(--md-default-fg-color--light);cursor:pointer;flex-shrink:0;font-size:.64rem;font-weight:700;padding:.78125em 1.25em .625em;scroll-margin-inline-start:1rem;transition:background-color .25s,color .25s;white-space:nowrap;width:auto}@media print{.md-typeset .tabbed-labels>label:first-child{order:1}.md-typeset .tabbed-labels>label:nth-child(2){order:2}.md-typeset .tabbed-labels>label:nth-child(3){order:3}.md-typeset .tabbed-labels>label:nth-child(4){order:4}.md-typeset .tabbed-labels>label:nth-child(5){order:5}.md-typeset .tabbed-labels>label:nth-child(6){order:6}.md-typeset .tabbed-labels>label:nth-child(7){order:7}.md-typeset .tabbed-labels>label:nth-child(8){order:8}.md-typeset .tabbed-labels>label:nth-child(9){order:9}.md-typeset .tabbed-labels>label:nth-child(10){order:10}.md-typeset .tabbed-labels>label:nth-child(11){order:11}.md-typeset .tabbed-labels>label:nth-child(12){order:12}.md-typeset .tabbed-labels>label:nth-child(13){order:13}.md-typeset .tabbed-labels>label:nth-child(14){order:14}.md-typeset .tabbed-labels>label:nth-child(15){order:15}.md-typeset .tabbed-labels>label:nth-child(16){order:16}.md-typeset .tabbed-labels>label:nth-child(17){order:17}.md-typeset .tabbed-labels>label:nth-child(18){order:18}.md-typeset .tabbed-labels>label:nth-child(19){order:19}.md-typeset .tabbed-labels>label:nth-child(20){order:20}}.md-typeset .tabbed-labels>label:hover{color:var(--md-default-fg-color)}.md-typeset .tabbed-labels>label>[href]:first-child{color:inherit}.md-typeset .tabbed-labels--linked>label{padding:0}.md-typeset .tabbed-labels--linked>label>a{display:block;padding:.78125em 1.25em .625em}.md-typeset .tabbed-content{width:100%}@media print{.md-typeset .tabbed-content{display:contents}}.md-typeset .tabbed-block{display:none}@media print{.md-typeset .tabbed-block{display:block}.md-typeset .tabbed-block:first-child{order:1}.md-typeset .tabbed-block:nth-child(2){order:2}.md-typeset .tabbed-block:nth-child(3){order:3}.md-typeset .tabbed-block:nth-child(4){order:4}.md-typeset .tabbed-block:nth-child(5){order:5}.md-typeset .tabbed-block:nth-child(6){order:6}.md-typeset .tabbed-block:nth-child(7){order:7}.md-typeset .tabbed-block:nth-child(8){order:8}.md-typeset .tabbed-block:nth-child(9){order:9}.md-typeset .tabbed-block:nth-child(10){order:10}.md-typeset .tabbed-block:nth-child(11){order:11}.md-typeset .tabbed-block:nth-child(12){order:12}.md-typeset .tabbed-block:nth-child(13){order:13}.md-typeset .tabbed-block:nth-child(14){order:14}.md-typeset .tabbed-block:nth-child(15){order:15}.md-typeset .tabbed-block:nth-child(16){order:16}.md-typeset .tabbed-block:nth-child(17){order:17}.md-typeset .tabbed-block:nth-child(18){order:18}.md-typeset .tabbed-block:nth-child(19){order:19}.md-typeset .tabbed-block:nth-child(20){order:20}}.md-typeset .tabbed-block>.highlight:first-child>pre,.md-typeset .tabbed-block>pre:first-child{margin:0}.md-typeset .tabbed-block>.highlight:first-child>pre>code,.md-typeset .tabbed-block>pre:first-child>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-block>.highlight:first-child>.filename{border-top-left-radius:0;border-top-right-radius:0;margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable{margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.filename span.filename,.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.linenos{border-top-left-radius:0;border-top-right-radius:0;margin:0}.md-typeset .tabbed-block>.highlight:first-child>.highlighttable>tbody>tr>.code>div>pre>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-block>.highlight:first-child+.result{margin-top:-.125em}.md-typeset .tabbed-block>.tabbed-set{margin:0}.md-typeset .tabbed-button{align-self:center;border-radius:100%;color:var(--md-default-fg-color--light);cursor:pointer;display:block;height:.9rem;margin-top:.1rem;pointer-events:auto;transition:background-color .25s;width:.9rem}.md-typeset .tabbed-button:hover{background-color:var(--md-accent-fg-color--transparent);color:var(--md-accent-fg-color)}.md-typeset .tabbed-button:after{background-color:currentcolor;content:"";display:block;height:100%;-webkit-mask-image:var(--md-tabbed-icon--prev);mask-image:var(--md-tabbed-icon--prev);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;transition:background-color .25s,transform .25s;width:100%}.md-typeset .tabbed-control{background:linear-gradient(to right,var(--md-default-bg-color) 60%,#0000);display:flex;height:1.9rem;justify-content:start;pointer-events:none;position:absolute;transition:opacity 125ms;width:1.2rem}[dir=rtl] .md-typeset .tabbed-control{transform:rotate(180deg)}.md-typeset .tabbed-control[hidden]{opacity:0}.md-typeset .tabbed-control--next{background:linear-gradient(to left,var(--md-default-bg-color) 60%,#0000);justify-content:end;right:0}.md-typeset .tabbed-control--next .tabbed-button:after{-webkit-mask-image:var(--md-tabbed-icon--next);mask-image:var(--md-tabbed-icon--next)}@media screen and (max-width:44.984375em){[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels{padding-left:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels{padding-right:.8rem}.md-content__inner>.tabbed-set .tabbed-labels{margin:0 -.8rem;max-width:100vw;scroll-padding-inline-start:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels:after{padding-right:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels:after{padding-left:.8rem}.md-content__inner>.tabbed-set .tabbed-labels:after{content:""}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{padding-left:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{padding-right:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{margin-left:-.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{margin-right:-.8rem}.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev{width:2rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{padding-right:.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{padding-left:.8rem}[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{margin-right:-.8rem}[dir=rtl] .md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{margin-left:-.8rem}.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next{width:2rem}}@media screen{.md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9){color:var(--md-default-fg-color)}.md-typeset .no-js .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset .no-js .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset .no-js .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset .no-js .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset .no-js .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset .no-js .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset .no-js .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset .no-js .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset .no-js .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset .no-js .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset .no-js .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset .no-js .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset .no-js .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset .no-js .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset .no-js .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset .no-js .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset .no-js .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset .no-js .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset .no-js .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset .no-js .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9),.md-typeset [role=dialog] .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.md-typeset [role=dialog] .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.md-typeset [role=dialog] .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.md-typeset [role=dialog] .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.md-typeset [role=dialog] .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.md-typeset [role=dialog] .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.md-typeset [role=dialog] .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.md-typeset [role=dialog] .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.md-typeset [role=dialog] .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.md-typeset [role=dialog] .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.md-typeset [role=dialog] .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.md-typeset [role=dialog] .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.md-typeset [role=dialog] .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.md-typeset [role=dialog] .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.md-typeset [role=dialog] .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.md-typeset [role=dialog] .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.md-typeset [role=dialog] .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.md-typeset [role=dialog] .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.md-typeset [role=dialog] .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.md-typeset [role=dialog] .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9),.no-js .md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,.no-js .md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),.no-js .md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),.no-js .md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),.no-js .md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),.no-js .md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),.no-js .md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),.no-js .md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),.no-js .md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),.no-js .md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),.no-js .md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),.no-js .md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),.no-js .md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),.no-js .md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),.no-js .md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),.no-js .md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),.no-js .md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),.no-js .md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),.no-js .md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),.no-js .md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9),[role=dialog] .md-typeset .tabbed-set>input:first-child:checked~.tabbed-labels>:first-child,[role=dialog] .md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),[role=dialog] .md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),[role=dialog] .md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),[role=dialog] .md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),[role=dialog] .md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),[role=dialog] .md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),[role=dialog] .md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),[role=dialog] .md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),[role=dialog] .md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),[role=dialog] .md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),[role=dialog] .md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),[role=dialog] .md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20),[role=dialog] .md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),[role=dialog] .md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),[role=dialog] .md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),[role=dialog] .md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),[role=dialog] .md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),[role=dialog] .md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),[role=dialog] .md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9){border-color:var(--md-default-fg-color)}}.md-typeset .tabbed-set>input:first-child.focus-visible~.tabbed-labels>:first-child,.md-typeset .tabbed-set>input:nth-child(10).focus-visible~.tabbed-labels>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11).focus-visible~.tabbed-labels>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12).focus-visible~.tabbed-labels>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13).focus-visible~.tabbed-labels>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14).focus-visible~.tabbed-labels>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15).focus-visible~.tabbed-labels>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16).focus-visible~.tabbed-labels>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17).focus-visible~.tabbed-labels>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18).focus-visible~.tabbed-labels>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19).focus-visible~.tabbed-labels>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2).focus-visible~.tabbed-labels>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20).focus-visible~.tabbed-labels>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3).focus-visible~.tabbed-labels>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4).focus-visible~.tabbed-labels>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5).focus-visible~.tabbed-labels>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6).focus-visible~.tabbed-labels>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7).focus-visible~.tabbed-labels>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8).focus-visible~.tabbed-labels>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9).focus-visible~.tabbed-labels>:nth-child(9){color:var(--md-accent-fg-color)}.md-typeset .tabbed-set>input:first-child:checked~.tabbed-content>:first-child,.md-typeset .tabbed-set>input:nth-child(10):checked~.tabbed-content>:nth-child(10),.md-typeset .tabbed-set>input:nth-child(11):checked~.tabbed-content>:nth-child(11),.md-typeset .tabbed-set>input:nth-child(12):checked~.tabbed-content>:nth-child(12),.md-typeset .tabbed-set>input:nth-child(13):checked~.tabbed-content>:nth-child(13),.md-typeset .tabbed-set>input:nth-child(14):checked~.tabbed-content>:nth-child(14),.md-typeset .tabbed-set>input:nth-child(15):checked~.tabbed-content>:nth-child(15),.md-typeset .tabbed-set>input:nth-child(16):checked~.tabbed-content>:nth-child(16),.md-typeset .tabbed-set>input:nth-child(17):checked~.tabbed-content>:nth-child(17),.md-typeset .tabbed-set>input:nth-child(18):checked~.tabbed-content>:nth-child(18),.md-typeset .tabbed-set>input:nth-child(19):checked~.tabbed-content>:nth-child(19),.md-typeset .tabbed-set>input:nth-child(2):checked~.tabbed-content>:nth-child(2),.md-typeset .tabbed-set>input:nth-child(20):checked~.tabbed-content>:nth-child(20),.md-typeset .tabbed-set>input:nth-child(3):checked~.tabbed-content>:nth-child(3),.md-typeset .tabbed-set>input:nth-child(4):checked~.tabbed-content>:nth-child(4),.md-typeset .tabbed-set>input:nth-child(5):checked~.tabbed-content>:nth-child(5),.md-typeset .tabbed-set>input:nth-child(6):checked~.tabbed-content>:nth-child(6),.md-typeset .tabbed-set>input:nth-child(7):checked~.tabbed-content>:nth-child(7),.md-typeset .tabbed-set>input:nth-child(8):checked~.tabbed-content>:nth-child(8),.md-typeset .tabbed-set>input:nth-child(9):checked~.tabbed-content>:nth-child(9){display:block}:root{--md-tasklist-icon:url('data:image/svg+xml;charset=utf-8,');--md-tasklist-icon--checked:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .task-list-item{list-style-type:none;position:relative}[dir=ltr] .md-typeset .task-list-item [type=checkbox]{left:-2em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{right:-2em}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em}.md-typeset .task-list-control [type=checkbox]{opacity:0;z-index:-1}[dir=ltr] .md-typeset .task-list-indicator:before{left:-1.5em}[dir=rtl] .md-typeset .task-list-indicator:before{right:-1.5em}.md-typeset .task-list-indicator:before{background-color:var(--md-default-fg-color--lightest);content:"";height:1.25em;-webkit-mask-image:var(--md-tasklist-icon);mask-image:var(--md-tasklist-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;top:.15em;width:1.25em}.md-typeset [type=checkbox]:checked+.task-list-indicator:before{background-color:#00e676;-webkit-mask-image:var(--md-tasklist-icon--checked);mask-image:var(--md-tasklist-icon--checked)}@media print{.giscus,[id=__comments]{display:none}}:root>*{--md-mermaid-font-family:var(--md-text-font-family),sans-serif;--md-mermaid-edge-color:var(--md-code-fg-color);--md-mermaid-node-bg-color:var(--md-accent-fg-color--transparent);--md-mermaid-node-fg-color:var(--md-accent-fg-color);--md-mermaid-label-bg-color:var(--md-default-bg-color);--md-mermaid-label-fg-color:var(--md-code-fg-color);--md-mermaid-sequence-actor-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-actor-fg-color:var(--md-mermaid-label-fg-color);--md-mermaid-sequence-actor-border-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-actor-line-color:var(--md-default-fg-color--lighter);--md-mermaid-sequence-actorman-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-actorman-line-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-box-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-box-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-label-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-label-fg-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-loop-bg-color:var(--md-mermaid-node-bg-color);--md-mermaid-sequence-loop-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-loop-border-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-message-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-message-line-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-note-bg-color:var(--md-mermaid-label-bg-color);--md-mermaid-sequence-note-fg-color:var(--md-mermaid-edge-color);--md-mermaid-sequence-note-border-color:var(--md-mermaid-label-fg-color);--md-mermaid-sequence-number-bg-color:var(--md-mermaid-node-fg-color);--md-mermaid-sequence-number-fg-color:var(--md-accent-bg-color)}.mermaid{line-height:normal;margin:1em 0}.md-typeset .grid{grid-gap:.4rem;display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,16rem),1fr));margin:1em 0}.md-typeset .grid.cards>ol,.md-typeset .grid.cards>ul{display:contents}.md-typeset .grid.cards>ol>li,.md-typeset .grid.cards>ul>li,.md-typeset .grid>.card{border:.05rem solid var(--md-default-fg-color--lightest);border-radius:.1rem;display:block;margin:0;padding:.8rem;transition:border .25s,box-shadow .25s}.md-typeset .grid.cards>ol>li:focus-within,.md-typeset .grid.cards>ol>li:hover,.md-typeset .grid.cards>ul>li:focus-within,.md-typeset .grid.cards>ul>li:hover,.md-typeset .grid>.card:focus-within,.md-typeset .grid>.card:hover{border-color:#0000;box-shadow:var(--md-shadow-z2)}.md-typeset .grid.cards>ol>li>hr,.md-typeset .grid.cards>ul>li>hr,.md-typeset .grid>.card>hr{margin-bottom:1em;margin-top:1em}.md-typeset .grid.cards>ol>li>:first-child,.md-typeset .grid.cards>ul>li>:first-child,.md-typeset .grid>.card>:first-child{margin-top:0}.md-typeset .grid.cards>ol>li>:last-child,.md-typeset .grid.cards>ul>li>:last-child,.md-typeset .grid>.card>:last-child{margin-bottom:0}.md-typeset .grid>*,.md-typeset .grid>.admonition,.md-typeset .grid>.highlight>*,.md-typeset .grid>.highlighttable,.md-typeset .grid>.md-typeset details,.md-typeset .grid>details,.md-typeset .grid>pre{margin-bottom:0;margin-top:0}.md-typeset .grid>.highlight>pre:only-child,.md-typeset .grid>.highlight>pre>code,.md-typeset .grid>.highlighttable,.md-typeset .grid>.highlighttable>tbody,.md-typeset .grid>.highlighttable>tbody>tr,.md-typeset .grid>.highlighttable>tbody>tr>.code,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight>pre,.md-typeset .grid>.highlighttable>tbody>tr>.code>.highlight>pre>code{height:100%}.md-typeset .grid>.tabbed-set{margin-bottom:0;margin-top:0}@media screen and (min-width:45em){[dir=ltr] .md-typeset .inline{float:left}[dir=rtl] .md-typeset .inline{float:right}[dir=ltr] .md-typeset .inline{margin-right:.8rem}[dir=rtl] .md-typeset .inline{margin-left:.8rem}.md-typeset .inline{margin-bottom:.8rem;margin-top:0;width:11.7rem}[dir=ltr] .md-typeset .inline.end{float:right}[dir=rtl] .md-typeset .inline.end{float:left}[dir=ltr] .md-typeset .inline.end{margin-left:.8rem;margin-right:0}[dir=rtl] .md-typeset .inline.end{margin-left:0;margin-right:.8rem}} \ No newline at end of file diff --git a/assets/stylesheets/palette.ab4e12ef.min.css b/assets/stylesheets/palette.ab4e12ef.min.css new file mode 100644 index 0000000000..75aaf84253 --- /dev/null +++ b/assets/stylesheets/palette.ab4e12ef.min.css @@ -0,0 +1 @@ +@media screen{[data-md-color-scheme=slate]{--md-default-fg-color:hsla(var(--md-hue),15%,90%,0.82);--md-default-fg-color--light:hsla(var(--md-hue),15%,90%,0.56);--md-default-fg-color--lighter:hsla(var(--md-hue),15%,90%,0.32);--md-default-fg-color--lightest:hsla(var(--md-hue),15%,90%,0.12);--md-default-bg-color:hsla(var(--md-hue),15%,14%,1);--md-default-bg-color--light:hsla(var(--md-hue),15%,14%,0.54);--md-default-bg-color--lighter:hsla(var(--md-hue),15%,14%,0.26);--md-default-bg-color--lightest:hsla(var(--md-hue),15%,14%,0.07);--md-code-fg-color:hsla(var(--md-hue),18%,86%,0.82);--md-code-bg-color:hsla(var(--md-hue),15%,18%,1);--md-code-bg-color--light:hsla(var(--md-hue),15%,18%,0.9);--md-code-bg-color--lighter:hsla(var(--md-hue),15%,18%,0.54);--md-code-hl-color:#2977ff;--md-code-hl-color--light:#2977ff1a;--md-code-hl-number-color:#e6695b;--md-code-hl-special-color:#f06090;--md-code-hl-function-color:#c973d9;--md-code-hl-constant-color:#9383e2;--md-code-hl-keyword-color:#6791e0;--md-code-hl-string-color:#2fb170;--md-code-hl-name-color:var(--md-code-fg-color);--md-code-hl-operator-color:var(--md-default-fg-color--light);--md-code-hl-punctuation-color:var(--md-default-fg-color--light);--md-code-hl-comment-color:var(--md-default-fg-color--light);--md-code-hl-generic-color:var(--md-default-fg-color--light);--md-code-hl-variable-color:var(--md-default-fg-color--light);--md-typeset-color:var(--md-default-fg-color);--md-typeset-a-color:var(--md-primary-fg-color);--md-typeset-kbd-color:hsla(var(--md-hue),15%,90%,0.12);--md-typeset-kbd-accent-color:hsla(var(--md-hue),15%,90%,0.2);--md-typeset-kbd-border-color:hsla(var(--md-hue),15%,14%,1);--md-typeset-mark-color:#4287ff4d;--md-typeset-table-color:hsla(var(--md-hue),15%,95%,0.12);--md-typeset-table-color--light:hsla(var(--md-hue),15%,95%,0.035);--md-admonition-fg-color:var(--md-default-fg-color);--md-admonition-bg-color:var(--md-default-bg-color);--md-footer-bg-color:hsla(var(--md-hue),15%,10%,0.87);--md-footer-bg-color--dark:hsla(var(--md-hue),15%,8%,1);--md-shadow-z1:0 0.2rem 0.5rem #0000000d,0 0 0.05rem #0000001a;--md-shadow-z2:0 0.2rem 0.5rem #00000040,0 0 0.05rem #00000040;--md-shadow-z3:0 0.2rem 0.5rem #0006,0 0 0.05rem #00000059;color-scheme:dark}[data-md-color-scheme=slate] img[src$="#gh-light-mode-only"],[data-md-color-scheme=slate] img[src$="#only-light"]{display:none}[data-md-color-scheme=slate][data-md-color-primary=pink]{--md-typeset-a-color:#ed5487}[data-md-color-scheme=slate][data-md-color-primary=purple]{--md-typeset-a-color:#c46fd3}[data-md-color-scheme=slate][data-md-color-primary=deep-purple]{--md-typeset-a-color:#a47bea}[data-md-color-scheme=slate][data-md-color-primary=indigo]{--md-typeset-a-color:#5488e8}[data-md-color-scheme=slate][data-md-color-primary=teal]{--md-typeset-a-color:#00ccb8}[data-md-color-scheme=slate][data-md-color-primary=green]{--md-typeset-a-color:#71c174}[data-md-color-scheme=slate][data-md-color-primary=deep-orange]{--md-typeset-a-color:#ff764d}[data-md-color-scheme=slate][data-md-color-primary=brown]{--md-typeset-a-color:#c1775c}[data-md-color-scheme=slate][data-md-color-primary=black],[data-md-color-scheme=slate][data-md-color-primary=blue-grey],[data-md-color-scheme=slate][data-md-color-primary=grey],[data-md-color-scheme=slate][data-md-color-primary=white]{--md-typeset-a-color:#5e8bde}[data-md-color-switching] *,[data-md-color-switching] :after,[data-md-color-switching] :before{transition-duration:0ms!important}}[data-md-color-accent=red]{--md-accent-fg-color:#ff1947;--md-accent-fg-color--transparent:#ff19471a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=pink]{--md-accent-fg-color:#f50056;--md-accent-fg-color--transparent:#f500561a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=purple]{--md-accent-fg-color:#df41fb;--md-accent-fg-color--transparent:#df41fb1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=deep-purple]{--md-accent-fg-color:#7c4dff;--md-accent-fg-color--transparent:#7c4dff1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=indigo]{--md-accent-fg-color:#526cfe;--md-accent-fg-color--transparent:#526cfe1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=blue]{--md-accent-fg-color:#4287ff;--md-accent-fg-color--transparent:#4287ff1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=light-blue]{--md-accent-fg-color:#0091eb;--md-accent-fg-color--transparent:#0091eb1a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=cyan]{--md-accent-fg-color:#00bad6;--md-accent-fg-color--transparent:#00bad61a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=teal]{--md-accent-fg-color:#00bda4;--md-accent-fg-color--transparent:#00bda41a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=green]{--md-accent-fg-color:#00c753;--md-accent-fg-color--transparent:#00c7531a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=light-green]{--md-accent-fg-color:#63de17;--md-accent-fg-color--transparent:#63de171a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-accent=lime]{--md-accent-fg-color:#b0eb00;--md-accent-fg-color--transparent:#b0eb001a;--md-accent-bg-color:#000000de;--md-accent-bg-color--light:#0000008a}[data-md-color-accent=yellow]{--md-accent-fg-color:#ffd500;--md-accent-fg-color--transparent:#ffd5001a;--md-accent-bg-color:#000000de;--md-accent-bg-color--light:#0000008a}[data-md-color-accent=amber]{--md-accent-fg-color:#fa0;--md-accent-fg-color--transparent:#ffaa001a;--md-accent-bg-color:#000000de;--md-accent-bg-color--light:#0000008a}[data-md-color-accent=orange]{--md-accent-fg-color:#ff9100;--md-accent-fg-color--transparent:#ff91001a;--md-accent-bg-color:#000000de;--md-accent-bg-color--light:#0000008a}[data-md-color-accent=deep-orange]{--md-accent-fg-color:#ff6e42;--md-accent-fg-color--transparent:#ff6e421a;--md-accent-bg-color:#fff;--md-accent-bg-color--light:#ffffffb3}[data-md-color-primary=red]{--md-primary-fg-color:#ef5552;--md-primary-fg-color--light:#e57171;--md-primary-fg-color--dark:#e53734;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=pink]{--md-primary-fg-color:#e92063;--md-primary-fg-color--light:#ec417a;--md-primary-fg-color--dark:#c3185d;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=purple]{--md-primary-fg-color:#ab47bd;--md-primary-fg-color--light:#bb69c9;--md-primary-fg-color--dark:#8c24a8;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=deep-purple]{--md-primary-fg-color:#7e56c2;--md-primary-fg-color--light:#9574cd;--md-primary-fg-color--dark:#673ab6;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=indigo]{--md-primary-fg-color:#4051b5;--md-primary-fg-color--light:#5d6cc0;--md-primary-fg-color--dark:#303fa1;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=blue]{--md-primary-fg-color:#2094f3;--md-primary-fg-color--light:#42a5f5;--md-primary-fg-color--dark:#1975d2;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=light-blue]{--md-primary-fg-color:#02a6f2;--md-primary-fg-color--light:#28b5f6;--md-primary-fg-color--dark:#0287cf;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=cyan]{--md-primary-fg-color:#00bdd6;--md-primary-fg-color--light:#25c5da;--md-primary-fg-color--dark:#0097a8;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=teal]{--md-primary-fg-color:#009485;--md-primary-fg-color--light:#26a699;--md-primary-fg-color--dark:#007a6c;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=green]{--md-primary-fg-color:#4cae4f;--md-primary-fg-color--light:#68bb6c;--md-primary-fg-color--dark:#398e3d;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=light-green]{--md-primary-fg-color:#8bc34b;--md-primary-fg-color--light:#9ccc66;--md-primary-fg-color--dark:#689f38;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=lime]{--md-primary-fg-color:#cbdc38;--md-primary-fg-color--light:#d3e156;--md-primary-fg-color--dark:#b0b52c;--md-primary-bg-color:#000000de;--md-primary-bg-color--light:#0000008a}[data-md-color-primary=yellow]{--md-primary-fg-color:#ffec3d;--md-primary-fg-color--light:#ffee57;--md-primary-fg-color--dark:#fbc02d;--md-primary-bg-color:#000000de;--md-primary-bg-color--light:#0000008a}[data-md-color-primary=amber]{--md-primary-fg-color:#ffc105;--md-primary-fg-color--light:#ffc929;--md-primary-fg-color--dark:#ffa200;--md-primary-bg-color:#000000de;--md-primary-bg-color--light:#0000008a}[data-md-color-primary=orange]{--md-primary-fg-color:#ffa724;--md-primary-fg-color--light:#ffa724;--md-primary-fg-color--dark:#fa8900;--md-primary-bg-color:#000000de;--md-primary-bg-color--light:#0000008a}[data-md-color-primary=deep-orange]{--md-primary-fg-color:#ff6e42;--md-primary-fg-color--light:#ff8a66;--md-primary-fg-color--dark:#f4511f;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=brown]{--md-primary-fg-color:#795649;--md-primary-fg-color--light:#8d6e62;--md-primary-fg-color--dark:#5d4037;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3}[data-md-color-primary=grey]{--md-primary-fg-color:#757575;--md-primary-fg-color--light:#9e9e9e;--md-primary-fg-color--dark:#616161;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3;--md-typeset-a-color:#4051b5}[data-md-color-primary=blue-grey]{--md-primary-fg-color:#546d78;--md-primary-fg-color--light:#607c8a;--md-primary-fg-color--dark:#455a63;--md-primary-bg-color:#fff;--md-primary-bg-color--light:#ffffffb3;--md-typeset-a-color:#4051b5}[data-md-color-primary=light-green]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#72ad2e}[data-md-color-primary=lime]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#8b990a}[data-md-color-primary=yellow]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#b8a500}[data-md-color-primary=amber]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#d19d00}[data-md-color-primary=orange]:not([data-md-color-scheme=slate]){--md-typeset-a-color:#e68a00}[data-md-color-primary=white]{--md-primary-fg-color:hsla(var(--md-hue),0%,100%,1);--md-primary-fg-color--light:hsla(var(--md-hue),0%,100%,0.7);--md-primary-fg-color--dark:hsla(var(--md-hue),0%,0%,0.07);--md-primary-bg-color:hsla(var(--md-hue),0%,0%,0.87);--md-primary-bg-color--light:hsla(var(--md-hue),0%,0%,0.54);--md-typeset-a-color:#4051b5}[data-md-color-primary=white] .md-button{color:var(--md-typeset-a-color)}[data-md-color-primary=white] .md-button--primary{background-color:var(--md-typeset-a-color);border-color:var(--md-typeset-a-color);color:hsla(var(--md-hue),0%,100%,1)}@media screen and (min-width:60em){[data-md-color-primary=white] .md-search__form{background-color:hsla(var(--md-hue),0%,0%,.07)}[data-md-color-primary=white] .md-search__form:hover{background-color:hsla(var(--md-hue),0%,0%,.32)}[data-md-color-primary=white] .md-search__input+.md-search__icon{color:hsla(var(--md-hue),0%,0%,.87)}}@media screen and (min-width:76.25em){[data-md-color-primary=white] .md-tabs{border-bottom:.05rem solid #00000012}}[data-md-color-primary=black]{--md-primary-fg-color:hsla(var(--md-hue),15%,9%,1);--md-primary-fg-color--light:hsla(var(--md-hue),15%,9%,0.54);--md-primary-fg-color--dark:hsla(var(--md-hue),15%,9%,1);--md-primary-bg-color:hsla(var(--md-hue),15%,100%,1);--md-primary-bg-color--light:hsla(var(--md-hue),15%,100%,0.7);--md-typeset-a-color:#4051b5}[data-md-color-primary=black] .md-button{color:var(--md-typeset-a-color)}[data-md-color-primary=black] .md-button--primary{background-color:var(--md-typeset-a-color);border-color:var(--md-typeset-a-color);color:hsla(var(--md-hue),0%,100%,1)}[data-md-color-primary=black] .md-header{background-color:hsla(var(--md-hue),15%,9%,1)}@media screen and (max-width:59.984375em){[data-md-color-primary=black] .md-nav__source{background-color:hsla(var(--md-hue),15%,11%,.87)}}@media screen and (max-width:76.234375em){html [data-md-color-primary=black] .md-nav--primary .md-nav__title[for=__drawer]{background-color:hsla(var(--md-hue),15%,9%,1)}}@media screen and (min-width:76.25em){[data-md-color-primary=black] .md-tabs{background-color:hsla(var(--md-hue),15%,9%,1)}} \ No newline at end of file diff --git a/code-of-conduct/index.html b/code-of-conduct/index.html new file mode 100644 index 0000000000..6df7026d72 --- /dev/null +++ b/code-of-conduct/index.html @@ -0,0 +1,15 @@ + + + + + + Redirecting... + + + + + + +Redirecting... + + diff --git a/credits/index.html b/credits/index.html new file mode 100644 index 0000000000..f77c3b413d --- /dev/null +++ b/credits/index.html @@ -0,0 +1,7642 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Credits - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Credits

    +

    A big thank you to all current and past sponsors, whose generous support has been and continues to be essential to the success of the project!

    +

    Infrastructure

    +

    Our project infrastructure is provided by the following companies:

    + +

    Technologies

    + +

    Artwork

    + +

    Thank You

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/css/custom.css b/css/custom.css new file mode 100644 index 0000000000..703dc1f043 --- /dev/null +++ b/css/custom.css @@ -0,0 +1,1005 @@ +:root { + --md-primary-fg-color: #fff; + --md-primary-fg-color--light: hsla(0, 0%, 100%, .7); + --md-primary-fg-color--dark: rgba(0, 0, 0, .07); + --md-primary-bg-color: rgba(0, 0, 0, .87); + --md-primary-bg-color--light: rgba(0, 0, 0, .54); + --md-accent-fg-color: #fa0; + --md-accent-fg-color--transparent: rgba(255, 170, 0, .1); + --md-accent-bg-color: rgba(0, 0, 0, .87); + --md-accent-bg-color--light: rgba(0, 0, 0, .54); + --md-typeset-a-color: #4051b5; + --md-admonition-icon--verified: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--example: url('data:image/svg+xml;charset=utf-8,'); + /* --md-text-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; */ +} + +.md-nav__link--passed, .md-nav__link--passed code, .md-nav__link[for] { + color: var(--md-primary-bg-color); +} + +.only-sm, +.only-sm-and-up, +.only-md-and-up, +.only-lg-and-up { + display: none; +} + +footer.md-footer .footer-info { + text-align: left; + width: 100%; + color: rgba(255, 255, 255, 0.5); + font-size: .64rem; + margin: auto 0.6rem; + padding: 0.4rem 0 0 0; +} + +footer.md-footer .footer-info a { + color: rgba(255, 255, 255, 0.55) !important; +} + +footer.md-footer .md-copyright a:hover, +footer.md-footer .footer-info a:hover { + color: rgba(255, 255, 255, 0.7) !important; + text-decoration: underline; +} + +body > div > aside.md-banner { + display: none; + background-size: 110% 110%; + background-image: repeating-linear-gradient(195deg, #311b92, #8b00d1); + background-color: #9f00ff; + padding: 0; +} + +.md-banner__inner { + background-color: #00000010; + padding: .23rem 0; + margin: 0; + max-width: 100%; +} + +@media (min-width: 600px) { + body > div > aside.md-banner { + display: block; + } +} + +p.announce-banner { + margin: 0; + padding: 0; + text-align: center; +} + +p.announce-banner a { + display: block; + font-size: 16px; + font-weight: 400; + vertical-align: baseline; + margin: 0; +} + +p.announce-banner a, +p.announce-banner a:focus, +p.announce-banner a:hover { + color: #ffffff; +} + +p.announce-banner a span { + font-size: 14px; + vertical-align: baseline; +} + +p.announce-banner strong { + white-space: nowrap; +} + +span.build { + font-size: .66rem; +} + +header.md-header .md-ellipsis { + font-family: Roboto, sans-serif !important; + font-size: 20px !important; + font-weight: 500; + letter-spacing: .02em !important; +} + +.twitter { + color: #1DA1F2; +} + +@keyframes heart { + 0%, 40%, 80%, 100% { + transform: scale(1); + } + 20%, 60% { + transform: scale(1.15); + } +} + +.heart { + animation: heart 1000ms infinite; +} + +.purple { + color: #b65fc5 !important; +} + +.shadow { + border-radius: 4px; + box-shadow: 0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%); + /* Elevation Low: box-shadow: 0px 2px 1px -1px rgb(0 0 0 / 20%), 0px 1px 1px 0px rgb(0 0 0 / 14%), 0px 1px 3px 0px rgb(0 0 0 / 12%); */ + /* Elevation High: box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); */ +} + +.zoom { + transition: transform ease-in-out 0.5s; + cursor: zoom-in; +} + +/* Fix for Mermaid SVG bug */ +.mermaid svg { + height: auto; +} + +.md-typeset .admonition.verified, +.md-typeset details.verified { + border-color: #6713b3; +} + +.md-typeset .verified > .admonition-title, +.md-typeset .verified > summary { + background-color: rgba(124, 77, 255, 0.1); +} + +.md-typeset .verified > .admonition-title::before, +.md-typeset .verified > summary::before { + /* background-color: rgb(43, 155, 70); */ + background-color: #6713b3; + -webkit-mask-image: var(--md-admonition-icon--verified); + mask-image: var(--md-admonition-icon--verified); +} + +@media only screen and (min-width: 601px) { + .only-sm, + .hide-sm-and-up { + display: none; + } + + .only-sm-and-up { + display: inherit; + } +} + +@media only screen and (min-width: 993px) { + .hide-md-and-up { + display: none; + } + + .only-md-and-up { + display: inherit; + } +} + +@media only screen and (min-width: 1100px) { + .hide-lg-and-up { + display: none; + } + + .only-lg-and-up { + display: inherit; + } +} + +/* General Button Styles */ + +button { + user-select: none; + text-decoration: none; +} + +a.btn, +a.btn-small, +a.btn-large, +a.action-button { + text-decoration: none !important; +} + +/* Action Button Styles */ + +div.action-buttons { + margin-top: 2.5em; + margin-bottom: 2em; +} + +.action-button { + background-color: rgba(0, 0, 0, 0.62); + color: #ffffff; + font-size: 12px; + font-weight: 500; + line-height: 18px; + margin: 2px 0; + padding: 8px 26px; + border-radius: 6px; + text-align: center; + text-transform: uppercase; + text-decoration: none; + cursor: pointer; + display: inline-block; + user-select: none; +} + +p .action-button { + color: #ffffff !important; +} + +.action-button.bold { + font-weight: bold !important; +} + +.center-align { + text-align: center; +} + +.center-align.action-buttons { + text-align: center; + user-select: none; +} + +p.action-buttons { + padding-bottom: 1em; +} + +p.action-buttons .action-button, +.md-content article .action-button, +.center-align.action-buttons .action-button { + background-color: rgba(0, 0, 0, 0.62); + margin-left: 2px; + margin-right: 2px; +} + +p.center-align.action-buttons .action-button { + min-width: 138px; +} + +p.action-buttons .action-button:hover, +.center-align.action-buttons .action-button:hover, +.action-button.prominent:hover, +.action-button:hover { + background-color: #fa0 !important; + color: #000000 !important; + font-weight: 500; + text-decoration: none; +} + +@media only screen and (min-width: 601px) { + .center-align.action-buttons .action-button { + margin-left: 5px; + margin-right: 5px; + } +} + +/* Sign Up Buttons */ + +div.action-buttons.membership { + /* border-radius: 0.3em; */ + padding: 1.5em 0; + margin: 0 0 1em 0; + text-align: center; +} + +div.action-buttons.membership .action-button { + min-width: 180px; + font-size: .9em; + padding: 0.525em; + margin: 0 0.625em 15px 0.625em; + background-color: rgba(0, 0, 0, 0.62); +} + +div.action-buttons.membership .action-button:hover { + background-color: #fa0; +} + +/* App Store Buttons */ + +div.appstore-buttons { + clear: both; + margin-top: 2.5em; + margin-bottom: 2.5em; + text-align: center; +} + +div.appstore-buttons a { + margin-left: 0.5em; + margin-right: 0.5em; +} + +/* IMAGES */ + +img.center { + display: block; + margin: 0 auto; +} + +img.right { + float: right; + margin: 0 0 0.8em 0.9em; +} + +img.left { + float: left; + margin: 0 0.9em 0.8em 0; +} + +img.xxs { + min-width: 75px; + max-width: 100px; +} + +img.tiny, +img.xs { + min-width: 75px; + max-width: 125px; +} + +img.small, +img.sm { + min-width: 100px; + width: 20%; + max-width: 250px; +} + +img.medium, +img.md { + min-width: 75px; + width: 40%; + max-width: 400px; +} + +img.large, +img.lg { + min-width: 75px; + width: 60%; + max-width: 480px; +} + +img.w10 { + max-width: 10%; +} + +img.w15 { + max-width: 15%; +} + +img.w20 { + max-width: 20%; +} + +img.w25 { + max-width: 25%; +} + +img.w33 { + max-width: 33.3333%; +} + +img.w40 { + max-width: 40%; +} + +img.w50 { + max-width: 50%; +} + +img.w60 { + max-width: 60%; +} + +img.w66 { + max-width: 66.6666%; +} + +img.pt-2 { + padding-top: 15px !important; +} + +img.pt-3 { + padding-top: 30px !important; +} + +img.pt-4 { + padding-top: 40px !important; +} + +img.pt-5 { + padding-top: 50px !important; +} + +img.pt-6 { + padding-top: 60px !important; +} + +img.pb-2 { + padding-bottom: 15px !important; +} + +img.pb-3 { + padding-bottom: 30px !important; +} + +img.pb-4 { + padding-bottom: 40px !important; +} + +img.pb-5 { + padding-bottom: 50px !important; +} + +img.pb-6 { + padding-bottom: 60px !important; +} + +img.pb-7 { + padding-bottom: 70px !important; +} + +img.pb-8 { + padding-bottom: 80px !important; +} + +img.pb-9 { + padding-bottom: 90px !important; +} + +img.pb-10 { + padding-bottom: 100px !important; +} + +img.w100, +img.full, +img.fullwidth { + width: 100%; +} + +img.sponsor-logo { + text-align: center; + vertical-align: middle; + width: auto; + height: 150px; + margin: 25px; +} + +/* Common Styles */ + +.right { + float: right !important; +} + +.left { + float: left !important; +} + +.ma-auto { + margin-top: auto !important; + margin-right: auto !important; + margin-bottom: auto !important; + margin-left: auto !important; +} + +.my-auto { + margin-top: auto !important; + margin-bottom: auto !important; +} + +.mx-auto { + margin-left: auto !important; + margin-right: auto !important; +} + +.mt-auto { + margin-top: auto !important; +} + +.mr-auto { + margin-right: auto !important; +} + +.mb-auto { + margin-bottom: auto !important; +} + +.ml-auto { + margin-left: auto !important; +} + +.ma-0 { + margin: 0 0 !important; +} + +.my-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; +} + +.mx-0 { + margin-left: 0 !important; + margin-right: 0 !important; +} + +.mt-0 { + margin-top: 0 !important; +} + +.mr-0 { + margin-right: 0 !important; +} + +.mb-0 { + margin-bottom: 0 !important; +} + +.ml-0 { + margin-left: 0 !important; +} + +.pa-0 { + padding: 0 0 !important; +} + +.py-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; +} + +.px-0 { + padding-left: 0 !important; + padding-right: 0 !important; +} + +.pt-0 { + padding-top: 0 !important; +} + +.pr-0 { + padding-right: 0 !important; +} + +.pb-0 { + padding-bottom: 0 !important; +} + +.pl-0 { + padding-left: 0 !important; +} + +.ma-1 { + margin: 4px 4px !important; +} + +.my-1 { + margin-top: 4px !important; + margin-bottom: 4px !important; +} + +.mx-1 { + margin-left: 4px !important; + margin-right: 4px !important; +} + +.mt-1 { + margin-top: 4px !important; +} + +.mr-1 { + margin-right: 4px !important; +} + +.mb-1 { + margin-bottom: 4px !important; +} + +.ml-1 { + margin-left: 4px !important; +} + +.pa-1 { + padding: 4px 4px !important; +} + +.py-1 { + padding-top: 4px !important; + padding-bottom: 4px !important; +} + +.px-1 { + padding-left: 4px !important; + padding-right: 4px !important; +} + +.pt-1 { + padding-top: 4px !important; +} + +.pr-1 { + padding-right: 4px !important; +} + +.pb-1 { + padding-bottom: 4px !important; +} + +.pl-1 { + padding-left: 4px !important; +} + +.ma-2 { + margin: 8px 8px !important; +} + +.my-2 { + margin-top: 8px !important; + margin-bottom: 8px !important; +} + +.mx-2 { + margin-left: 8px !important; + margin-right: 8px !important; +} + +.mt-2 { + margin-top: 8px !important; +} + +.mr-2 { + margin-right: 8px !important; +} + +.mb-2 { + margin-bottom: 8px !important; +} + +.ml-2 { + margin-left: 8px !important; +} + +.pa-2 { + padding: 8px 8px !important; +} + +.py-2 { + padding-top: 8px !important; + padding-bottom: 8px !important; +} + +.px-2 { + padding-left: 8px !important; + padding-right: 8px !important; +} + +.pt-2 { + padding-top: 8px !important; +} + +.pr-2 { + padding-right: 8px !important; +} + +.pb-2 { + padding-bottom: 8px !important; +} + +.pl-2 { + padding-left: 8px !important; +} + +.ma-3 { + margin: 16px 16px !important; +} + +.my-3 { + margin-top: 16px !important; + margin-bottom: 16px !important; +} + +.mx-3 { + margin-left: 16px !important; + margin-right: 16px !important; +} + +.mt-3 { + margin-top: 16px !important; +} + +.mr-3 { + margin-right: 16px !important; +} + +.mb-3 { + margin-bottom: 16px !important; +} + +.ml-3 { + margin-left: 16px !important; +} + +.pa-3 { + padding: 16px 16px !important; +} + +.py-3 { + padding-top: 16px !important; + padding-bottom: 16px !important; +} + +.px-3 { + padding-left: 16px !important; + padding-right: 16px !important; +} + +.pt-3 { + padding-top: 16px !important; +} + +.pr-3 { + padding-right: 16px !important; +} + +.pb-3 { + padding-bottom: 16px !important; +} + +.pl-3 { + padding-left: 16px !important; +} + +.ma-4 { + margin: 24px 24px !important; +} + +.my-4 { + margin-top: 24px !important; + margin-bottom: 24px !important; +} + +.mx-4 { + margin-left: 24px !important; + margin-right: 24px !important; +} + +.mt-4 { + margin-top: 24px !important; +} + +.mr-4 { + margin-right: 24px !important; +} + +.mb-4 { + margin-bottom: 24px !important; +} + +.ml-4 { + margin-left: 24px !important; +} + +.pa-4 { + padding: 24px 24px !important; +} + +.py-4 { + padding-top: 24px !important; + padding-bottom: 24px !important; +} + +.px-4 { + padding-left: 24px !important; + padding-right: 24px !important; +} + +.pt-4 { + padding-top: 24px !important; +} + +.pr-4 { + padding-right: 24px !important; +} + +.pb-4 { + padding-bottom: 24px !important; +} + +.pl-4 { + padding-left: 24px !important; +} + +.ma-5 { + margin: 48px 48px !important; +} + +.my-5 { + margin-top: 48px !important; + margin-bottom: 48px !important; +} + +.mx-5 { + margin-left: 48px !important; + margin-right: 48px !important; +} + +.mt-5 { + margin-top: 48px !important; +} + +.mt-6 { + margin-top: 64px !important; +} + +.mr-5 { + margin-right: 48px !important; +} + +.mb-5 { + margin-bottom: 48px !important; +} + +.ml-5 { + margin-left: 48px !important; +} + +.pa-5 { + padding: 48px 48px !important; +} + +.py-5 { + padding-top: 48px !important; + padding-bottom: 48px !important; +} + +.px-5 { + padding-left: 48px !important; + padding-right: 48px !important; +} + +.pt-5 { + padding-top: 48px !important; +} + +.pt-6 { + padding-top: 64px !important; +} + +.pr-5 { + padding-right: 48px !important; +} + +.pb-5 { + padding-bottom: 48px !important; +} + +.pl-5 { + padding-left: 48px !important; +} + +.font-weight-thin { + font-weight: 100 !important; +} + +.font-weight-light { + font-weight: 300 !important; +} + +.font-weight-regular { + font-weight: 400 !important; +} + +.font-weight-medium { + font-weight: 500 !important; +} + +.font-weight-bold { + font-weight: 700 !important; +} + +.font-weight-black { + font-weight: 900 !important; +} + +.font-italic { + font-style: italic !important; +} + +.text-capitalize { + text-transform: capitalize !important; +} + +.text-lowercase { + text-transform: lowercase !important; +} + +.text-none { + text-transform: none !important; +} + +.text-uppercase { + text-transform: uppercase !important; +} + +.text-no-wrap { + white-space: nowrap !important; +} + +.text-truncate { + white-space: nowrap !important; + overflow: hidden !important; + text-overflow: ellipsis !important; + line-height: 1.1 !important; +} + +.text-xs-left { + text-align: left; +} + +.text-xs-right { + text-align: right; +} + +.text-xs-center { + text-align: center; +} + +@media (min-width: 600px) { + .text-sm-left { + text-align: left !important; + } + + .text-sm-right { + text-align: right !important; + } + + .text-sm-center { + text-align: center !important; + } +} + +/* Landing Page */ + +.center-align.action-buttons { + padding: 16px 0 16px; +} + +.center-align.action-buttons .action-button { + margin: 5px; +} + +.center-align.action-buttons .action-button.action-secondary, +.center-align.action-buttons .action-button.action-primary { + min-width: 185px; + font-weight: 700 !important; +} + +.action-buttons .action-button.action-secondary { + background-color: rgba(0, 0, 0, 0); + border: 1px solid #404040; + color: #404040 !important; +} + +.action-buttons .action-button.action-primary { + background-color: #404040; + border: 1px solid rgba(0, 0, 0, 0); +} + +@media (max-width: 599px) { + .action-buttons .action-button.action-secondary, + .action-buttons .action-button.action-primary { + display: block; + min-width: auto; + } +} + +@media (max-width: 560px) { + .block-xs { + display: block; + margin-bottom: 0.5em; + } +} \ No newline at end of file diff --git a/developer-guide/api/auth/index.html b/developer-guide/api/auth/index.html new file mode 100644 index 0000000000..83d9dff382 --- /dev/null +++ b/developer-guide/api/auth/index.html @@ -0,0 +1,7850 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Client Authentication - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Client Authentication

    +

    In order to grant limited access to API clients, you can use the photoprism auth and photoprism clients subcommands to generate authentication tokens for them. While app passwords are bound to user accounts and can be generated by anyone from the UI, OAuth2 access tokens and client credentials can be used to access the REST API without being bound to an account.

    +

    App Passwords

    +

    All users can generate app-specific passwords for their own use from the web interface by navigating to Settings > Account and then clicking the Apps and Devices button.

    +

    Alternatively, running the following command in a terminal will generate a new app-specific password e.g. for the admin account, so that WebDAV-compatible clients can synchronize files even if 2FA is enabled for the account or the account password is changed:

    +
    photoprism auth add -n Sync -s "webdav" admin
    +
    +

    You will then be shown the generated app password so you can copy it and keep it in a safe place or enter it directly into an app, as you will not be able to see it again:

    +
    |-----------------------------|---------------------|
    +| App Password                | Authorization Scope |
    +|-----------------------------|---------------------|
    +| HY8fxO-8hvNqB-43UV4q-1AZ0vu | webdav              |
    +|-----------------------------|---------------------|
    +
    +

    For added security, we recommend setting an expiration date for the app passwords and access tokens you generate. Common scopes for use with app passwords are either "*" for Full Access or "webdav" for WebDAV-compatible file synchronization apps.

    +
    +

    App passwords can be used as access tokens in the Bearer Authorization header without first creating a session access token, and to obtain short-lived session access tokens through the POST /api/v1/session endpoint.

    +
    +

    Command Options

    +

    The following flags can be used with the photoprism auth add command (if you omit name or scope, you will be asked to enter them interactively):

    + + + + + + + + + + + + + + + + + + + + + +
    Command FlagDescription
    --name CLIENT, -n CLIENTCLIENT name to help identify the application
    --scope SCOPES, -s SCOPESauthorization SCOPES e.g. "metrics" or "photos albums" ("*" to allow all)
    --expires LIFETIME, -e LIFETIMEauthentication LIFETIME in seconds, after which access expires (-1 to disable the limit) (default: 31536000)
    +

    Authorization Scopes

    +

    One or more of these scopes can be specified to limit the access to certain API endpoints:

    +

    files, folders, shares, photos, videos, favorites, albums, moments, calendar, people, places, labels, config, settings, services, users, sessions, logs, webdav, metrics

    +
    +

    Clients authenticated with app passwords are unable to change the account password or manage user accounts, even if you specify all scopes or use the wildcard "*" to allow all.

    +
    +

    Access Tokens

    +

    If you do not specify a username as argument for the photoprism auth add command, a client access token will be generated (the same flags and scopes as above can be used to limit token authorization and lifetime):

    +
    |--------------------------------------------------|---------------------|
    +| Access Token                                     | Authorization Scope |
    +|--------------------------------------------------|---------------------|
    +| 7dbfa37b5a3db2a9e9dd186479018bfe2e3ce5a71fc2f955 | files folders       |
    +|--------------------------------------------------|---------------------|
    +
    +

    Generating access tokens is a good choice for developers and other advanced users to connect scripts and external services to the PhotoPrism API, e.g. services that collect metrics or start indexing at regular intervals.

    +

    Please note, however, that client access tokens cannot be used to synchronize files via WebDAV, even if the token authorization scope is set to "webdav" or "*", as this requires a registered user account. Access tokens can also not be used as a direct password replacement for apps, since clients are not allowed to use the POST /api/v1/session endpoint, which is required for logging in through the user interface.

    +

    Client Credentials

    +

    If clients support authentication via OAuth2 client credentials, you can use the following terminal commands to generate a client_id and client_secret for them, list registered clients, and delete client credentials that are no longer used:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CLI CommandDescription
    photoprism clients ls [search]Lists registered client applications
    photoprism clients add [username]Registers a new client application
    photoprism clients show [identifier]Shows client configuration details
    photoprism clients mod [identifier]Updates client application settings
    photoprism clients rm [identifier]Deletes the specified client application
    photoprism clients reset --yesRemoves all registered client applications
    +

    For example, running the following in a terminal will generate credentials for Prometheus, with access limited to the metrics endpoint:

    +
    photoprism clients add -n Prometheus -s metrics
    +
    +

    You will then be shown the generated client_id and client_secret so you can copy them and keep them in a safe place:

    +
    |------------------|----------------------------------|
    +| Client ID        | Client Secret                    |
    +|------------------|----------------------------------|
    +| csce0w2joodmirvi | 5VKkBeZLDvojjpE9XzCMXShnrxmxHWvN |
    +|------------------|----------------------------------|
    +
    +
    +

    OAuth2 client credentials cannot be directly used for synchronizing files via WebDAV, as a password replacement for apps, or for logging in to the web interface.

    +
    +

    Command Options

    +

    The following parameters can be used with the photoprism clients add command, e.g. to limit the number of access tokens the client can request:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Command FlagDescription
    --name CLIENT, -n CLIENTCLIENT name to help identify the application
    --role ROLE, -r ROLEclient authorization ROLE (default: "client")
    --scope SCOPES, -s SCOPESclient authorization SCOPES e.g. "metrics" or "photos albums" ("*" to allow all)
    --expires LIFETIME, -e LIFETIMEaccess token LIFETIME in seconds, after which a new token must be requested (default: 86400)
    --tokens NUMBER, -t NUMBERmaximum NUMBER of access tokens that the client can request (-1 to disable the limit) (default: 10)
    +

    If you omit the name or scope parameter, you will be asked to enter them interactively. One or more of these scopes can be specified to limit the access to certain API endpoints:

    +

    files, folders, shares, photos, videos, favorites, albums, moments, calendar, people, places, labels, config, settings, services, users, sessions, logs, webdav, metrics

    +
    +

    When requesting access tokens, clients can further restrict the scope of the tokens by passing the scope parameter to the POST /api/v1/oauth/token endpoint.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/api/clients/go/index.html b/developer-guide/api/clients/go/index.html new file mode 100644 index 0000000000..9d8a4acdf9 --- /dev/null +++ b/developer-guide/api/clients/go/index.html @@ -0,0 +1,7714 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Go - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Go API Client

    +

    Maintained by Kris Nóva

    +

    GitHub URL: kris-nova/photoprism-client-go

    +

    Installation

    +
    go get github.com/kris-nova/photoprism-client-go
    +
    +

    Methods

    +
    func New(connURL *url.URL, token, downloadToken string) *V1Client
    +func (v1 *V1Client) AddPhotosToAlbum(albumUUID string, photoIDs []string) error
    +func (v1 *V1Client) ApprovePhoto(uuid string) error
    +func (v1 *V1Client) CancelIndex() error
    +func (v1 *V1Client) CloneAlbum(album Album) (Album, error)
    +func (v1 *V1Client) CreateAlbum(album Album) (Album, error)
    +func (v1 *V1Client) DeleteAlbums(albumUUIDs []string) error
    +func (v1 *V1Client) DELETE(payload interface{}, endpointFormat string, a ...interface{}) *V1Response
    +func (v1 *V1Client) DeletePhotosFromAlbum(albumUUID string, photoIDs []string) error
    +func (v1 *V1Client) DislikeAlbum(uuid string) error
    +func (v1 *V1Client) DislikePhoto(uuid string) error
    +func (v1 *V1Client) Endpoint(str string) string
    +func (v1 *V1Client) GetAlbumDownload(uuid string) ([]byte, error)
    +func (v1 *V1Client) GetAlbums(options *AlbumOptions) ([]Album, error)
    +func (v1 *V1Client) GetAlbum(uuid string) (Album, error)
    +func (v1 *V1Client) GET(endpointFormat string, a ...interface{}) *V1Response
    +func (v1 *V1Client) GetPhotoDownload(uuid string) ([]byte, error)
    +func (v1 *V1Client) GetPhotos(options *PhotoOptions) ([]Photo, error)
    +func (v1 *V1Client) GetPhoto(uuid string) (Photo, error)
    +func (v1 *V1Client) GetPhotoYaml(uuid string) ([]byte, error)
    +func (v1 *V1Client) Index() error
    +func (v1 *V1Client) LikeAlbum(uuid string) error
    +func (v1 *V1Client) LikePhoto(uuid string) error
    +func (v1 *V1Client) PhotoPrimary(uuid, fileuuid string) error
    +func (v1 *V1Client) POST(payload interface{}, endpointFormat string, a ...interface{}) *V1Response
    +func (v1 *V1Client) PUT(payload interface{}, endpointFormat string, a ...interface{}) *V1Response
    +func (v1 *V1Client) SetToken(token string)
    +func (v1 *V1Client) UpdateAlbum(album Album) (Album, error)
    +func (v1 *V1Client) UpdatePhoto(photo Photo) (Photo, error)
    +
    +

    Example

    +
    package main
    +
    +import (
    +    "fmt"
    +    "io/ioutil"
    +    "path"
    +
    +    photoprism "github.com/kris-nova/photoprism-client-go"
    +    "github.com/kris-nova/logger"
    +)
    +
    +func main()
    +    logger.Level = 4
    +
    +    uuid := "pqnzigq351j2fqgn" // This is a known ID
    +    client := photoprism.New("http://localhost:8080")
    +    err := client.Auth(photoprism.NewClientAuthLogin("admin", "missy"))
    +    if err != nil
    +        logger.Critical("Error logging into API: %v", err)
    +        os.Exit(1)
    +    }
    +
    +    // -----------------
    +    // GetPhoto()
    +    //
    +    photo, err := client.V1().GetPhoto(uuid)
    +    if err != nil
    +        logger.Critical("Error fetching photo: %v", err)
    +        os.Exit(1)
    +    }
    +
    +    // -----------------
    +    // UpdatePhoto()
    +    //
    +    photo.PhotoTitle = "A really great photo!"
    +    photo, err = client.V1().UpdatePhoto(photo)
    +    if err != nil
    +        logger.Critical("Error updating photo: %v", err)
    +        os.Exit(1)
    +    }
    +
    +    // -----------------
    +    // GetPhotoDownload()
    +    //
    +    file, err := client.V1().GetPhotoDownload(photo.UUID)
    +    if err != nil
    +        logger.Critical("Error getting photo download: %v", err)
    +        os.Exit(1)
    +    }
    +
    +    for _, f := range photo.Files
    +        fileName := fmt.Sprintf("/tmp/%s", path.Base(f.FileName))
    +        logger.Always(fileName)
    +        ioutil.WriteFile(fileName, file, 0666)
    +    }
    +    os.Exit(0)
    +}
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/api/docs/index.html b/developer-guide/api/docs/index.html new file mode 100644 index 0000000000..d3f1f46bce --- /dev/null +++ b/developer-guide/api/docs/index.html @@ -0,0 +1,7517 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Swagger Docs - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Swagger API Documentation

    +

    Our interactive API documentation, publicly available at docs.photoprism.dev, is automatically generated from the source code annotations in the /internal/api package.

    +

    Running the following command in your local development environment will update the /internal/api/swagger.json file, which contains the API endpoint documentation in machine-readable form:

    +
    make swag
    +
    +

    You can view it as HTML by navigating to /api/v1/docs/index.html after you have rebuilt and started photoprism with the commands make build and ./photoprism start:

    +

    swagger-docs

    +

    Any help with adding annotations to improve our documentation is much appreciated!

    +
    +

    The /api/v1/docs API documentation endpoint is disabled in production builds.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/api/img/oidc-login.jpg b/developer-guide/api/img/oidc-login.jpg new file mode 100644 index 0000000000..f621246de8 Binary files /dev/null and b/developer-guide/api/img/oidc-login.jpg differ diff --git a/developer-guide/api/img/oidc-sso-flow.jpg b/developer-guide/api/img/oidc-sso-flow.jpg new file mode 100644 index 0000000000..793ff98251 Binary files /dev/null and b/developer-guide/api/img/oidc-sso-flow.jpg differ diff --git a/developer-guide/api/img/oidc-subject.jpg b/developer-guide/api/img/oidc-subject.jpg new file mode 100644 index 0000000000..38079586e1 Binary files /dev/null and b/developer-guide/api/img/oidc-subject.jpg differ diff --git a/developer-guide/api/img/redirect-url-example.jpg b/developer-guide/api/img/redirect-url-example.jpg new file mode 100644 index 0000000000..1dd8ad611e Binary files /dev/null and b/developer-guide/api/img/redirect-url-example.jpg differ diff --git a/developer-guide/api/img/swagger.jpg b/developer-guide/api/img/swagger.jpg new file mode 100644 index 0000000000..079a17584b Binary files /dev/null and b/developer-guide/api/img/swagger.jpg differ diff --git a/developer-guide/api/index.html b/developer-guide/api/index.html new file mode 100644 index 0000000000..365b141fb3 --- /dev/null +++ b/developer-guide/api/index.html @@ -0,0 +1,7731 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Introduction - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Web Service API

    +

    REST API Endpoints

    +

    For the currently implemented REST request endpoints available under /api/v1, please refer to the automatically generated backend API documentation as well as the request forms and entity models in our public repository:

    + +

    API request bodies and responses are usually JSON-encoded, except for binary data and some of the OAuth2 endpoints. Note that the Content-Type header must be set to application/json for this, as the request may otherwise fail with error 400.

    +
    +

    We welcome any contributions that help improve our API docs and make them easier to use for developers. To learn how to access the API while our Swagger documentation is not complete yet, we recommend checking the requests in the browser console that our JS frontend sends when you perform actions like creating an album - and then use the same method, URI, encoding, value names and types for sending requests with your own application.

    +
    +

    Client Authentication

    +

    When clients have a valid access token, e.g. obtained through the POST /api/v1/session or POST /api/v1/oauth/token endpoint, they can use a standard Bearer Authorization header to authenticate their requests:

    +
    Authorization: Bearer <token>
    +
    +

    Submitting the access token with a custom X-Auth-Token header is supported as well:

    +
    curl -H "X-Auth-Token: 7dbfa37b5a3db2a9e9dd186479018bfe2e3ce5a71fc2f955" \
    +http://localhost:2342/api/v1/photos?count=10
    +
    +

    Besides using the API endpoints provided for this, you can also generate valid access tokens by running the photoprism auth add command in a terminal.

    +

    Learn more ›

    +
    +

    App passwords can be used as access tokens in the Bearer Authorization header without first creating a session access token, and to obtain short-lived session access tokens through the POST /api/v1/session endpoint.

    +
    +

    Service Discovery Endpoints

    +

    OAuth2 Authorization Server

    +
    /.well-known/oauth-authorization-server
    +
    +

    https://demo.photoprism.app/.well-known/oauth-authorization-server

    +

    Learn more ›

    +

    OpenID Configuration

    +

    It is not yet possible to use PhotoPrism as an OpenID Connect (OIDC) Identity Provider, since not all the required endpoints and grant types have been fully implemented. However, querying the /.well-known/openid-configuration endpoint shows what has already been implemented, so the missing capabilities can be identified and added over time.

    +

    https://demo.photoprism.app/.well-known/openid-configuration

    +

    Learn more ›

    +

    Deprecation Policy

    +

    Our REST API endpoints are currently not covered by an official deprecation policy, so some routes and request parameters may change as we add new features in upcoming releases.

    +

    However, we avoid making breaking changes, especially to endpoints that we know other developers are using.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/api/oauth2/index.html b/developer-guide/api/oauth2/index.html new file mode 100644 index 0000000000..532a421155 --- /dev/null +++ b/developer-guide/api/oauth2/index.html @@ -0,0 +1,7831 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OAuth2 Grant Types - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    OAuth2 Grant Types

    +

    The OAuth 2.0 specification is an authorization framework that contains a set of methods, or grants, that a client application can use to obtain an access token. Each grant type is designed for a specific use case:

    + +

    The access token can then be passed to an API endpoint, which checks it to determine validity and authorization scope.

    +
    +

    Support for the Authorization Code Flow is planned for a future release.

    +
    +

    Server Endpoints

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ResourceEndpointMethods
    Authorization/api/v1/oauth/authorizeGET, POST
    Token/api/v1/oauth/tokenPOST
    UserInfo/api/v1/oauth/userinfoGET, POST
    Registrationnot implemented yet
    Introspectionnot implemented yet
    Revocation/api/v1/oauth/revokePOST
    End Sessionnot implemented yet
    Device Authorizationnot implemented yet
    +

    Clients can query the /.well-known/oauth-authorization-server and /.well-known/openid-configuration endpoints for automatic service discovery:

    + +
    +

    Note that the Authorization and UserInfo endpoints cannot be used yet as they are still under development.

    +
    +

    Access Tokens

    +

    When clients have a valid access token, e.g. obtained through the POST /api/v1/oauth/token endpoint, they can use a standard Bearer Authorization header to authenticate their requests:

    +
    Authorization: Bearer <token>
    +
    +

    Submitting the access token with a custom X-Auth-Token header is supported as well:

    +
    curl -H "X-Auth-Token: 7dbfa37b5a3db2a9e9dd186479018bfe2e3ce5a71fc2f955" \
    +http://localhost:2342/api/v1/photos?count=10
    +
    +

    Besides using the API endpoints provided for this, you can also generate valid access tokens by running the photoprism auth add command in a terminal.

    +

    Learn more ›

    +
    +

    App passwords can be used as access tokens in the Bearer Authorization header without first creating a session access token, and to obtain short-lived session access tokens through the POST /api/v1/session endpoint.

    +
    + + +

    Protocol References

    + +

    Authentication Libraries

    + +

    Documentation Examples

    + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/api/oidc/index.html b/developer-guide/api/oidc/index.html new file mode 100644 index 0000000000..8fa60fb339 --- /dev/null +++ b/developer-guide/api/oidc/index.html @@ -0,0 +1,8183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OpenID Connect - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Single Sign-On via OpenID Connect

    +

    OpenID Connect (OIDC) extends OAuth 2.0 to allow users to log in and optionally register through an external identity provider instead of manually entering a username and password:

    +

    oidc-login

    +

    Authentication Flow

    +

    oidc-sso-flow

    +

    Learn more ›

    +

    Config Options

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_OIDC_URI--oidc-uriissuer URI for single sign-on via OpenID Connect, e.g. https://accounts.google.com
    PHOTOPRISM_OIDC_CLIENT--oidc-clientclient ID for single sign-on via OpenID Connect
    PHOTOPRISM_OIDC_SECRET--oidc-secretclient SECRET for single sign-on via OpenID Connect
    PHOTOPRISM_OIDC_PROVIDER--oidc-providercustom identity provider NAME, e.g. Google
    PHOTOPRISM_OIDC_ICON--oidc-iconcustom identity provider icon URI
    PHOTOPRISM_OIDC_REDIRECT--oidc-redirectautomatically redirect unauthenticated users to the configured identity provider
    PHOTOPRISM_OIDC_REGISTER--oidc-registerallow new users to create an account when they sign in with OpenID Connect
    PHOTOPRISM_OIDC_USERNAME--oidc-usernamepreferred_usernamepreferred username CLAIM for new OpenID Connect users (preferred_username, name, nickname, email)
    PHOTOPRISM_OIDC_WEBDAV--oidc-webdavallow new OpenID Connect users to use WebDAV when they have a role that allows it
    PHOTOPRISM_DISABLE_OIDC--disable-oidcdisable single sign-on via OpenID Connect, even if an identity provider has been configured
    +
    +

    Your PhotoPrism instance and the OpenID Connect Identity Provider (IdP) must be accessible via HTTPS and have valid TLS certificates configured for it. Please also make sure that the hostname in the Redirect URL configured on the IdP matches the Site URL used by PhotoPrism. Single sign-on via OIDC can otherwise not be enabled.

    +
    +

    Identity Providers

    +

    To authenticate users via OIDC, you can either set up and use a self-hosted identity provider such as ZITADEL or Keycloak, or configure a public identity provider service such as those operated by Google, Microsoft, GitHub, or Amazon.

    +

    Single sign-on can be configured automatically if the identity provider offers a standardized /.well-known/openid-configuration endpoint for service discovery, for example:

    + +

    Issuer URI

    +

    The Issuer URI in your configuration must match the issuer value returned by the /.well-known/openid-configuration endpoint of your OpenID Connect Identity Provider (IdP), for example https://accounts.google.com if you use Google for authentication.

    +
    +

    You may not modify the URI in any way, e.g. by adding or omitting slashes at the end. If the values do not match, the validation will fail and users cannot be redirected to your provider's login page. For security reasons, only a generic error message is displayed in this case.

    +
    +

    Redirect URL

    +

    The Redirect URL that must be specified when registering a new client with an Identity Provider is as follows, where {hostname} must be replaced by the hostname in the Site URL, e.g. configured via PHOTOPRISM_SITE_URL:

    +
    https://{hostname}/api/v1/oidc/redirect
    +
    +
    +

    Note that both the Site URL configured for your instance and the Redirect URL must start with https:// and that their hostnames must match, as the use of secure connections is a strict requirement for OpenID Connect.

    +
    +

    Local Development

    +

    Our development environment comes with a pre-configured Keycloak OIDC Identity Provider running at https://keycloak.localssl.dev/ for local testing:

    + +

    An admin account for managing users and a user account for testing single sign-on are pre-registered. Both have the password photoprism.

    +
    +

    Please do not use this test identity provider in a production environment as its configuration is not secure.

    +
    +

    Preferred Username

    +

    When a new user signs in with OpenID Connect1, their preferred username may already be registered. In this case, a random 6-digit number is appended to resolve the conflict.

    +

    The config option PHOTOPRISM_OIDC_USERNAME allows you to change the preferred username for new accounts from preferred_username to name, nickname, or verified email. Names are changed to lowercase handles so that, for example, "Jens Mander" becomes "jens.mander".

    +

    Existing Accounts

    +

    Super admins can manually connect existing user accounts2 under Settings > Users by changing the authentication to OIDC and then setting the Subject ID to match the account identifier from the configured Identity Provider:

    +

    Edit Dialog

    +

    The Edit Account dialog may additionally contain a text field for the Issuer URL. It does not need to be entered manually as it is set automatically after the first login.

    +

    Alternatively, you can run the following command in a terminal to allow authentication via OIDC and set a Subject ID to connect existing accounts:

    +
    photoprism users mod --auth=oidc --auth-id=[sub] [username]
    +
    +

    Learn more ›

    +

    Passwords

    +

    Changing the authentication of an account to OIDC does not remove a previously set password, so that it can still be used to log in (optionally also in combination with 2FA).

    +

    If a local password has been set for an account, you can remove it by running the following command in a terminal:

    +
    photoprism passwd --rm [username]
    +
    +

    Super admins can alternatively set the account password to a long random value through the Admin Web UI or CLI to effectively prevent local authentication.

    +

    Learn more ›

    +

    Deleting Accounts

    +

    Deleted accounts remain linked to the Subject ID, so logging in via OIDC is no longer possible and no new account can be registered for the same Subject ID either.

    +

    If you wish to change the connected user account or create a new account instead, you must therefore change the authentication of the old account e.g. to None before deleting it:

    +
    photoprism users mod --auth=none [username]
    +
    +

    To restore a previously deleted account, admins can follow the same steps as for creating a new account with the same username through the Admin Web UI or the photoprism users add command. You will then be asked if you want to restore the account.

    +

    Learn more ›

    +

    Service Discovery

    +

    It is not yet possible to use PhotoPrism as an Identity Provider, since not all the required endpoints and grant types have been fully implemented.

    +

    However, querying the /.well-known/openid-configuration endpoint shows what has already been implemented, so the missing capabilities can be identified and added over time:

    + + + +

    Software Libraries

    + +

    Protocol References

    + +

    Session Management

    + +

    Frequently Asked Questions

    +

    Is it possible to set a default role for new OIDC users?

    +

    For security reasons, our Personal Editions currently default to the Guest role, which admins can then upgrade after checking the eligibility of newly registered accounts.

    +

    Learn more ›

    +

    Can I configure a custom claim for the preferred username?

    +

    You can choose between preferred_username, name, nickname and email, where preferred_username is the default. The other options are used as a fallback if no value is returned for the configured claim.

    +

    Note that it is not possible to use a non-standard claim such as username, as this could lead to conflicts and potential security issues, e.g. if the value is not unique or not reliably set.

    +

    Learn more ›

    +
    +
    +
      +
    1. +

      PHOTOPRISM_OIDC_REGISTER must be set to "true" to allow new users to create an account 

      +
    2. +
    3. +

      Admins cannot change the authentication of their own user account through the Admin Web UI so that they do not accidentally lock themselves out e.g. by setting it to None

      +
    4. +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/api/search/index.html b/developer-guide/api/search/index.html new file mode 100644 index 0000000000..9e23a540fd --- /dev/null +++ b/developer-guide/api/search/index.html @@ -0,0 +1,8373 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Search Endpoints - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Search API Endpoints

    +

    GET /api/v1/photos

    +

    When querying the /api/v1/photos endpoint, matching files will be returned along with the metadata of the corresponding photos. This may be unexpected at first due to the endpoint name. However, this approach allows to sort results e.g. by color or file size, which differ at the file level and therefore require dynamic sorting/grouping of multiple matching files that may belong to the same photo (see Stacks).

    +

    To simplify processing in the client/user interface, you can set the merged parameter to true so that consecutive files with the same photo ID are merged into a single result with the Files property containing the related files.

    +

    In particular, multiple files are returned for a single photo in the case of multi-file/hybrid media formats such as Live Photos, as these consist of a photo and a video file. The same applies to RAW/JPEG.

    +
    +

    If you only want the primary image (thumbnail) to be returned, you can set the primary filter to true. In this case, it is not necessary to also set merged to true as a single file will be returned for each photo. This can simplify things if you don't need to know anything about the additional files, e.g. when just rendering thumbnails without metadata.

    +
    +

    Composite ID and UIDs

    +

    The ID returned by the search API is a composite ID consisting of the photo ID and the related file ID (usually the file selected as primary image), so that this ID is unique as required for rendering in the user interface.

    +

    Note that you do not need this composite ID to communicate with any of our API endpoints. Instead, you will either have to pass a UID (e.g. when updating photo metadata) or the SHA1 Hash of a file (e.g. when requesting a thumbnail).

    +
    +

    Using random UIDs prevents possible caching issues in proxies/clients, e.g. if the index is reset on the server and the numeric IDs would thus start at 0 again. They are also hard to guess, so a time-consuming brute force attack is required instead of simply enumerating integers starting from 0.

    +
    +

    Response Example

    +
    [{
    +    "ID": "11-21",
    +    "UID": "pse1yzastu36irsj",
    +    "Type": "image",
    +    "TypeSrc": "",
    +    "TakenAt": "2012-08-27T12:40:25Z",
    +    "TakenAtLocal": "2012-08-27T14:40:25Z",
    +    "TakenSrc": "meta",
    +    "TimeZone": "Europe/Madrid",
    +    "Path": "2012",
    +    "Name": "20120827-144025-Bodegas-Ysios-Winery-Laguardia-Spain",
    +    "OriginalName": "",
    +    "Title": "Bodegas Ysios Winery / Laguardia / Spain",
    +    "Description": "",
    +    "Year": 2012,
    +    "Month": 8,
    +    "Day": 27,
    +    "Country": "es",
    +    "Stack": 0,
    +    "Favorite": false,
    +    "Private": false,
    +    "Iso": 100,
    +    "FocalLength": 28,
    +    "FNumber": 11,
    +    "Exposure": "1/160",
    +    "Quality": 4,
    +    "Resolution": 8,
    +    "Color": 2,
    +    "Scan": false,
    +    "Panorama": false,
    +    "CameraID": 5,
    +    "CameraSrc": "meta",
    +    "CameraSerial": "1730906408",
    +    "CameraMake": "Canon",
    +    "CameraModel": "EOS 30D",
    +    "LensID": 5,
    +    "LensModel": "EF28mm f/1.8 USM",
    +    "Lat": 42.568302,
    +    "Lng": -2.5908582,
    +    "CellID": "s2:0d4ff9b1b32c",
    +    "PlaceID": "es:7v314KSnaCLy",
    +    "PlaceSrc": "meta",
    +    "PlaceLabel": "Laguardia, Euskadi, Spain",
    +    "PlaceCity": "Laguardia",
    +    "PlaceState": "Euskadi",
    +    "PlaceCountry": "es",
    +    "InstanceID": "",
    +    "FileUID": "fse1yza2r4gsjq6f",
    +    "FileRoot": "/",
    +    "FileName": "2012/20120827-144025-Bodegas-Ysios-Winery-Laguardia-Spain.jpg",
    +    "Hash": "4bc82c3ea5aaa323aea801fe0125b554af8e49af",
    +    "Width": 3368,
    +    "Height": 2246,
    +    "Portrait": false,
    +    "Merged": false,
    +    "CreatedAt": "2024-05-25T17:52:22.291451832Z",
    +    "UpdatedAt": "2024-05-25T17:52:22.307889576Z",
    +    "EditedAt": "0001-01-01T00:00:00Z",
    +    "CheckedAt": "2024-05-26T06:15:09Z",
    +    "Files": [
    +        {
    +        "UID": "fse1yza2r4gsjq6f",
    +        "PhotoUID": "pse1yzastu36irsj",
    +        "Name": "2012/20120827-144025-Bodegas-Ysios-Winery-Laguardia-Spain.jpg",
    +        "Root": "/",
    +        "Hash": "4bc82c3ea5aaa323aea801fe0125b554af8e49af",
    +        "Size": 2473990,
    +        "Primary": true,
    +        "Codec": "jpeg",
    +        "FileType": "jpg",
    +        "MediaType": "image",
    +        "Mime": "image/jpeg",
    +        "Width": 3368,
    +        "Height": 2246,
    +        "Orientation": 1,
    +        "AspectRatio": 1.5,
    +        "Colors": "616121222",
    +        "Luminance": "AACA5A888",
    +        "Diff": 767,
    +        "Chroma": 15,
    +        "CreatedAt": "2024-05-25T17:52:22.291451832Z",
    +        "UpdatedAt": "2024-05-25T17:52:22.307889576Z",
    +        "Markers": []
    +        }
    +    ]
    +}]
    +
    +

    Response Headers

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    HeaderTypeExampleNotes
    X-Countint120Actual number of files returned
    X-Limitint120Maximum number of files requested
    X-Offsetint0File offset
    X-Download-Tokenstring3qjg1db2Security token required to download original files
    X-Preview-Tokenstring174utza5Security token required for the Thumbnail Image API
    +
    +

    In order to fetch all results, you can perform a follow-up query if the number in the X-Count response header matches X-Limit. For this, the offset request parameter must be set to the number of files already returned.

    +
    +

    Request Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeExampleNotes
    qstringdog color:redSearch query as entered by the user in the search toolbar, see Search Filters
    sstringariqwb43p5dh9h13Limits the result scope to the specified album UID
    countint1000Maximum number of files to be returned
    offsetint0File offset
    orderstringaddedSort order e.g. added, updated, edited, relevance, duration, size, newest, oldest, similar, name, title, or random
    mergedbooltrueMerges consecutive files that belong to the same photo into a single result, see above for an explanation
    +

    Search Filters

    +

    The following search filters can be used as regular GET request parameters or submitted as part of a search query with the q request parameter:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FilterTypeExamplesNotes
    distdecimaldist:50Distance to Position (km)
    latdecimallat:41.894043GPS Position (Latitude)
    lngdecimallng:-87.62448GPS Position (Longitude)
    chromanumberchroma:70Chroma (0-100)
    diffnumberdiff:-1 diff:2Differential Perceptual Hash (000000-FFFFFF)
    qualitynumberquality:0 quality:3Minimum quality score (1-7)
    albumstringalbum:berlinAlbum UID or Name, supports * wildcards
    albumsstringalbums:"South Africa & Birds"Album Names (combinable with & and |)
    altstringalt:300-500GPS Altitude (m)
    camerastringcamera:canonCamera Make/Model Name
    categorystringcategory:airportLocation Category
    citystringcity:"Berlin"Location City (separate with |)
    colorstringcolor:"red|blue"Color Name (purple, magenta, pink, red, orange, gold, yellow, lime, green, teal, cyan, blue, brown, white, grey, black) (separate with |)
    countrystringcountry:"de|us"Location Country Code (separate with |)
    daystringday:3|13Day of Month (1-31, separate with |)
    fstringf:2.8-4.5Aperture (f-number)
    facestringface:PN6QO5INYTUSAATOFL43LL2ABAV5ACZGFace ID, yes, no, new, or kind
    facesstringfaces:yes faces:3Minimum number of Faces (yes = 1)
    favoritestringfavorite:true favorite:falseFinds images by favorite status
    filenamestringfilename:"2021/07/12345.jpg"File Name with path and extension (separate with |)
    folderstringfolder:"*/2020"Path Name (separate with |), supports * wildcards
    geostringgeo:yesFinds pictures with or without coordinates
    hashstringhash:2fd4e1c67a2dSHA1 File Hash (separate with |)
    idstringid:123e4567-e89b-...Finds pictures by Exif UID, XMP Document ID or Instance ID
    isostringiso:200-400ISO Number (light sensitivity)
    keywordsstringkeywords:"sand&water"Keywords (combinable with & and |)
    labelstringlabel:cat|dogLabel Names (separate with |)
    latlngstringlatlng:"name"GPS Bounding Box (Lat N, Lng E, Lat S, Lng W)
    lensstringlens:ef24Lens Make/Model Name
    mmstringmm:28-35Focal Length (35mm equivalent)
    monthstringmonth:7|10Month (1-12, separate with |)
    mpstringmp:3-6Resolution in Megapixels (MP)
    namestringname:"IMG_9831-112*"File Name without path and extension (separate with |)
    nearstringnear:pqbcf5j446s0futyFinds nearby pictures (UID)
    olcstringolc:8FWCHX7W+OLC Position (Open Location Code)
    originalstringoriginal:"IMG_9831-112*"Original file name of imported files (separate with |)
    pathstringpath:2020/HolidayPath Name (separate with |), supports * wildcards
    peoplestringpeople:"Jane & John"Subject Names (combinable with & and |)
    personstringperson:"Jane Doe & John Doe"Subject Names, exact matches (combinable with & and |)
    s2strings2:4799e370ca54c8b9S2 Position (Cell ID)
    scanstringscan:true scan:falseFinds scanned photos and documents
    statestringstate:"Baden-Württemberg"Location State (separate with |)
    subjectstringsubject:"Jane Doe & John Doe"Alias for person
    subjectsstringsubjects:"Jane & John"Alias for people
    titlestringtitle:"Lake*"Title (separate with |)
    typestringtype:rawMedia Type (image, video, raw, live, animated); separate with |
    uidstringuid:pqbcf5j446s0futyLimits results to the specified internal unique IDs
    yearstringyear:1990|2003Year (separate with |)
    animatedswitchanimated:yesFinds animated GIFs
    archivedswitcharchived:yesFinds archived pictures
    errorswitcherror:yesFinds pictures with errors
    hiddenswitchhidden:yesFinds hidden pictures (broken or unsupported)
    landscapeswitchlandscape:yesFinds pictures in landscape format
    liveswitchlive:yesFinds Live Photos and short videos
    monoswitchmono:yesFinds pictures with few or no colors
    panoramaswitchpanorama:yesFinds pictures with an aspect ratio > 1.9:1
    photoswitchphoto:yesFinds only photos, no videos
    portraitswitchportrait:yesFinds pictures in portrait format
    primaryswitchprimary:yesFinds primary JPEG files only
    privateswitchprivate:yesFinds private pictures
    publicswitchpublic:yesExcludes private pictures
    rawswitchraw:yesFinds pictures with RAW image file
    reviewswitchreview:yesFinds pictures in review
    squareswitchsquare:yesFinds images with an aspect ratio of 1:1
    stackswitchstack:yesFinds pictures with more than one media file
    stackableswitchstackable:yesFinds pictures that can be stacked with additional media files
    unsortedswitchunsorted:yesFinds pictures not in an album
    unstackedswitchunstacked:yesFinds pictures with a file that has been removed from a stack
    vectorswitchvector:yesFinds vector graphics only
    videoswitchvideo:yesFinds video files only
    addedtimestampadded:"2006-01-02T15:04:05Z"Finds pictures added at or after this time
    aftertimestampafter:"2022-01-30"Finds pictures taken on or after this date
    beforetimestampbefore:"2022-01-30"Finds pictures taken on or before this date
    editedtimestampedited:"2006-01-02T15:04:05Z"Finds pictures edited at or after this time
    takentimestamptaken:"2022-01-30"Finds pictures taken on the specified date
    updatedtimestampupdated:"2006-01-02T15:04:05Z"Finds pictures updated at or after this time
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/api/thumbnails/index.html b/developer-guide/api/thumbnails/index.html new file mode 100644 index 0000000000..3936fbaabc --- /dev/null +++ b/developer-guide/api/thumbnails/index.html @@ -0,0 +1,7901 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Thumbnail Images - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    + +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Thumbnail Image API

    +

    Introduction

    +

    Like most commercial image hosting services, we have chosen to implement a cookie-free thumbnail API to minimize request latency by avoiding unnecessary network traffic:

    +
      +
    • When a browser requests static files such as images from a server over HTTPS, it is generally unnecessary to send a cookie with each request if the URLs cannot be guessed, so for most practical use cases
    • +
    • One possible use of cookies may be to prevent the user from intentionally or accidentally sharing confidential thumbnail URLs with others
    • +
    • This is possible with most image hosting services/social media sites, and could also be considered a feature if you just want to share a few thumbnails without a lot of bells and whistles
    • +
    • Once an image has been downloaded by someone else, blocking the original URL provides little additional security, as digital copies are just as good as the original, see info box below
    • +
    • Keeping that in mind, previously shared URLs can be invalidated by changing the security token in your config
    • +
    • This will invalidate the browser cache on all connected devices, requiring previously cached thumbnails to be downloaded again
    • +
    • Be aware that frequent token changes result in performance degradation and a poor user experience.
    • +
    +

    In addition to better performance, a major advantage of cookie-free thumbnails is that they can be easily integrated into a content delivery network (CDN), since there is no need to check cookies or add other complex logic on edge servers.

    +
    +

    Since most users only have one domain/host name and modern web applications can store authentication tokens in localStorage, our Thumbnail Image API does not currently require or use cookies.

    +
    +

    Security Considerations

    +

    Digital copies are as good as originals: Once shared and downloaded, such images should be considered "leaked" because they are cached and can be re-shared by the recipient at any time, with no sure way to get all copies back, even if the download URL becomes invalid or the service is shut down completely.

    +

    Any form of protection we could provide would essentially be "snake oil", could be circumvented, and would have a negative impact on the user experience, such as disabling the browser cache or context menu.

    +

    For the highest level of protection, it is recommended to shield your private server from the public Internet. Always use HTTPS, a VPN and/or ideally TLS client certificates and make sure that only people you trust have access to your instance.

    +

    Further Reading

    + +

    Image Endpoint URI

    +

    A GET request to the following URI returns a thumbnail image of the specified size for the picture referenced by the SHA1 hash:

    +
    /api/v1/t/:hash/:token/:size
    +
    +

    If the instance requires authentication, you must also specify a valid security token, e.g.:

    +
    /api/v1/t/a2195b6840f46b555719d8e22e9b080e61a7317c/10d68214/tile_500
    +
    +

    With public mode enabled, you can instead use "public" as security token placeholder:

    +
    /api/v1/t/a2195b6840f46b555719d8e22e9b080e61a7317c/public/tile_500
    +
    +

    Video Endpoint URI

    +

    A GET request to the following URI returns the video referenced by the SHA1 hash (transcoding and range requests are supported for streaming):

    +
    /api/v1/videos/:hash/:token/:format
    +
    +

    If the instance requires authentication, you must also specify a valid security token, e.g.:

    +
    /api/v1/videos/51843134d75f4cbde534270cdd5954067f887ee6/10d68214/avc
    +
    +

    With public mode enabled, you can instead use "public" as security token placeholder:

    +
    /api/v1/videos/51843134d75f4cbde534270cdd5954067f887ee6/public/avc
    +
    +
    +

    The only target format currently available for transcoding is avc (MPEG-4 AVC). Additional formats will become available in a future release.

    +
    +

    Thumbnail Sizes

    +

    The smallest configurable static and dynamic size limit is 720px, so most sizes up to fit_720 are always generated by default. +Higher size limits generate thumbnails with more detail at higher resolutions - either statically (pre-generated while indexing) or on demand if the configuration permits.

    +

    Optional thumbnail sizes cannot be pre-generated and are only rendered on request, for example when sharing an image on Instagram.

    +

    The following overview shows the name, dimensions, and aspect ratio for each thumbnail size as well as a description of how it is used:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameWidthHeightAspect RatioAvailableUsage
    colors331:1AlwaysColor Detection
    tile_5050501:1AlwaysList View
    tile_1001001001:1AlwaysPlaces View
    left_2242242241:1On-DemandTensorFlow
    right_2242242241:1On-DemandTensorFlow
    tile_2242242241:1AlwaysTensorFlow, Mosaic View
    tile_5005005001:1AlwaysCards View
    fit_720720720PreservedAlwaysSD TV, Mobile
    tile_1080108010801:1OptionalInstagram
    fit_128012801024PreservedOn-DemandHD TV, SXGA
    fit_16001600900PreservedOptionalSocial Media
    fit_192019201200PreservedOn-DemandFull HD
    fit_204820482048PreservedOptionalDCI 2K, Tablets
    fit_256025601600PreservedOn-DemandQuad HD, Notebooks
    fit_384038402400PreservedOptional4K Ultra HD
    fit_409640964096PreservedOn-DemandDCI 4K, Retina 4K
    fit_768076804320PreservedOn-Demand8K Ultra HD 2
    +

    internal/thumb/sizes.go

    +
    +

    Generated thumbnail files are stored in the storage/cache/thumbnails folder, where the path and file name depend on the thumbnail size and original file hash. Learn more ›

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/code-quality/index.html b/developer-guide/code-quality/index.html new file mode 100644 index 0000000000..341cf4511f --- /dev/null +++ b/developer-guide/code-quality/index.html @@ -0,0 +1,7853 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Best Practices - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Quality and Best Practices

    +

    The quality of code has a practical impact on both your agility and the cost of development:1

    +
      +
    • you can't change buggy and/or bloated code fast enough to be truly agile
    • +
    • existing bugs can easily increase development costs (and time) by 10x
    • +
    • the mess eventually becomes so big and so deep that you cannot clean it up anymore2
    • +
    +
    +

    Productivity vs Time +

    +
    Productivity vs Time
    +
    +
    +

    All developers with more than a few years experience know that previous messes slow them down. And yet all +developers feel the pressure to make messes in order to meet deadlines. In short, they don’t take the time +to go fast! You will not make the deadline by making a mess. Indeed, the mess will slow you down instantly, and +will force you to miss the deadline. — Robert C. Martin

    +
    +

    Bottom-Up Development

    +

    Breaking a problem down into small, coherent fragments lends itself to organization. Start with the basic low-level +components and then proceed to higher-level abstractions.

    +

    Bottom-up development emphasizes coding and early testing, which can begin before having a detailed understanding of the final system. In practice, this may never be the case as requirements are constantly evolving.

    +

    Advantages of the bottom-up approach are component reusability, agility, and testability.

    +
    +

    I compared Mel's hand-optimized programs with the same code massaged by the optimizing assembler program, and Mel's always ran faster. +That was because the “top-down” method of program design hadn't been invented yet, and Mel wouldn't have used it anyway. +He wrote the innermost parts of his program loops first. — The Story of Mel

    +
    +

    Opportunistic Refactoring

    +

    We encourage developers to refactor existing code when they notice a specific issue, even though this may seem difficult when working with a distributed team, branches, and pull requests due to potential merge conflicts and delayed feedback.

    +

    It is best to do this while you are working on the same component anyway, for example to implement a feature or enhancement. This way you can easily validate if the proposed changes make sense and you avoid conflicts with others.

    +

    Releasing imperfect code is not a problem as long as it is accompanied by automated tests. This makes it easy to refactor later without breaking anything or requiring detailed knowledge of the requirements and a lot of time for manual testing. Be pragmatic. Done is better than perfect.

    +

    Potential security issues are an important exception. These should never be ignored. If you find a problem, please report it to us immediately so we can fix it.

    +
    +

    Feel free to think ahead, just don't code ahead. But also, don't feel the need to decide so many +details ahead. Learn enough to get started and build only what you need. +— J. B. Rainsberger

    +
    +

    Premature Optimization

    +

    One of the hardest parts of software development is knowing what to work on. +Don't get carried away implementing unnecessary abstractions and focusing on scalability optimization before you've even validated the functionality of a feature or component.

    +

    Instead of spending a lot of time on something you may not need, focus on user needs and test automation. +That way, you'll make sure you're developing the right functionality, and you can refactor it later for scalability and other non-functional aspects without breaking anything.

    +

    Also keep in mind that it's much easier and less effort to maintain small amounts of duplicate code than to choose the wrong abstraction.

    +
    +

    Premature optimization is the root of all evil. — Donald Knuth

    +
    +

    Be Careful with Caching

    +

    There are two hard things in computer science: cache invalidation and naming things.

    +
    +

    A cache is just a memory leak you haven't met yet. — Dave Cheney

    +
    +

    Go Slow Before You Go Fast 🐰

    +

    Read the docs, understand the context, and talk to others to gather missing information before you start coding. Write tests. Stay focused.

    +

    Don't worry that this will take too long. Take your time. It's the fastest and only sustainable way to get things done. You have to go slow before you can go fast.

    +
    +

    Simple, elegant solutions are more effective, but they are harder to find than complex ones, and they require more +time, which we too often believe to be unaffordable. — Niklaus Wirth, Communications of the ACM, 1985

    +
    +

    Effectiveness > Efficiency

    +

    Optimize for effectiveness before efficiency when prioritizing tasks:

    +
      +
    • Effectiveness is about achieving a specific outcome, such as providing the features that best help users solve their problems.
    • +
    • Efficiency means doing things in an optimal way, for example, faster and cheaper. We all strive to be efficient, but that's worthless if it doesn't contribute to effectiveness.
    • +
    +

    In contrast, a feature factory focuses on the quantity of new features rather than their quality:

    +

    Feature Factory

    +
    +

    It is fundamentally the confusion between effectiveness and efficiency that stands between doing the right things and doing things right. There is surely nothing quite so useless as doing with great efficiency what should not be done at all. — Peter Drucker

    +
    +

    Test Automation Guidelines

    +

    We strive for complete test coverage as it is a useful tool for finding +untested parts of our code base. Test coverage is of limited use as a numerical statement of how good our tests are.

    +

    The F.I.R.S.T. Principle includes five rules that good tests should follow:2

    +
      +
    • Fast. If tests are slow, you won't run them frequently, which makes them much less useful and increases the cost of development.
    • +
    • Independent. You should be able to run each test independently and run the tests in any order you like. When tests depend on each other, then the first one to fail causes a cascade of downstream failures, making diagnosis difficult and hiding downstream defects.
    • +
    • Repeatable. If your tests aren’t repeatable in any environment, then you’ll always have an excuse for why they fail. You’ll also find yourself unable to run the tests when the environment isn’t available.
    • +
    • Self-Validating. You should not have to read through a log file to tell whether the tests pass. If the tests aren’t +self-validating, then failure can become subjective and running the tests can require a long manual evaluation.
    • +
    • Timely. If you write the tests after the production code, you will generally find that the production code is difficult to test. Instead, add tests at implementation time to ensure that the code is testable, does what you expect it to do, and meets the requirements.
    • +
    +
    +

    Code that cannot be tested is flawed. — Anonymous

    +
    +

    Code Quality Reports

    +

    goreportcard.com generates reports on the quality of Open Source Go projects. It uses several measures, +including gofmt, go vet, go lint and gocyclo. If you find this helpful and also use the tool for your own projects, you can support the developers on Patreon.

    +

    Code Quality

    +
    +

    Take inspiration from quality reports, but keep in mind that not every reported issue must be fixed immediately.

    +
    +

    Security Best Practices

    +

    The Open Source Security Foundation (OpenSSF) maintains standardized security criteria and best practices for open-source projects:

    +

    OpenSSF Best Practices

    +

    View Security Testing Guide ›

    + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/configuration/index.html b/developer-guide/configuration/index.html new file mode 100644 index 0000000000..9c0ccabf13 --- /dev/null +++ b/developer-guide/configuration/index.html @@ -0,0 +1,7609 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Config Options - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Configuration

    +

    Command-Line Interface

    +

    The photoprism help (or photoprism --help) command lists all supported subcommands and config options available in the current version:

    +
    ./photoprism help
    +
    +

    Run the following to display all global config options and their current values:

    +
    ./photoprism show config
    +
    +

    You can also use the photoprism show command to display supported search filters, file types, thumbnail sizes, and metadata tags:

    +
    ./photoprism show --help
    +
    +

    Config Options

    +

    You can either use environment variables like PHOTOPRISM_CACHE_PATH, config files (in the storage/config directory) or command line parameters like --cache-path to change the value of config options.

    +

    Learn more ›

    +
    +

    Changes to the config options always require a restart to take effect. For development purposes, you do not need to change any settings if you followed the steps described under Build Setup.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/database/index.html b/developer-guide/database/index.html new file mode 100644 index 0000000000..e5dfb77211 --- /dev/null +++ b/developer-guide/database/index.html @@ -0,0 +1,7658 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Introduction - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Database Schema

    +
    +

    This schema description is for illustrative purposes only and should not be used to update or replace an existing production database.

    +
    +

    Entity-Relationship Diagram

    +

    docs.photoprism.app/developer-guide/database/schema/

    +

    Mermaid Markup

    +

    With Mermaid.js you can generate visual diagrams from this markup file:

    +

    mariadb.mmd

    +

    MariaDB SQL Dump

    +

    An SQL schema dump can be created using the command shown below, for example:

    +

    mariadb.sql

    +

    If you need an updated dump, you can run this command in your development environment:

    +
    mariadb-dump --no-data --skip-add-locks --skip-comments \
    + --skip-opt --skip-set-charset photoprism > mariadb.sql
    +
    +

    Please note that the dump we provide is only updated at irregular intervals and should therefore not be used to update or replace an existing production database.

    +

    Schema Migrations

    +

    docs.photoprism.app/developer-guide/database/migrations/

    +

    github.com/photoprism/photoprism/tree/develop/internal/migrate

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/database/migrations/index.html b/developer-guide/database/migrations/index.html new file mode 100644 index 0000000000..4767b298e3 --- /dev/null +++ b/developer-guide/database/migrations/index.html @@ -0,0 +1,7661 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Migrations - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Schema Migrations

    +
    +

    When using Docker Compose, you can prepend commands like docker compose exec [service] [command] to run them in a service container. +Should this fail with no container found, make sure the service has been started, you have specified an existing service (usually photoprism) and you are in the folder where your compose.yaml or docker-compose.yml file is located.

    +
    +

    Show Migration Status

    +

    Run the photoprism migrations ls command in a terminal to see a list of all migrations and their current status:

    +
    $ photoprism migrations ls
    +
    +|-----------------|---------|---------------------|---------------------|--------|
    +|       ID        | Dialect |     Started At      |     Finished At     | Status |
    +|-----------------|---------|---------------------|---------------------|--------|
    +| 20211121-094727 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20211124-120008 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-030000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-040000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-050000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-060000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-061000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-070000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-071000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-080000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-081000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-083000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-090000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-091000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-093000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220421-200000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220521-000001 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220521-000002 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220521-000003 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +|-----------------|---------|---------------------|---------------------|--------|
    +
    +

    Run Specific Migrations

    +

    To explicitly re-run specific migrations, you can pass them as arguments to the photoprism migrations run command:

    +
    $ photoprism migrations run 20220521-000003
    +
    +INFO[2022-07-12T11:45:29Z] migrate: 20220521-000003 successful [12.967654ms] 
    +INFO[2022-07-12T11:45:29Z] migration completed in 40.89123ms            
    +INFO[2022-07-12T11:45:29Z] closed database connection 
    +
    +

    Retry Failed Migrations

    +

    To automatically retry previously failed migrations, pass the -f flag to the photoprism migrations run command:

    +
    $ photoprism migrations run -f
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/database/schema/diagram.svg b/developer-guide/database/schema/diagram.svg new file mode 100644 index 0000000000..df8e1b2df2 --- /dev/null +++ b/developer-guide/database/schema/diagram.svg @@ -0,0 +1 @@ +
    user_uid
    user_uid
    user_uid
    user_uid
    cell_id
    place_id
    label_id
    category_id
    place_id
    photo_id
    photo_id
    file_id
    service_id
    file_id
    service_id
    file_uid
    face_id
    subject_uid
    subject_uid
    camera_id
    cell_id
    lens_id
    place_id
    photo_country
    album_country
    path
    created_by
    user_uid
    place_country
    keyword_id
    photo_id
    label_id
    photo_id
    album_uid
    photo_uid
    user_uid
    created_by
    uid
    user_uid
    client_uid
    user_uid
    photo_uid
    album_uid
    user_uid
    albums
    datetime created_at
    datetime updated_at
    datetime published_at
    datetime deleted_at
    varbinary(42) : album_uid
    varbinary(42) : parent_uid
    varbinary(160) : album_slug
    varchar(1024) : album_path
    varbinary(8) : album_type
    varchar(160) : album_title
    varchar(160) : album_location
    varchar(100) : album_category
    varchar(1024) : album_caption
    varchar(2048) : album_description
    varchar(1024) : album_notes
    varbinary(2048) : album_filter
    varbinary(32) : album_order
    varbinary(255) : album_template
    varchar(100) : album_state
    varbinary(2) : album_country
    int(11) : album_year
    int(11) : album_month
    int(11) : album_day
    tinyint(1) : album_favorite
    tinyint(1) : album_private
    varbinary(128) : thumb
    varbinary(8) : thumb_src
    varbinary(42) : created_by
    int(10) : unsigned id
    albums_users
    varbinary(42) : team_uid
    int(10) : unsigned perm
    varbinary(42) : uid
    varbinary(42) : user_uid
    auth_clients
    datetime created_at
    datetime updated_at
    datetime expires_at
    datetime deleted_at
    varbinary(42) : user_uid
    varchar(200) : client_name
    varbinary(16) : client_type
    varbinary(255) : callback_url
    varbinary(128) : auth_provider
    varbinary(128) : auth_method
    varchar(1024) : auth_scope
    bigint(20) : auth_expires
    bigint(20) : auth_timeout
    tinyint(1) : auth_enabled
    bigint(20) : last_active
    varbinary(16) : ref_id
    varchar(64) : user_name
    varbinary(255) : client_url
    varbinary(255) : auth_domain
    varbinary(255) : auth_id
    bigint(20) : auth_tokens
    varbinary(42) : client_uid
    auth_sessions
    datetime login_at
    datetime created_at
    datetime updated_at
    varchar(64) : client_ip
    varbinary(42) : user_uid
    varchar(64) : user_name
    varbinary(128) : auth_provider
    varbinary(128) : auth_method
    varbinary(255) : auth_domain
    varbinary(128) : auth_id
    varchar(1024) : auth_scope
    bigint(20) : last_active
    bigint(20) : sess_expires
    bigint(20) : sess_timeout
    varbinary(64) : preview_token
    varbinary(64) : download_token
    varbinary(4096) : access_token
    varbinary(512) : refresh_token
    varbinary(1024) : id_token
    varchar(512) : user_agent
    varbinary(4096) : data_json
    varbinary(16) : ref_id
    varchar(64) : login_ip
    varbinary(2048) : id
    auth_users
    datetime login_at
    datetime expires_at
    datetime verified_at
    datetime consent_at
    datetime born_at
    datetime created_at
    datetime updated_at
    datetime deleted_at
    varbinary(64) : user_uuid
    varbinary(42) : user_uid
    varbinary(128) : auth_provider
    varbinary(255) : auth_id
    varchar(255) : user_name
    varchar(200) : display_name
    varchar(255) : user_email
    varchar(255) : backup_email
    varchar(64) : user_role
    varchar(1024) : user_attr
    tinyint(1) : super_admin
    tinyint(1) : can_login
    tinyint(1) : webdav
    varbinary(1024) : base_path
    varbinary(1024) : upload_path
    tinyint(1) : can_invite
    varbinary(64) : invite_token
    varchar(64) : invited_by
    varbinary(64) : verify_token
    varbinary(64) : reset_token
    varbinary(64) : preview_token
    varbinary(64) : download_token
    varbinary(128) : thumb
    varbinary(8) : thumb_src
    varbinary(16) : ref_id
    int(11) : id
    auth_users_details
    datetime created_at
    datetime updated_at
    varbinary(42) : subj_uid
    varbinary(8) : subj_src
    varbinary(42) : place_id
    varbinary(8) : place_src
    varbinary(42) : cell_id
    int(11) : birth_year
    int(11) : birth_month
    int(11) : birth_day
    varchar(32) : name_title
    varchar(64) : given_name
    varchar(64) : middle_name
    varchar(64) : family_name
    varchar(32) : name_suffix
    varchar(64) : nick_name
    varbinary(8) : name_src
    varchar(16) : user_gender
    varchar(512) : user_about
    varchar(2048) : user_bio
    varchar(512) : user_location
    varbinary(2) : user_country
    varchar(32) : user_phone
    varbinary(512) : site_url
    varbinary(512) : profile_url
    varbinary(512) : feed_url
    varbinary(512) : avatar_url
    varchar(64) : org_title
    varchar(128) : org_name
    varchar(255) : org_email
    varchar(32) : org_phone
    varbinary(512) : org_url
    varbinary(512) : id_url
    varbinary(42) : user_uid
    auth_users_settings
    datetime created_at
    datetime updated_at
    varbinary(32) : ui_theme
    varbinary(32) : ui_language
    varbinary(64) : ui_time_zone
    varbinary(32) : maps_style
    int(11) : maps_animate
    varbinary(1024) : index_path
    int(11) : index_rescan
    varbinary(1024) : import_path
    int(11) : import_move
    int(11) : download_originals
    int(11) : download_media_raw
    int(11) : download_media_sidecar
    varbinary(1024) : upload_path
    varbinary(128) : default_page
    varbinary(42) : user_uid
    auth_users_shares
    datetime expires_at
    datetime created_at
    datetime updated_at
    varbinary(42) : link_uid
    varchar(512) : comment
    int(10) : unsigned perm
    varbinary(16) : ref_id
    varbinary(42) : user_uid
    varbinary(42) : share_uid
    cameras
    datetime created_at
    datetime updated_at
    datetime deleted_at
    varbinary(160) : camera_slug
    varchar(160) : camera_name
    varchar(160) : camera_make
    varchar(160) : camera_model
    varchar(100) : camera_type
    varchar(2048) : camera_description
    varchar(1024) : camera_notes
    int(10) : unsigned id
    categories
    int(10) : unsigned label_id
    int(10) : unsigned category_id
    cells
    datetime created_at
    datetime updated_at
    varchar(200) : cell_name
    varchar(100) : cell_street
    varchar(50) : cell_postcode
    varchar(50) : cell_category
    varbinary(42) : place_id
    varbinary(42) : id
    countries
    varbinary(160) : country_slug
    varchar(160) : country_name
    varchar(2048) : country_description
    varchar(1024) : country_notes
    int(10) : unsigned country_photo_id
    varbinary(2) : id
    details
    datetime created_at
    datetime updated_at
    varchar(2048) : keywords
    varbinary(8) : keywords_src
    varchar(2048) : notes
    varbinary(8) : notes_src
    varchar(1024) : subject
    varbinary(8) : subject_src
    varchar(1024) : artist
    varbinary(8) : artist_src
    varchar(1024) : copyright
    varbinary(8) : copyright_src
    varchar(1024) : license
    varbinary(8) : license_src
    varchar(1024) : software
    varbinary(8) : software_src
    int(10) : unsigned photo_id
    duplicates
    varbinary(128) : file_hash
    bigint(20) : file_size
    bigint(20) : mod_time
    varbinary(755) : file_name
    varbinary(16) : file_root
    errors
    datetime error_time
    varbinary(32) : error_level
    varbinary(2048) : error_message
    int(10) : unsigned id
    faces
    double sample_radius
    double collision_radius
    mediumblob embedding_json
    datetime matched_at
    datetime created_at
    datetime updated_at
    varbinary(8) : face_src
    int(11) : face_kind
    tinyint(1) : face_hidden
    varbinary(42) : subj_uid
    int(11) : samples
    int(11) : collisions
    varbinary(64) : id
    files
    datetime photo_taken_at
    double file_fps
    float file_aspect_ratio
    datetime created_at
    datetime updated_at
    datetime published_at
    datetime deleted_at
    int(10) : unsigned photo_id
    varbinary(42) : photo_uid
    varbinary(64) : time_index
    varbinary(32) : media_id
    bigint(20) : media_utc
    varbinary(64) : instance_id
    varbinary(42) : file_uid
    varbinary(1024) : file_name
    varbinary(16) : file_root
    varbinary(755) : original_name
    varbinary(128) : file_hash
    bigint(20) : file_size
    varbinary(32) : file_codec
    varbinary(16) : file_type
    varbinary(16) : media_type
    varbinary(64) : file_mime
    tinyint(1) : file_primary
    tinyint(1) : file_sidecar
    tinyint(1) : file_missing
    tinyint(1) : file_portrait
    tinyint(1) : file_video
    bigint(20) : file_duration
    int(11) : file_frames
    int(11) : file_width
    int(11) : file_height
    int(11) : file_orientation
    varbinary(8) : file_orientation_src
    varbinary(64) : file_projection
    tinyint(1) : file_hdr
    tinyint(1) : file_watermark
    varbinary(64) : file_color_profile
    varbinary(16) : file_main_color
    varbinary(18) : file_colors
    varbinary(18) : File_luminance
    int(11) : file_diff
    smallint(6) : file_chroma
    varchar(64) : file_software
    varbinary(512) : file_error
    bigint(20) : mod_time
    bigint(20) : created_in
    bigint(20) : updated_in
    int(10) : unsigned id
    files_share
    datetime created_at
    datetime updated_at
    varbinary(16) : status
    varbinary(512) : error
    int(11) : errors
    int(10) : unsigned file_id
    int(10) : unsigned service_id
    varbinary(255) : remote_name
    files_sync
    datetime remote_date
    datetime created_at
    datetime updated_at
    int(10) : unsigned file_id
    bigint(20) : remote_size
    varbinary(16) : status
    varbinary(512) : error
    int(11) : errors
    varbinary(255) : remote_name
    int(10) : unsigned service_id
    folders
    datetime created_at
    datetime updated_at
    datetime modified_at
    datetime published_at
    datetime deleted_at
    varbinary(1024) : path
    varbinary(16) : root
    varbinary(16) : folder_type
    varchar(200) : folder_title
    varchar(100) : folder_category
    varchar(2048) : folder_description
    varbinary(32) : folder_order
    varbinary(2) : folder_country
    int(11) : folder_year
    int(11) : folder_month
    int(11) : folder_day
    tinyint(1) : folder_favorite
    tinyint(1) : folder_private
    tinyint(1) : folder_ignore
    tinyint(1) : folder_watch
    varbinary(42) : folder_uid
    keywords
    varchar(64) : keyword
    tinyint(1) : skip
    int(10) : unsigned id
    labels
    datetime created_at
    datetime updated_at
    datetime published_at
    datetime deleted_at
    varbinary(42) : label_uid
    varbinary(160) : label_slug
    varbinary(160) : custom_slug
    varchar(160) : label_name
    int(11) : label_priority
    tinyint(1) : label_favorite
    varchar(2048) : label_description
    varchar(1024) : label_notes
    int(11) : photo_count
    varbinary(128) : thumb
    varbinary(8) : thumb_src
    int(10) : unsigned id
    lenses
    datetime created_at
    datetime updated_at
    datetime deleted_at
    varbinary(160) : lens_slug
    varchar(160) : lens_name
    varchar(160) : lens_make
    varchar(160) : lens_model
    varchar(100) : lens_type
    varchar(2048) : lens_description
    varchar(1024) : lens_notes
    int(10) : unsigned id
    links
    datetime created_at
    datetime modified_at
    varbinary(42) : share_uid
    varbinary(160) : share_slug
    varbinary(160) : link_token
    int(11) : link_expires
    int(10) : unsigned link_views
    int(10) : unsigned max_views
    tinyint(1) : has_password
    varchar(512) : comment
    int(10) : unsigned perm
    varbinary(16) : ref_id
    varbinary(42) : created_by
    varbinary(42) : link_uid
    markers
    double face_dist
    mediumblob embeddings_json
    mediumblob landmarks_json
    float x
    float y
    float w
    float h
    datetime matched_at
    datetime created_at
    datetime updated_at
    varbinary(42) : file_uid
    varbinary(8) : marker_type
    varbinary(8) : marker_src
    varchar(160) : marker_name
    tinyint(1) : marker_review
    tinyint(1) : marker_invalid
    varbinary(42) : subj_uid
    varbinary(8) : subj_src
    varbinary(64) : face_id
    int(11) : q
    int(11) : size
    smallint(6) : score
    varbinary(128) : thumb
    varbinary(42) : marker_uid
    passwords
    datetime created_at
    datetime updated_at
    varbinary(255) : hash
    varbinary(255) : uid
    photos
    datetime taken_at
    datetime taken_at_local
    float photo_lat
    float photo_lng
    float photo_f_number
    datetime created_at
    datetime updated_at
    datetime edited_at
    datetime published_at
    datetime checked_at
    datetime estimated_at
    datetime deleted_at
    varbinary(64) : uuid
    varbinary(8) : taken_src
    varbinary(42) : photo_uid
    varbinary(8) : photo_type
    varbinary(8) : type_src
    varchar(200) : photo_title
    varbinary(8) : title_src
    varchar(4096) : photo_description
    varbinary(8) : description_src
    varbinary(1024) : photo_path
    varbinary(255) : photo_name
    varbinary(755) : original_name
    tinyint(4) : photo_stack
    tinyint(1) : photo_favorite
    tinyint(1) : photo_private
    tinyint(1) : photo_scan
    tinyint(1) : photo_panorama
    varbinary(64) : time_zone
    varbinary(42) : place_id
    varbinary(8) : place_src
    varbinary(42) : cell_id
    int(11) : cell_accuracy
    int(11) : photo_altitude
    varbinary(2) : photo_country
    int(11) : photo_year
    int(11) : photo_month
    int(11) : photo_day
    int(11) : photo_iso
    varbinary(64) : photo_exposure
    int(11) : photo_focal_length
    smallint(6) : photo_quality
    int(11) : photo_faces
    smallint(6) : photo_resolution
    bigint(20) : photo_duration
    smallint(6) : photo_color
    int(10) : unsigned camera_id
    varbinary(160) : camera_serial
    varbinary(8) : camera_src
    int(10) : unsigned lens_id
    varbinary(42) : created_by
    int(10) : unsigned id
    photos_albums
    datetime created_at
    datetime updated_at
    int(11) : order
    tinyint(1) : hidden
    tinyint(1) : missing
    varbinary(42) : photo_uid
    varbinary(42) : album_uid
    photos_keywords
    int(10) : unsigned photo_id
    int(10) : unsigned keyword_id
    photos_labels
    varbinary(8) : label_src
    smallint(6) : uncertainty
    int(10) : unsigned photo_id
    int(10) : unsigned label_id
    photos_users
    varbinary(42) : team_uid
    int(10) : unsigned perm
    varbinary(42) : uid
    varbinary(42) : user_uid
    places
    datetime created_at
    datetime updated_at
    varchar(400) : place_label
    varchar(100) : place_district
    varchar(100) : place_city
    varchar(100) : place_state
    varbinary(2) : place_country
    varchar(300) : place_keywords
    tinyint(1) : place_favorite
    int(11) : photo_count
    varbinary(42) : id
    reactions
    datetime reacted_at
    int(11) : reacted
    varbinary(42) : uid
    varbinary(42) : user_uid
    varbinary(64) : reaction
    services
    datetime sync_date
    datetime created_at
    datetime updated_at
    datetime deleted_at
    varchar(160) : acc_name
    varchar(160) : acc_owner
    varchar(255) : acc_url
    varbinary(255) : acc_type
    varbinary(255) : acc_key
    varbinary(255) : acc_user
    varbinary(255) : acc_pass
    varbinary(16) : acc_timeout
    varbinary(512) : acc_error
    int(11) : acc_errors
    tinyint(1) : acc_share
    tinyint(1) : acc_sync
    int(11) : retry_limit
    varbinary(1024) : share_path
    varbinary(16) : share_size
    int(11) : share_expires
    varbinary(1024) : sync_path
    varbinary(16) : sync_status
    int(11) : sync_interval
    tinyint(1) : sync_upload
    tinyint(1) : sync_download
    tinyint(1) : sync_filenames
    tinyint(1) : sync_raw
    int(10) : unsigned id
    subjects
    datetime created_at
    datetime updated_at
    datetime deleted_at
    varbinary(8) : subj_type
    varbinary(8) : subj_src
    varbinary(160) : subj_slug
    varchar(160) : subj_name
    varchar(160) : subj_alias
    varchar(512) : subj_about
    varchar(2048) : subj_bio
    varchar(1024) : subj_notes
    tinyint(1) : subj_favorite
    tinyint(1) : subj_hidden
    tinyint(1) : subj_private
    tinyint(1) : subj_excluded
    int(11) : file_count
    int(11) : photo_count
    varbinary(128) : thumb
    varbinary(8) : thumb_src
    varbinary(42) : subj_uid
    users
    \ No newline at end of file diff --git a/developer-guide/database/schema/index.html b/developer-guide/database/schema/index.html new file mode 100644 index 0000000000..f28a3d0951 --- /dev/null +++ b/developer-guide/database/schema/index.html @@ -0,0 +1,7524 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ER Diagram - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + + + +

    Entity-Relationship Diagram

    +

    Please keep in mind that some information may be outdated or missing, as these docs were automatically generated from the source code.

    +
    +
    user_uid
    user_uid
    user_uid
    user_uid
    cell_id
    place_id
    label_id
    category_id
    place_id
    photo_id
    photo_id
    file_id
    service_id
    file_id
    service_id
    file_uid
    face_id
    subject_uid
    subject_uid
    camera_id
    cell_id
    lens_id
    place_id
    photo_country
    album_country
    path
    created_by
    user_uid
    place_country
    keyword_id
    photo_id
    label_id
    photo_id
    album_uid
    photo_uid
    user_uid
    created_by
    uid
    user_uid
    client_uid
    user_uid
    photo_uid
    album_uid
    user_uid
    albums
    datetime created_at
    datetime updated_at
    datetime published_at
    datetime deleted_at
    varbinary(42) : album_uid
    varbinary(42) : parent_uid
    varbinary(160) : album_slug
    varchar(1024) : album_path
    varbinary(8) : album_type
    varchar(160) : album_title
    varchar(160) : album_location
    varchar(100) : album_category
    varchar(1024) : album_caption
    varchar(2048) : album_description
    varchar(1024) : album_notes
    varbinary(2048) : album_filter
    varbinary(32) : album_order
    varbinary(255) : album_template
    varchar(100) : album_state
    varbinary(2) : album_country
    int(11) : album_year
    int(11) : album_month
    int(11) : album_day
    tinyint(1) : album_favorite
    tinyint(1) : album_private
    varbinary(128) : thumb
    varbinary(8) : thumb_src
    varbinary(42) : created_by
    int(10) : unsigned id
    albums_users
    varbinary(42) : team_uid
    int(10) : unsigned perm
    varbinary(42) : uid
    varbinary(42) : user_uid
    auth_clients
    datetime created_at
    datetime updated_at
    datetime expires_at
    datetime deleted_at
    varbinary(42) : user_uid
    varchar(200) : client_name
    varbinary(16) : client_type
    varbinary(255) : callback_url
    varbinary(128) : auth_provider
    varbinary(128) : auth_method
    varchar(1024) : auth_scope
    bigint(20) : auth_expires
    bigint(20) : auth_timeout
    tinyint(1) : auth_enabled
    bigint(20) : last_active
    varbinary(16) : ref_id
    varchar(64) : user_name
    varbinary(255) : client_url
    varbinary(255) : auth_domain
    varbinary(255) : auth_id
    bigint(20) : auth_tokens
    varbinary(42) : client_uid
    auth_sessions
    datetime login_at
    datetime created_at
    datetime updated_at
    varchar(64) : client_ip
    varbinary(42) : user_uid
    varchar(64) : user_name
    varbinary(128) : auth_provider
    varbinary(128) : auth_method
    varbinary(255) : auth_domain
    varbinary(128) : auth_id
    varchar(1024) : auth_scope
    bigint(20) : last_active
    bigint(20) : sess_expires
    bigint(20) : sess_timeout
    varbinary(64) : preview_token
    varbinary(64) : download_token
    varbinary(4096) : access_token
    varbinary(512) : refresh_token
    varbinary(1024) : id_token
    varchar(512) : user_agent
    varbinary(4096) : data_json
    varbinary(16) : ref_id
    varchar(64) : login_ip
    varbinary(2048) : id
    auth_users
    datetime login_at
    datetime expires_at
    datetime verified_at
    datetime consent_at
    datetime born_at
    datetime created_at
    datetime updated_at
    datetime deleted_at
    varbinary(64) : user_uuid
    varbinary(42) : user_uid
    varbinary(128) : auth_provider
    varbinary(255) : auth_id
    varchar(255) : user_name
    varchar(200) : display_name
    varchar(255) : user_email
    varchar(255) : backup_email
    varchar(64) : user_role
    varchar(1024) : user_attr
    tinyint(1) : super_admin
    tinyint(1) : can_login
    tinyint(1) : webdav
    varbinary(1024) : base_path
    varbinary(1024) : upload_path
    tinyint(1) : can_invite
    varbinary(64) : invite_token
    varchar(64) : invited_by
    varbinary(64) : verify_token
    varbinary(64) : reset_token
    varbinary(64) : preview_token
    varbinary(64) : download_token
    varbinary(128) : thumb
    varbinary(8) : thumb_src
    varbinary(16) : ref_id
    int(11) : id
    auth_users_details
    datetime created_at
    datetime updated_at
    varbinary(42) : subj_uid
    varbinary(8) : subj_src
    varbinary(42) : place_id
    varbinary(8) : place_src
    varbinary(42) : cell_id
    int(11) : birth_year
    int(11) : birth_month
    int(11) : birth_day
    varchar(32) : name_title
    varchar(64) : given_name
    varchar(64) : middle_name
    varchar(64) : family_name
    varchar(32) : name_suffix
    varchar(64) : nick_name
    varbinary(8) : name_src
    varchar(16) : user_gender
    varchar(512) : user_about
    varchar(2048) : user_bio
    varchar(512) : user_location
    varbinary(2) : user_country
    varchar(32) : user_phone
    varbinary(512) : site_url
    varbinary(512) : profile_url
    varbinary(512) : feed_url
    varbinary(512) : avatar_url
    varchar(64) : org_title
    varchar(128) : org_name
    varchar(255) : org_email
    varchar(32) : org_phone
    varbinary(512) : org_url
    varbinary(512) : id_url
    varbinary(42) : user_uid
    auth_users_settings
    datetime created_at
    datetime updated_at
    varbinary(32) : ui_theme
    varbinary(32) : ui_language
    varbinary(64) : ui_time_zone
    varbinary(32) : maps_style
    int(11) : maps_animate
    varbinary(1024) : index_path
    int(11) : index_rescan
    varbinary(1024) : import_path
    int(11) : import_move
    int(11) : download_originals
    int(11) : download_media_raw
    int(11) : download_media_sidecar
    varbinary(1024) : upload_path
    varbinary(128) : default_page
    varbinary(42) : user_uid
    auth_users_shares
    datetime expires_at
    datetime created_at
    datetime updated_at
    varbinary(42) : link_uid
    varchar(512) : comment
    int(10) : unsigned perm
    varbinary(16) : ref_id
    varbinary(42) : user_uid
    varbinary(42) : share_uid
    cameras
    datetime created_at
    datetime updated_at
    datetime deleted_at
    varbinary(160) : camera_slug
    varchar(160) : camera_name
    varchar(160) : camera_make
    varchar(160) : camera_model
    varchar(100) : camera_type
    varchar(2048) : camera_description
    varchar(1024) : camera_notes
    int(10) : unsigned id
    categories
    int(10) : unsigned label_id
    int(10) : unsigned category_id
    cells
    datetime created_at
    datetime updated_at
    varchar(200) : cell_name
    varchar(100) : cell_street
    varchar(50) : cell_postcode
    varchar(50) : cell_category
    varbinary(42) : place_id
    varbinary(42) : id
    countries
    varbinary(160) : country_slug
    varchar(160) : country_name
    varchar(2048) : country_description
    varchar(1024) : country_notes
    int(10) : unsigned country_photo_id
    varbinary(2) : id
    details
    datetime created_at
    datetime updated_at
    varchar(2048) : keywords
    varbinary(8) : keywords_src
    varchar(2048) : notes
    varbinary(8) : notes_src
    varchar(1024) : subject
    varbinary(8) : subject_src
    varchar(1024) : artist
    varbinary(8) : artist_src
    varchar(1024) : copyright
    varbinary(8) : copyright_src
    varchar(1024) : license
    varbinary(8) : license_src
    varchar(1024) : software
    varbinary(8) : software_src
    int(10) : unsigned photo_id
    duplicates
    varbinary(128) : file_hash
    bigint(20) : file_size
    bigint(20) : mod_time
    varbinary(755) : file_name
    varbinary(16) : file_root
    errors
    datetime error_time
    varbinary(32) : error_level
    varbinary(2048) : error_message
    int(10) : unsigned id
    faces
    double sample_radius
    double collision_radius
    mediumblob embedding_json
    datetime matched_at
    datetime created_at
    datetime updated_at
    varbinary(8) : face_src
    int(11) : face_kind
    tinyint(1) : face_hidden
    varbinary(42) : subj_uid
    int(11) : samples
    int(11) : collisions
    varbinary(64) : id
    files
    datetime photo_taken_at
    double file_fps
    float file_aspect_ratio
    datetime created_at
    datetime updated_at
    datetime published_at
    datetime deleted_at
    int(10) : unsigned photo_id
    varbinary(42) : photo_uid
    varbinary(64) : time_index
    varbinary(32) : media_id
    bigint(20) : media_utc
    varbinary(64) : instance_id
    varbinary(42) : file_uid
    varbinary(1024) : file_name
    varbinary(16) : file_root
    varbinary(755) : original_name
    varbinary(128) : file_hash
    bigint(20) : file_size
    varbinary(32) : file_codec
    varbinary(16) : file_type
    varbinary(16) : media_type
    varbinary(64) : file_mime
    tinyint(1) : file_primary
    tinyint(1) : file_sidecar
    tinyint(1) : file_missing
    tinyint(1) : file_portrait
    tinyint(1) : file_video
    bigint(20) : file_duration
    int(11) : file_frames
    int(11) : file_width
    int(11) : file_height
    int(11) : file_orientation
    varbinary(8) : file_orientation_src
    varbinary(64) : file_projection
    tinyint(1) : file_hdr
    tinyint(1) : file_watermark
    varbinary(64) : file_color_profile
    varbinary(16) : file_main_color
    varbinary(18) : file_colors
    varbinary(18) : File_luminance
    int(11) : file_diff
    smallint(6) : file_chroma
    varchar(64) : file_software
    varbinary(512) : file_error
    bigint(20) : mod_time
    bigint(20) : created_in
    bigint(20) : updated_in
    int(10) : unsigned id
    files_share
    datetime created_at
    datetime updated_at
    varbinary(16) : status
    varbinary(512) : error
    int(11) : errors
    int(10) : unsigned file_id
    int(10) : unsigned service_id
    varbinary(255) : remote_name
    files_sync
    datetime remote_date
    datetime created_at
    datetime updated_at
    int(10) : unsigned file_id
    bigint(20) : remote_size
    varbinary(16) : status
    varbinary(512) : error
    int(11) : errors
    varbinary(255) : remote_name
    int(10) : unsigned service_id
    folders
    datetime created_at
    datetime updated_at
    datetime modified_at
    datetime published_at
    datetime deleted_at
    varbinary(1024) : path
    varbinary(16) : root
    varbinary(16) : folder_type
    varchar(200) : folder_title
    varchar(100) : folder_category
    varchar(2048) : folder_description
    varbinary(32) : folder_order
    varbinary(2) : folder_country
    int(11) : folder_year
    int(11) : folder_month
    int(11) : folder_day
    tinyint(1) : folder_favorite
    tinyint(1) : folder_private
    tinyint(1) : folder_ignore
    tinyint(1) : folder_watch
    varbinary(42) : folder_uid
    keywords
    varchar(64) : keyword
    tinyint(1) : skip
    int(10) : unsigned id
    labels
    datetime created_at
    datetime updated_at
    datetime published_at
    datetime deleted_at
    varbinary(42) : label_uid
    varbinary(160) : label_slug
    varbinary(160) : custom_slug
    varchar(160) : label_name
    int(11) : label_priority
    tinyint(1) : label_favorite
    varchar(2048) : label_description
    varchar(1024) : label_notes
    int(11) : photo_count
    varbinary(128) : thumb
    varbinary(8) : thumb_src
    int(10) : unsigned id
    lenses
    datetime created_at
    datetime updated_at
    datetime deleted_at
    varbinary(160) : lens_slug
    varchar(160) : lens_name
    varchar(160) : lens_make
    varchar(160) : lens_model
    varchar(100) : lens_type
    varchar(2048) : lens_description
    varchar(1024) : lens_notes
    int(10) : unsigned id
    links
    datetime created_at
    datetime modified_at
    varbinary(42) : share_uid
    varbinary(160) : share_slug
    varbinary(160) : link_token
    int(11) : link_expires
    int(10) : unsigned link_views
    int(10) : unsigned max_views
    tinyint(1) : has_password
    varchar(512) : comment
    int(10) : unsigned perm
    varbinary(16) : ref_id
    varbinary(42) : created_by
    varbinary(42) : link_uid
    markers
    double face_dist
    mediumblob embeddings_json
    mediumblob landmarks_json
    float x
    float y
    float w
    float h
    datetime matched_at
    datetime created_at
    datetime updated_at
    varbinary(42) : file_uid
    varbinary(8) : marker_type
    varbinary(8) : marker_src
    varchar(160) : marker_name
    tinyint(1) : marker_review
    tinyint(1) : marker_invalid
    varbinary(42) : subj_uid
    varbinary(8) : subj_src
    varbinary(64) : face_id
    int(11) : q
    int(11) : size
    smallint(6) : score
    varbinary(128) : thumb
    varbinary(42) : marker_uid
    passwords
    datetime created_at
    datetime updated_at
    varbinary(255) : hash
    varbinary(255) : uid
    photos
    datetime taken_at
    datetime taken_at_local
    float photo_lat
    float photo_lng
    float photo_f_number
    datetime created_at
    datetime updated_at
    datetime edited_at
    datetime published_at
    datetime checked_at
    datetime estimated_at
    datetime deleted_at
    varbinary(64) : uuid
    varbinary(8) : taken_src
    varbinary(42) : photo_uid
    varbinary(8) : photo_type
    varbinary(8) : type_src
    varchar(200) : photo_title
    varbinary(8) : title_src
    varchar(4096) : photo_description
    varbinary(8) : description_src
    varbinary(1024) : photo_path
    varbinary(255) : photo_name
    varbinary(755) : original_name
    tinyint(4) : photo_stack
    tinyint(1) : photo_favorite
    tinyint(1) : photo_private
    tinyint(1) : photo_scan
    tinyint(1) : photo_panorama
    varbinary(64) : time_zone
    varbinary(42) : place_id
    varbinary(8) : place_src
    varbinary(42) : cell_id
    int(11) : cell_accuracy
    int(11) : photo_altitude
    varbinary(2) : photo_country
    int(11) : photo_year
    int(11) : photo_month
    int(11) : photo_day
    int(11) : photo_iso
    varbinary(64) : photo_exposure
    int(11) : photo_focal_length
    smallint(6) : photo_quality
    int(11) : photo_faces
    smallint(6) : photo_resolution
    bigint(20) : photo_duration
    smallint(6) : photo_color
    int(10) : unsigned camera_id
    varbinary(160) : camera_serial
    varbinary(8) : camera_src
    int(10) : unsigned lens_id
    varbinary(42) : created_by
    int(10) : unsigned id
    photos_albums
    datetime created_at
    datetime updated_at
    int(11) : order
    tinyint(1) : hidden
    tinyint(1) : missing
    varbinary(42) : photo_uid
    varbinary(42) : album_uid
    photos_keywords
    int(10) : unsigned photo_id
    int(10) : unsigned keyword_id
    photos_labels
    varbinary(8) : label_src
    smallint(6) : uncertainty
    int(10) : unsigned photo_id
    int(10) : unsigned label_id
    photos_users
    varbinary(42) : team_uid
    int(10) : unsigned perm
    varbinary(42) : uid
    varbinary(42) : user_uid
    places
    datetime created_at
    datetime updated_at
    varchar(400) : place_label
    varchar(100) : place_district
    varchar(100) : place_city
    varchar(100) : place_state
    varbinary(2) : place_country
    varchar(300) : place_keywords
    tinyint(1) : place_favorite
    int(11) : photo_count
    varbinary(42) : id
    reactions
    datetime reacted_at
    int(11) : reacted
    varbinary(42) : uid
    varbinary(42) : user_uid
    varbinary(64) : reaction
    services
    datetime sync_date
    datetime created_at
    datetime updated_at
    datetime deleted_at
    varchar(160) : acc_name
    varchar(160) : acc_owner
    varchar(255) : acc_url
    varbinary(255) : acc_type
    varbinary(255) : acc_key
    varbinary(255) : acc_user
    varbinary(255) : acc_pass
    varbinary(16) : acc_timeout
    varbinary(512) : acc_error
    int(11) : acc_errors
    tinyint(1) : acc_share
    tinyint(1) : acc_sync
    int(11) : retry_limit
    varbinary(1024) : share_path
    varbinary(16) : share_size
    int(11) : share_expires
    varbinary(1024) : sync_path
    varbinary(16) : sync_status
    int(11) : sync_interval
    tinyint(1) : sync_upload
    tinyint(1) : sync_download
    tinyint(1) : sync_filenames
    tinyint(1) : sync_raw
    int(10) : unsigned id
    subjects
    datetime created_at
    datetime updated_at
    datetime deleted_at
    varbinary(8) : subj_type
    varbinary(8) : subj_src
    varbinary(160) : subj_slug
    varchar(160) : subj_name
    varchar(160) : subj_alias
    varchar(512) : subj_about
    varchar(2048) : subj_bio
    varchar(1024) : subj_notes
    tinyint(1) : subj_favorite
    tinyint(1) : subj_hidden
    tinyint(1) : subj_private
    tinyint(1) : subj_excluded
    int(11) : file_count
    int(11) : photo_count
    varbinary(128) : thumb
    varbinary(8) : thumb_src
    varbinary(42) : subj_uid
    users
    +
    + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/directories/index.html b/developer-guide/directories/index.html new file mode 100644 index 0000000000..7118752eb2 --- /dev/null +++ b/developer-guide/directories/index.html @@ -0,0 +1,7516 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Project Structure - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Project Directory Structure

    +

    The directory layout that we use for our public project repository is loosely based on golang-standards/project-layout:

    +
      +
    • / contains a Makefile, a readme, the license and various config files for dependency management, building and continuous integration
    • +
    • /assets contains subdirectories for various assets such as photos, built JS/HTML/CSS files and server-side HTML templates
    • +
    • /cmd contains the application source code (main package)
    • +
    • /docker contains Dockerfiles and related assets, e.g. for Development, TensorFlow and ARM64
    • +
    • /frontend contains our Web frontend JS/HTML/CSS source code
    • +
    • /internal contains the source code of our internal Go packages
    • +
    • /pkg contains the source code of our public Go packages
    • +
    • /scripts contains shell scripts used for building, deployment and continuous integration
    • +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/documentation/index.html b/developer-guide/documentation/index.html new file mode 100644 index 0000000000..a40bd82df6 --- /dev/null +++ b/developer-guide/documentation/index.html @@ -0,0 +1,7613 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Documentation - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Documentation

    +

    User Facing Documentation

    +

    User-facing documentation lives in the photoprism-docs +repository and can be browsed on docs.photoprism.app.

    +

    See the README for how to easily edit the docs yourself.

    +

    Package Documentation (GoDoc)

    +

    Godoc generates documentation automatically, so it's tightly coupled to the photoprism source code.

    +

    + View Package Docs +

    + +

    Some highlights to explore:

    + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/faq/index.html b/developer-guide/faq/index.html new file mode 100644 index 0000000000..34f9b1ea43 --- /dev/null +++ b/developer-guide/faq/index.html @@ -0,0 +1,7745 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FAQ - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Frequently Asked Questions

    +

    Can your development environment be used under Windows?

    +

    Yes, this is possible if you have Git and Docker Desktop installed. However, you are likely to experience problems when using our Makefile and other scripts directly on Windows, as they were developed and tested on Linux/Unix.

    +

    We therefore recommend not to use Make for setting up your development environment and to instead run the required commands manually from the main project directory (where the compose.yaml file is located):

    +
    docker compose --profile=all pull --ignore-pull-failures
    +docker compose build
    +docker compose up
    +
    +

    Once the services have been built and started as shown above, you can open a terminal session:

    +
    docker compose exec photoprism bash
    +
    +

    In addition, you should disable the "autocrlf" option in Git under Windows to avoid problems with Linux/Unix line endings:

    +
    git config --global core.autocrlf false
    +
    +

    That's it! Most of the other Make targets are used from within the terminal session, i.e. not under Windows, so no compatibility issues or missing dependencies are to be expected.

    +

    Learn more ›

    +

    Why not use a more permissive public license so that, for example, developers at Google can contribute more easily?

    +

    Since we had hoped for a collaboration with Google and were aware of their AGPL policy, PhotoPrism was initially licensed under Apache 2.0, which is much more permissive. However, no one seemed interested, although we did talk to quite a few personal acquaintances at Google. That made it easy for our community to convince us to use AGPL instead.

    +

    Isn't it insecure that thumbnail URLs work even if you are not logged in?

    +

    Like most commercial image hosting services, we've chosen to use a cookie-free thumbnail API to minimize request latency and avoid unnecessary network traffic. If you were to copy private session cookies and use them in a different browser window, you would have a similar problem, except that they also work for other API endpoints, not just a single image.

    +

    Even if URLs were to become invalid every minute: Digital copies are as good as originals. Once shared and downloaded, such images should be considered "leaked" because they are cached and can be re-shared by the recipient at any time, with no sure way to get all copies back. Any form of protection we could provide would essentially be "snake oil", could be circumvented, and would have a negative impact on the user experience, such as disabling the browser cache or context menu.

    +

    For the highest level of protection, it is recommended to shield your private server from the public Internet. Always use HTTPS, a VPN and/or ideally TLS client certificates and make sure that only people you trust have access to your instance.

    +

    Visit docs.photoprism.app/developer-guide/media/thumbnails/ to learn more.

    +

    Does your software depend on any external services?

    +

    As explained in our Privacy Policy, reverse geocoding and interactive world maps depend on retrieving the necessary information from us and MapTiler AG, headquartered in Switzerland. Both services are provided with a very high level of privacy and confidentiality.

    +

    Your use of these services is fully covered by us. Depending on your usage, this can save you much more than the cost of a PhotoPrism+ Membership, since other providers generally charge usage-based fees and often don't allow you to cache the data they provide, compromising performance and your privacy with unnecessary requests.

    +

    In order to use the maps and view location details in PhotoPrism, as well as successfully run our test suite as a developer, you must allow requests to these API endpoints if you have a firewall installed and make sure your Internet connection is working.

    +

    Should you wish to operate one or both of these services on your own premises, we can set up such a fully autonomous solution for you, provided you are prepared to cover the initial setup costs as well as ongoing maintenance fees for content licenses and updates.

    +

    View Privacy Policy › View Compliance FAQ ›

    +

    Other applications sometimes use the API that OpenStreetMap provides for development and testing. In this case, their usage and privacy policies apply, which means that your request data is stored and used to generate publicly available reports. This differs from our self-hosted service, which ensures your privacy and provides a better user experience with faster loading times.

    +

    Why do I see connection errors when requesting API keys at startup?

    +

    Retrieving location data with reverse geocoding and loading the interactive world maps we provide requires a connection to external services. Please make sure that requests to these API endpoints are allowed if you have a firewall installed, and that your internet connection is working.

    +

    Learn more ›

    +

    Are the keys for using interactive world maps provided free of charge?

    +

    All users have access to a high-resolution vector map that we host on our own infrastructure, so no commercial API key is required. It is based on data published by OpenStreetMap (OSM).

    +

    In addition, we automatically provide our members and business customers with an API key for MapTiler's commercial service, which includes satellite, outdoor and 3D maps. You can test these on our public demo.

    +

    Learn more ›

    +
    +

    Although experienced users could alternatively register test accounts with a commercial provider to gain access to additional map styles instead of signing up for a membership, we believe this would not be fair. Keep in mind that we have a much larger user base than others who might encourage their users to do so, and that providers might then stop offering free test accounts, which is something we don't want to be responsible for.

    +
    +

    Why don't you use the free map tile service provided by OpenStreetMap?

    +

    Other free and open-source software sometimes uses the public maps that OpenStreetMap provides for development and testing. These are not intended for end-user applications like ours.

    +

    Using their service also means that their usage and privacy policies apply, as your request data is stored and used to generate publicly available reports. This differs from our services, which ensure a high level of privacy and provide a better user experience with faster loading times.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/img/CLA.png b/developer-guide/img/CLA.png new file mode 100644 index 0000000000..0c414558d6 Binary files /dev/null and b/developer-guide/img/CLA.png differ diff --git a/developer-guide/img/CLAAgree.png b/developer-guide/img/CLAAgree.png new file mode 100644 index 0000000000..c7f5601929 Binary files /dev/null and b/developer-guide/img/CLAAgree.png differ diff --git a/developer-guide/img/DashboardUntranslated.png b/developer-guide/img/DashboardUntranslated.png new file mode 100644 index 0000000000..e2c533c2d4 Binary files /dev/null and b/developer-guide/img/DashboardUntranslated.png differ diff --git a/developer-guide/img/LICENSE b/developer-guide/img/LICENSE new file mode 100644 index 0000000000..a4b2f9e83e --- /dev/null +++ b/developer-guide/img/LICENSE @@ -0,0 +1,75 @@ + + PhotoPrism® Digital Assets + +TERMS OF USE + + (a) We hereby grant you a non-exclusive, non-transferable right to use the +Digital Assets accompanying the software, embedded in the documentation, or +provided for download — such as icons, fonts, illustrations, graphics, +wallpapers, videos, sounds, models, and sample files („Digital Assets“) — +as part of the Software distributed by us, unless otherwise noted. + + (b) Because some Digital Assets are licensed to us solely for direct +distribution, we cannot redistribute them under a more permissive license for +other purposes. If the author or copyright holder has not released them under a +permissive license, you must obtain a license before using them in your own +work, whether commercial or non-commercial in nature. + + (c) Related documentation is available under the terms of the CC BY-NC-SA 4.0 +License. Other terms may apply to Digital Assets — in particular +illustrations, graphics, and videos — embedded in the documentation if they +are licensed to us solely for direct distribution. When in doubt, please ask +before distributing or using them for other works. + +TRADEMARK AND BRAND ASSETS + + (a) PhotoPrism’s Brand Assets — including trademarks, logos, icons, fonts, +corporate design, product and service names, and any other brand features and +elements, whether registered or unregistered („Brand Assets“) — are +proprietary assets owned exclusively by PhotoPrism UG („PhotoPrism“). We +reserve the right to object to any use or misuse in any jurisdiction worldwide. +Visit photoprism.app/trademark to learn more. + + (b) Contributors, licensees, business partners, and other third parties may +never claim ownership of PhotoPrism's Brand Assets or brands confusingly +similar to PhotoPrism's Brand Assets in any way, including, without limitation, +as a trademark, service mark, company name or designation, domain name, social +media profile/handle, or in any other manner. + + (c) You may not include the PhotoPrism trademark in the name of your app, +product, or service, whether commercial or non-commercial in nature. This +includes online services such as e-commerce, community, blog, information, +advertising, and personal home pages, as well as apps, app stores, client apps, +or third-party apps that interact with PhotoPrism. + +DISCLAIMER OF WARRANTY + + OUR SOFTWARE, SERVICES, DOCUMENTATION, AND DIGITAL ASSETS ARE PROVIDED +“AS-IS” AND WITHOUT WARRANTY OF ANY KIND. WE DISCLAIM ALL WARRANTIES, +EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, TITLE, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE. + +LIMITATION OF LIABILITY + + WE WILL NOT BE LIABLE FOR ANY DAMAGES ASSOCIATED WITH OUR SOFTWARE AND +SERVICES, INCLUDING WITHOUT LIMITATION ORDINARY, INCIDENTAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OF ANY KIND, INCLUDING BUT NOT LIMITED TO DAMAGES +RELATING TO LOST DATA OR LOST PROFITS, EVEN IF WE HAVE BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +SEVERABILITY + + (a) If the Disclaimer of Warranty and Limitation of Liability set forth above +cannot be given local legal effect according to their terms, the reviewing +courts shall apply the local law that most closely approximates an absolute +waiver of any civil liability in connection with our software and services, +unless a written warranty or assumption of liability has been granted for a fee. + + (b) In the event that any provision set forth above is found by a court of +competent jurisdiction to be invalid, illegal, void or unenforceable, the +remaining provisions shall remain in full force and effect and shall not be +impaired, affected or invalidated in any way. + + (c) It is hereby agreed and declared that it is the intention of the parties to +enforce the remaining provisions without the inclusion of those provisions that +may subsequently be declared invalid, illegal, void or unenforceable. diff --git a/developer-guide/img/WeblateAddTranslation.png b/developer-guide/img/WeblateAddTranslation.png new file mode 100644 index 0000000000..3fadc3f9fb Binary files /dev/null and b/developer-guide/img/WeblateAddTranslation.png differ diff --git a/developer-guide/img/WeblateApproveString.png b/developer-guide/img/WeblateApproveString.png new file mode 100644 index 0000000000..3fe4977689 Binary files /dev/null and b/developer-guide/img/WeblateApproveString.png differ diff --git a/developer-guide/img/WeblateChooseLanguages.png b/developer-guide/img/WeblateChooseLanguages.png new file mode 100644 index 0000000000..aafb39ed7a Binary files /dev/null and b/developer-guide/img/WeblateChooseLanguages.png differ diff --git a/developer-guide/img/WeblateDashboard.png b/developer-guide/img/WeblateDashboard.png new file mode 100644 index 0000000000..e62797f698 Binary files /dev/null and b/developer-guide/img/WeblateDashboard.png differ diff --git a/developer-guide/img/WeblateEditAllStrings.png b/developer-guide/img/WeblateEditAllStrings.png new file mode 100644 index 0000000000..b66510ece6 Binary files /dev/null and b/developer-guide/img/WeblateEditAllStrings.png differ diff --git a/developer-guide/img/WeblateInitialSettings.png b/developer-guide/img/WeblateInitialSettings.png new file mode 100644 index 0000000000..d3fda03f11 Binary files /dev/null and b/developer-guide/img/WeblateInitialSettings.png differ diff --git a/developer-guide/img/WeblateOpenComponent.png b/developer-guide/img/WeblateOpenComponent.png new file mode 100644 index 0000000000..28503d4401 Binary files /dev/null and b/developer-guide/img/WeblateOpenComponent.png differ diff --git a/developer-guide/img/WeblateRegister1.png b/developer-guide/img/WeblateRegister1.png new file mode 100644 index 0000000000..e007e97732 Binary files /dev/null and b/developer-guide/img/WeblateRegister1.png differ diff --git a/developer-guide/img/WeblateRegister2.png b/developer-guide/img/WeblateRegister2.png new file mode 100644 index 0000000000..48e6367ea8 Binary files /dev/null and b/developer-guide/img/WeblateRegister2.png differ diff --git a/developer-guide/img/WeblateStringsForReview.png b/developer-guide/img/WeblateStringsForReview.png new file mode 100644 index 0000000000..000eac657d Binary files /dev/null and b/developer-guide/img/WeblateStringsForReview.png differ diff --git a/developer-guide/img/productivity-vs-time.jpg b/developer-guide/img/productivity-vs-time.jpg new file mode 100644 index 0000000000..3d00bb679a Binary files /dev/null and b/developer-guide/img/productivity-vs-time.jpg differ diff --git a/developer-guide/index.html b/developer-guide/index.html new file mode 100644 index 0000000000..c946335c90 --- /dev/null +++ b/developer-guide/index.html @@ -0,0 +1,7723 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Introduction - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Every Contribution Makes a Difference

    +

    We welcome contributions of any kind, including blog posts, tutorials, testing, writing documentation, and pull requests.

    +

    Our Milestones

    +

    +

    Upcoming Features and Enhancements

    +

    The public roadmap shows what tasks are in progress, what needs testing, and which features are going to be implemented next.

    +

    You are welcome to submit specific feature requests after you have verified that no similar issue already exists. Give ideas you like a thumbs-up 👍 , so that we know what is most popular.

    +

    Join the Community

    +

    Follow us on Mastodon, Bluesky, or join the Community Chat to get regular updates, connect with other users, and discuss your ideas. Our Code of Conduct explains the "dos and don’ts" when interacting with other community members.

    +

    As a contributor, you are also welcome to contact us directly if you have something on your mind that you don't want to discuss publicly. Please note, however, that due to the high volume of emails we receive, our team may be unable to get back to you immediately. We do our best to respond within five business days or less.

    +

    Creating Bug Reports

    +

    Before reporting a bug, first use our Troubleshooting Checklists to determine the cause of your problem. If you have a general question, need help, it could be a configuration issue, or a misunderstanding in how the software works:

    + +

    In order for us to investigate new bug reports, they must include a complete list of steps to reproduce the problem, the software versions used and information about the environment in which the problem occurred, such as browser type, browser version, browser plug-ins, operating system, storage type, processor type, and memory size.

    +

    A template for creating bug reports can be found at photoprism.app/kb/reporting-bugs. We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app.

    +
    +

    When browsing issues, please note that our team and all issue subscribers receive an email notification from GitHub whenever a new comment is added, so these should only be used for sharing important information and not for discussions, questions, or expressing personal opinions.Thank you very much!

    +
    +

    Submitting Pull Requests

    +

    Follow our step-by-step guide to learn how to submit new features, bug fixes, and documentation enhancements.

    +

    Pull requests solving "help wanted" issues are the easiest to merge and the most helpful to us, as they allow us to spend more time on core functionality and other issues that are difficult for external contributors to work on. If you are new to this project, anything labeled "easy" may be a good first contribution.

    +

    Be aware that reviewing, testing and finally merging pull requests requires significant resources on our side. It can therefore take several months if it is not just a small fix, especially if extensive testing is needed to prevent bugs from getting into our stable version.

    +

    Thank You to All Current and Past Sponsors

    +

    A big thank you to all of our sponsors, whose generous support has been and continues to be essential to the success of the project!

    +

    Our project infrastructure is provided by the following companies:

    + +

    View Sponsors › View Credits ›

    +
    + +
    +

    Privacy Notice

    +

    We operate a number of web services that help us develop and maintain our software in collaboration with the open source community, for example translate.photoprism.app.

    +

    Because many of these apps and tools were originally developed for internal use without a high level of privacy in mind, we ask that you do not enter personal information such as your real name or personal email address if you want it to remain private.

    +

    Personal details may otherwise show up in logs, source code, translation files, commit messages, and pull request comments.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/issues/index.html b/developer-guide/issues/index.html new file mode 100644 index 0000000000..9547ca3200 --- /dev/null +++ b/developer-guide/issues/index.html @@ -0,0 +1,7648 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GitHub Issues - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    GitHub Issues

    +

    We use GitHub issues for managing bugs, ideas, and enhancements. +Issues labeled help wanted / easy can be good (first) contributions. +You are welcome to email us if you want to work on something specific, need help with a pull request, or have something on your mind that you don't want to discuss publicly.

    +
    +

    When browsing issues, please note that our team and all issue subscribers receive an email notification from GitHub whenever a new comment is added, so these should only be used for sharing important information and not for discussions, questions, or expressing personal opinions.

    +
    +

    Writing User Stories

    +

    Start describing new ideas and enhancements with a user story similar to this one:

    +

    As a [type of person] I'd like to be able to [do something] so that I can [get some result].

    +

    This makes it easier for everyone to understand who wants what and why.

    +
    +

    Reading between the lines consumes a lot of time that can be used more effectively. +Vague requirements become even more expensive when the wrong things are implemented, or they don't provide value +in the way they were implemented.

    +
    +

    Creating Bug Reports

    +

    In order for us to investigate new bug reports, they must include a complete list of steps to reproduce the problem, the software versions used and information about the environment in which the problem occurred, such as browser type, browser version, browser plug-ins, operating system, storage type, processor type, and memory size:

    +

    https://www.photoprism.app/kb/reporting-bugs

    +

    We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app. +Before reporting a bug, first use our Troubleshooting Checklists to determine the cause of your problem. Contact us or a community member if you need help, it could be a configuration problem, or a misunderstanding in how the software works.

    +

    This gives us the opportunity to improve our documentation and provide best-in-class support instead of dealing with unclear/duplicate bug reports or triggering a flood of notifications by replying to comments. Thank you very much!

    +

    Acceptance Criteria

    +

    Issues should include a list of Acceptance Criteria that clearly state what is expected. +We recommend using MAY, SHOULD, and MUST as keywords to indicate priorities.

    +

    Clickable checkboxes for each item can be created via +GitHub Markdown:

    +
    Acceptance Criteria:
    +
    +- [ ] "Log In" button MUST be visible on /login page
    +- [ ] "Log In" button MAY be disabled if password field is empty
    +- [ ] Page SHOULD use existing Vuetify components
    +- [ ] Login MUST work in latest Firefox, Safari and Chrome
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/machine-learning/classification/index.html b/developer-guide/machine-learning/classification/index.html new file mode 100644 index 0000000000..6aebdb9086 --- /dev/null +++ b/developer-guide/machine-learning/classification/index.html @@ -0,0 +1,7918 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Image Classification - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    + +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Image Classification

    +

    Image classification is performed using a pre-trained model, NASNet Mobile 224, that we have chosen because of its size, performance and accuracy. To get a basic understanding of how this works, you can read Image Classification using Deep Neural Networks.

    +

    In addition, we manually matched the model classification with the labels you see in our UI:

    +

    cat:
    +  label: cat
    +  threshold: 0.3
    +  priority: 5
    +  categories:
    +    - animal
    +
    +tabby cat:
    +  see: cat
    +
    +

    +

    This was necessary because we didn't find a taxonomy suitable for consumers (mainly just scientific ones) and needed a lot of control to fine tune terms and their probability thresholds. The raw results were not useful to a typical user. Indexing too many words, categories and alternatives also negatively affects performance and leads to noise.

    +

    It took us several months of testing until we were happy with the results and there are still labels to improve.

    +

    Updating labels

    +

    After editing or adding labels in rules.yml, you now have to run make generate in the main project directory to generate native Go source from this file.

    +

    Pre-trained Models

    +

    See also: TensorFlow Hub

    +

    Source: https://github.com/tensorflow/models/blob/master/research/slim/README.md

    +

    Neural nets work best when they have many parameters, making them powerful +function approximators. +However, this means they must be trained on very large datasets. Because +training models from scratch can be a very computationally intensive process +requiring days or even weeks, there are various pre-trained models available. +These CNNs have been trained on the +ILSVRC-2012-CLS +image classification dataset.

    +

    Note that the VGG and ResNet V1 parameters have been converted from their original +caffe formats +(here +and +here), +whereas the Inception and ResNet V2 parameters have been trained internally at +Google. Also be aware that these accuracies were computed by evaluating using a +single image crop. PhotoPrism uses three crops, except for square images.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ModelTF-Slim FileCheckpointTop-1 AccuracyTop-5 Accuracy
    Inception V1Codeinception_v1_2016_08_28.tar.gz69.889.6
    Inception V2Codeinception_v2_2016_08_28.tar.gz73.991.8
    Inception V3Codeinception_v3_2016_08_28.tar.gz78.093.9
    Inception V4Codeinception_v4_2016_09_09.tar.gz80.295.2
    Inception-ResNet-v2Codeinception_resnet_v2_2016_08_30.tar.gz80.495.3
    ResNet V1 50Coderesnet_v1_50_2016_08_28.tar.gz75.292.2
    ResNet V1 101Coderesnet_v1_101_2016_08_28.tar.gz76.492.9
    ResNet V1 152Coderesnet_v1_152_2016_08_28.tar.gz76.893.2
    ResNet V2 50^Coderesnet_v2_50_2017_04_14.tar.gz75.692.8
    ResNet V2 101^Coderesnet_v2_101_2017_04_14.tar.gz77.093.7
    ResNet V2 152^Coderesnet_v2_152_2017_04_14.tar.gz77.894.1
    ResNet V2 200CodeTBA79.9*95.2*
    VGG 16Codevgg_16_2016_08_28.tar.gz71.589.8
    VGG 19Codevgg_19_2016_08_28.tar.gz71.189.8
    MobileNet_v1_1.0_224Codemobilenet_v1_1.0_224.tgz70.989.9
    MobileNet_v1_0.50_160Codemobilenet_v1_0.50_160.tgz59.181.9
    MobileNet_v1_0.25_128Codemobilenet_v1_0.25_128.tgz41.566.3
    MobileNet_v2_1.4_224^*Codemobilenet_v2_1.4_224.tgz74.992.5
    MobileNet_v2_1.0_224^*Codemobilenet_v2_1.0_224.tgz71.991.0
    NASNet-A_Mobile_224#Codenasnet-a_mobile_04_10_2017.tar.gz74.091.6
    NASNet-A_Large_331#Codenasnet-a_large_04_10_2017.tar.gz82.796.2
    PNASNet-5_Large_331Codepnasnet-5_large_2017_12_13.tar.gz82.996.2
    PNASNet-5_Mobile_224Codepnasnet-5_mobile_2017_12_13.tar.gz74.291.9
    +

    ^ ResNet V2 models use Inception pre-processing and input image size of 299 (use +--preprocessing_name inception --eval_image_size 299 when using +eval_image_classifier.py). Performance numbers for ResNet V2 models are +reported on the ImageNet validation set.

    +

    (#) More information and details about the NASNet architectures are available at this README

    +

    All 16 float MobileNet V1 models reported in the MobileNet Paper and all +16 quantized TensorFlow Lite compatible MobileNet V1 models can be found +here.

    +

    (^#) More details on MobileNetV2 models can be found here.

    +

    (*): Results quoted from the paper.

    +

    Here is an example of how to download the Inception V3 checkpoint:

    +
    $ CHECKPOINT_DIR=/tmp/checkpoints
    +$ mkdir ${CHECKPOINT_DIR}
    +$ wget http://download.tensorflow.org/models/inception_v3_2016_08_28.tar.gz
    +$ tar -xvf inception_v3_2016_08_28.tar.gz
    +$ mv inception_v3.ckpt ${CHECKPOINT_DIR}
    +$ rm inception_v3_2016_08_28.tar.gz
    +
    +

    Landmark detection

    +

    DELF: DEep Local Features + - https://github.com/tensorflow/models/tree/master/research/delf - Tensorflow implementation

    +

    https://gitcdn.xyz/cdn/Tony607/blog_statics/ce9c3391932e24655b78e27a54543f28f11f3af0/images/landmark/query.jpg

    +

    Source: https://gitcdn.xyz/cdn/Tony607/blog_statics/ce9c3391932e24655b78e27a54543f28f11f3af0/images/landmark/query.jpg

    +

    Types of neural networks

    +

    http://www.asimovinstitute.org/wp-content/uploads/2016/09/neuralnetworks.png +Source: http://www.asimovinstitute.org/neural-network-zoo/

    +

    External Resources

    + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/machine-learning/face-recognition/index.html b/developer-guide/machine-learning/face-recognition/index.html new file mode 100644 index 0000000000..dfedb1b4ca --- /dev/null +++ b/developer-guide/machine-learning/face-recognition/index.html @@ -0,0 +1,7676 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Face Recognition - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Face Recognition

    +

    To recognize faces, PhotoPrism first extracts crops from your images using the Pigo face detection library. It is based on pixel intensity comparisons.

    +

    These are then fed into TensorFlow to compute 512-dimensional vectors for characterization.

    +

    In the final step, the DBSCAN algorithm attempts to cluster these so-called face embeddings so that they can be assigned to people with a few clicks.

    +

    Configuration

    +
    +

    We recommend that only advanced users and developers change these parameters.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_FACE_SIZE--face-size50minimum size of faces in PIXELS (20-10000)
    PHOTOPRISM_FACE_SCORE--face-score9.000000minimum face QUALITY score (1-100)
    PHOTOPRISM_FACE_OVERLAP--face-overlap42face area overlap threshold in PERCENT (1-100)
    PHOTOPRISM_FACE_CLUSTER_SIZE--face-cluster-size80minimum size of automatically clustered faces in PIXELS (20-10000) members only
    PHOTOPRISM_FACE_CLUSTER_SCORE--face-cluster-score15minimum QUALITY score of automatically clustered faces (1-100) members only
    PHOTOPRISM_FACE_CLUSTER_CORE--face-cluster-core4NUMBER of faces forming a cluster core (1-100) members only
    PHOTOPRISM_FACE_CLUSTER_DIST--face-cluster-dist0.640000similarity DISTANCE of faces forming a cluster core (0.1-1.5) members only
    PHOTOPRISM_FACE_MATCH_DIST--face-match-dist0.460000similarity OFFSET for matching faces with existing clusters (0.1-1.5) members only
    +
      +
    • A reasonable range for the similarity distance between face embeddings is between 0.60 and 0.70, with a higher value being more aggressive and leading to larger clusters with more false positives.
    • +
    • To cluster a smaller number of faces, you can reduce the kernel to 3 or 2 similar faces.
    • +
    +

    External Resources

    + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/machine-learning/models/index.html b/developer-guide/machine-learning/models/index.html new file mode 100644 index 0000000000..191d894848 --- /dev/null +++ b/developer-guide/machine-learning/models/index.html @@ -0,0 +1,8624 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Additional Models - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Additional Computer Vision Models

    +

    Our photoprism/photoprism-vision repository on GitHub provides supplementary computer vision models which can be accessed as web services by PhotoPrism and other applications. Their RESTful API accepts an image URL and returns, for example, a suitable caption in response.

    +
    +

    Note that this has not been officially released yet. Further documentation and models will be added over time.

    +
    +

    Models

    +

    The currently integrated models, each with its own endpoint, are kosmos-2, vit-gpt2-image-captioning, and blip-image-captioning large:

    +

    Kosmos-2

    +

    Komsos-2 is the most accurate model of the three. It was developed by Microsoft, and this application uses the transformers implementation of the original model, as described in its Huggingface. This model was released in June 2023, and offers object detection and spatial reasoning. Kosmos-2 has very accurate accurate image captions (a .04-.1 increase in clip score when compared to the other two models offered), and is the default model used.

    +

    VIT-GPT2

    +

    This model was released by nlpconnect. This model combined VIT and GPT-2 to create a multi-modal image captioning model. I have found this to be the least performing of the three, but your mileage may vary.

    +

    BLIP

    +

    This model was released by Salesforce in 2022. The primary purpose for this model was to increase both image understanding and text generation using novel techniques. It has achieved a +2.8% CIDEr result, and I've found this model to be more performant than VIT-GPT2, but Kosmos-2 to be slightly better (a .4 increase in CLIP score).

    +

    Dependencies

    +

    Flask

    +

    Flask is the framework that is used for the API. It allows for API creation with Python, which is key for this application as it utilizes ML.

    +

    PyTorch

    +

    PyTorch is key for working with the ML models to generate the outputs. It also enables GPU processing, speeding up the image processing with the models. PyTorch primarily creates and handles tensors, which are crucial for the function of the models.

    +

    Transformers

    +

    Transformers is used for downloading and loading the models. In addition to this it is used in the image processing with the models.

    +

    Pillow

    +

    Pillow is used to take the supplied URl and convert it into the format needed to input into the models.

    +

    Hardware Acceleration Libraries

    +

    Numpy could be used for further hardware acceleration. It isn't included in the application by default to save space and keep from installing unnecessary dependencies. Numpy can be configured to use the GPU for computations. PyTorch already enables GPU processing, so numpy may not make a signficant difference.

    +

    Build Setup

    +

    Before installing the Python dependencies, please make sure that you have Git and Python 3.12+ (incl. pip) installed on your system, e.g. by running the following command on Ubuntu/Debian Linux:

    +
    sudo apt-get install -y git python3 python3-pip python3-venv python3-wheel
    +
    +

    You can then install the required libraries in a virtual environment by either using the Makefiles we provide (i.e. run make in the main project directory or a subdirectory) or by manually running the following commands in a service directory, for example:

    +
    git clone git@github.com:photoprism/photoprism-vision.git
    +cd photoprism-vision/describe
    +python3 -m venv ./venv
    +. ./venv/bin/activate
    +./venv/bin/pip install --disable-pip-version-check --upgrade pip
    +./venv/bin/pip install --disable-pip-version-check -r requirements.txt
    +
    +

    Usage

    +

    Run the Python file app.py in the describe subdirectory to start the describe service after you have installed the dependencies (more services, e.g. for OCR and tag generation, may follow):

    +
    ./venv/bin/python app.py
    +
    +

    The service then listens on port 5000 by default and its API endpoints for generating captions support both GET and POST requests. It can be tested with the curl command (curl.exe on Windows) as shown in the example below:

    +
    curl -v -H "Content-Type: application/json" \
    +  --data '{"url":"https://dl.photoprism.app/img/team/avatar.jpg"}' \
    +  -X POST http://localhost:5000/api/v1/vision/describe
    +
    +

    At a minimum, a valid image url must be specified for this. In addition, a model name and an arbitrary id can be passed. The API will return the same id in the response. If no id is passed, a randomly generated UUID will be returned instead.

    +

    If your client submits POST requests, the request body must be JSON-encoded, e.g.:

    +
    {
    +    "id": "3487da77-246e-4b4c-9437-67507177bcd7",
    +    "url": "https://dl.photoprism.app/img/team/avatar.jpg"
    +}
    +
    +

    Alternatively, you can perform GET requests with URL-encoded query parameters, which is easier to test without an HTTP client:

    +
    +

    http://localhost:5000/api/v1/vision/describe?url=https%3A%2F%2Fdl.photoprism.app%2Fimg%2Fteam%2Favatar.jpg&id=3487da77-246e-4b4c-9437-67507177bcd7

    +
    +

    API Endpoints

    +

    /api/v1/vision/describe

    +

    This is the default endpoint of the API. An image url should be passed in with the key "url", and optionally a "model" and/or "id" value can be passed in. The "model" key allows the user to specify which of the three models they would like to use. If no model is given, the application will default to using the kosmos-2 model.

    +

    /api/v1/vision/describe/kosmos-2/patch14-224

    +

    This is the endpoint for the Kosmos-2 model. An image url should be passed in with the key "url", and optionally a "model" and/or "id" value can be passed in.

    +

    /api/v1/vision/describe/vit-gpt2-image-captioning

    +

    This is the endpoint for the VIT GPT-2 model. An image url should be passed in with the key "url", and optionally an "id" value can be passed in.

    +

    /api/v1/vision/describe/blip-image-captioning-large

    +

    This is the endpoint for the BLIP model. An image url should be passed in with the key "url", and an "id" value can be passed in.

    +

    Example Request

    +

    POST /api/v1/vision/describe

    +
    {
    +    "id": "b0db2187-7a09-438c-8649-a9c6c0f7b8a1",
    +    "model": "kosmos-2"
    +    "url": "https://dl.photoprism.app/img/team/avatar.jpg",
    +}
    +
    +

    Example Response

    +
    {
    +    "id": "b0db2187-7a09-438c-8649-a9c6c0f7b8a1",
    +    "model": {
    +        "name": "kosmos-2",
    +        "version": "patch14-224"
    +    },
    +    "result": {
    +        "caption": "An image of a man in a suit smiling."
    +    }
    +}
    +
    +

    Code Structure

    +

    Model Loading and Initialization

    +
    MODEL_DIR = "models"
    +KOSMOS_MODEL_PATH = os.path.join(MODEL_DIR, "kosmos-2-patch14-224")
    +VIT_MODEL_PATH = os.path.join(MODEL_DIR, "vit-gpt2-image-captioning")
    +BLIP_MODEL_PATH = os.path.join(MODEL_DIR, "blip-image-captioning-large")
    +
    +

    This code block creates the paths for the models. This will be useful when downloading/loading the models. It uses os.path to assemble the correct path depending on if the system is Windows-based or UNIX-based.

    +

    Downloading Models

    +
    def download_model(model_name, save_path):
    +    if not os.path.exists(save_path):
    +        print(f"Downloading {model_name}...")
    +        if model_name == "microsoft/kosmos-2-patch14-224":
    +            AutoModelForVision2Seq.from_pretrained(model_name).save_pretrained(save_path)
    +            AutoProcessor.from_pretrained(model_name).save_pretrained(save_path)
    +        elif model_name == "nlpconnect/vit-gpt2-image-captioning":
    +            VisionEncoderDecoderModel.from_pretrained(model_name).save_pretrained(save_path)
    +            ViTImageProcessor.from_pretrained(model_name).save_pretrained(save_path)
    +            AutoTokenizer.from_pretrained(model_name).save_pretrained(save_path)
    +        elif model_name == "Salesforce/blip-image-captioning-large":
    +            BlipForConditionalGeneration.from_pretrained(model_name).save_pretrained(save_path)
    +            BlipProcessor.from_pretrained(model_name).save_pretrained(save_path)
    +        print(f"{model_name} downloaded and saved to {save_path}")
    +    else:
    +        print(f"{model_name} already exists at {save_path}")
    +
    +

    Here the code is checking if the models already exist or not. If they don't exist it is downloading them, if they do it is skipping the downloading.

    +
    os.makedirs(MODEL_DIR, exist_ok=True)
    +download_model("microsoft/kosmos-2-patch14-224", KOSMOS_MODEL_PATH)
    +download_model("nlpconnect/vit-gpt2-image-captioning", VIT_MODEL_PATH)
    +download_model("Salesforce/blip-image-captioning-large", BLIP_MODEL_PATH)
    +
    +

    Here the code is downloading the models by calling the function in the previous block.

    +

    Loading Models

    +
    print("Loading models...")
    +kosmosModel = AutoModelForVision2Seq.from_pretrained(KOSMOS_MODEL_PATH)
    +kosmosProcessor = AutoProcessor.from_pretrained(KOSMOS_MODEL_PATH)
    +
    +vitModel = VisionEncoderDecoderModel.from_pretrained(VIT_MODEL_PATH)
    +vitFeature_extractor = ViTImageProcessor.from_pretrained(VIT_MODEL_PATH)
    +vitTokenizer = AutoTokenizer.from_pretrained(VIT_MODEL_PATH)
    +
    +blipProcessor = BlipProcessor.from_pretrained(BLIP_MODEL_PATH)
    +blipModel = BlipForConditionalGeneration.from_pretrained(BLIP_MODEL_PATH)
    +
    +device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    +vitModel.to(device)
    +
    +

    Here the models are being loaded after they have been saved.

    +

    Services

    +
    def kosmosGenerateResponse(url):
    +    try:
    +        image = Image.open(requests.get(url, stream=True).raw)
    +    except Exception as e:
    +        return "fetchError", f"Unable to fetch image: {str(e)}"
    +
    +    prompt = "<grounding>An image of"
    +
    +    try:
    +        inputs = kosmosProcessor(text=prompt, images=image, return_tensors="pt")
    +        generated_ids = kosmosModel.generate(
    +            pixel_values=inputs["pixel_values"],
    +            input_ids=inputs["input_ids"],
    +            attention_mask=inputs["attention_mask"],
    +            image_embeds=None,
    +            image_embeds_position_mask=inputs["image_embeds_position_mask"],
    +            use_cache=True,
    +            max_new_tokens=128,
    +        )
    +
    +        generated_text = kosmosProcessor.batch_decode(generated_ids, skip_special_tokens=True)[0]
    +        processed_text, entities = kosmosProcessor.post_process_generation(generated_text)
    +    except Exception as e:
    +        return "processingError", f"Error during processing: {str(e)}"
    +
    +    return "ok", processed_text
    +
    +def vitGenerateResponse(url):
    +    vitModel.to(device)    
    +
    +    max_length = 16
    +    num_beams = 4
    +    gen_kwargs = {"max_length": max_length, "num_beams": num_beams}
    +
    +    def predict_step(url):
    +        image = Image.open(requests.get(url, stream=True).raw)
    +        images = []
    +
    +        if image.mode != "RGB":
    +            image = image.convert(mode="RGB")
    +
    +        images.append(image)
    +
    +        pixel_values = vitFeature_extractor(images=images, return_tensors="pt").pixel_values
    +        pixel_values = pixel_values.to(device)
    +
    +        output_ids = vitModel.generate(pixel_values, **gen_kwargs)
    +
    +        preds = vitTokenizer.batch_decode(output_ids, skip_special_tokens=True)
    +        preds = [pred.strip() for pred in preds]
    +        return preds
    +
    +    processed_text = predict_step(url)  # returns prediction
    +
    +    return "ok", processed_text
    +
    +def blipGenerateResponse(url):
    +    img_url = url
    +    raw_image = Image.open(requests.get(img_url, stream=True).raw).convert('RGB')
    +
    +    inputs = blipProcessor(raw_image, return_tensors="pt")
    +
    +    out = blipModel.generate(**inputs)
    +    processed_text = blipProcessor.decode(out[0], skip_special_tokens=True)
    +
    +    return "ok", processed_text
    +
    +

    These are the services to generate the captions. There is a function for each model.

    +

    API Endpoints

    +

    Default Endpoint

    +
    @app.route('/api/v1/vision/describe', methods=['POST', 'GET'])
    +def generateResponse():
    +    if request.method == 'POST':
    +        if not request.is_json:
    +            return jsonify({"error": "Request must be JSON"}), 400
    +        data = request.get_json()
    +    elif request.method == 'GET':
    +        data = request.args
    +
    +    url = data.get('url')
    +    model = data.get('model')
    +    id = data.get('id')
    +
    +    if not url:
    +        return jsonify({"error": "URL is required"}), 400
    +
    +    if model == "kosmos-2" or not model:
    +        status, result = kosmosGenerateResponse(url)
    +        if status == "fetchError":
    +            return jsonify({"error": result}), 500
    +        elif status == "processingError":
    +            return jsonify({"error": result}), 500
    +        elif status == "ok":
    +            if id:
    +                return jsonify({"id": id, "result": {"caption": result}, "model": {"name": "kosmos-2", "version": "patch14-224"}}), 200
    +            return jsonify({"id": uuid.uuid4(), "result": {"caption": result}, "model": {"name": "kosmos-2", "version": "patch14-224"}}), 200
    +    elif model == "vit-gpt2-image-captioning":
    +        status, result = vitGenerateResponse(url)
    +        if status == "ok":
    +            if id:
    +                return jsonify({"id": id, "result": {"caption": result}, "model": {"name": model, "version": "latest"}}), 200
    +            return jsonify({"id": uuid.uuid4(), "result": {"caption": result}, "model": {"name": model, "version": "latest"}}), 200
    +        return jsonify({"error": "Error during processing"})
    +    elif model == "blip-image-captioning-large":
    +        status, result = blipGenerateResponse(url)
    +        if status =='ok':
    +            if id:
    +                return jsonify({"id": id, "result": {"caption": result}, "model": {"name": model, "version": "latest"}}), 200
    +            return jsonify({"id": uuid.uuid4(), "result": {"caption": result}, "model": {"name": model, "version": "latest"}}), 200
    +        return jsonify({"error": "Error during processing"})
    +
    +

    This is the default endpoint. It checks to see if a model is specified, and if it is it calls the service associated with that model and returns the respose with the data. If a model isn't specified it uses kosmos-2.

    +

    Specific Endpoints

    +
    @app.route('/api/v1/vision/describe/kosmos-2/patch14-224', methods=['POST', 'GET'])
    +def kosmosController():
    +    if request.method == 'POST':
    +        if not request.is_json:
    +            return jsonify({"error": "Request must be JSON"}), 400
    +        data = request.get_json()
    +    elif request.method == 'GET':
    +        data = request.args
    +
    +    url = data.get('url')
    +    id = data.get('id')
    +
    +    if not url:
    +        return jsonify({"error": "URL is required"}), 400
    +
    +    status, result = kosmosGenerateResponse(url)
    +
    +    if status == "fetchError":
    +        return jsonify({"error": result}), 500
    +    elif status == "processingError":
    +        return jsonify({"error": result}), 500
    +    elif status == "ok":
    +        if id:
    +            return jsonify({"id": id, "result": {"caption": result}, "model": {"name": "kosmos-2", "version": "patch14-224"}}), 200
    +        return jsonify({"id": uuid.uuid4(), "result": {"caption": result}, "model": {"name": "kosmos-2", "version": "patch14-224"}}), 200
    +
    +
    +
    +
    +@app.route('/api/v1/vision/describe/vit-gpt2-image-captioning', methods=['POST', 'GET'])
    +def vitController():
    +    if request.method == 'POST':
    +        if not request.is_json:
    +            return jsonify({"error": "Request must be JSON"}), 400
    +        data = request.get_json()
    +    elif request.method == 'GET':
    +        data = request.args
    +
    +    url = data.get('url')
    +    id = data.get('id')
    +
    +    if not url:
    +        return jsonify({"error": "URL is required"}), 400
    +
    +    status, result = vitGenerateResponse(url)
    +
    +    if status == "ok":
    +        if id:
    +            return jsonify({"id": id, "result": {"caption": result}, "model": {"name": "vit-gpt2-image-captioning", "version": "latest"}}), 200
    +        return jsonify({"id": uuid.uuid4(), "result": {"caption": result}, "model": {"name": "vit-gpt2-image-captioning", "version": "latest"}}), 200
    +
    +    return jsonify({"error": "Error during processing"})
    +
    +
    +
    +@app.route('/api/v1/vision/describe/blip-image-captioning-large', methods=['POST', 'GET'])
    +def blipController():
    +    if request.method == 'POST':
    +        if not request.is_json:
    +            return jsonify({"error": "Request must be JSON"}), 400
    +        data = request.get_json()
    +    elif request.method == 'GET':
    +        data = request.args
    +
    +    url = data.get('url')
    +    id = data.get('id')
    +
    +    if not url:
    +        return jsonify({"error": "URL is required"}), 400
    +
    +    status, result = blipGenerateResponse(url)
    +
    +    if status == "ok":
    +        if id:
    +            return jsonify({"id": id, "result": {"caption": result}, "model": {"name": "vit-gpt2-image-captioning", "version": "latest"}}), 200
    +        return jsonify({"id": uuid.uuid4(), "result": {"caption": result}, "model": {"name": "vit-gpt2-image-captioning", "version": "latest"}}), 200
    +
    +    return jsonify({"error", "Error during processing"})
    +
    +

    These are the endpoints for each model. They do some error handling, run the service, and return the response.

    +

    Contributors

    +

    We would like to thank everyone involved, especially Aatif Dawawala who got things rolling and contributed much of the initial code:

    + +

    Learn more ›

    +

    Submitting Pull Requests

    +

    Follow our step-by-step guide to learn how to submit new features, bug fixes, and documentation enhancements.

    +

    Learn more ›

    +

    License and Disclaimer

    +

    The files in the photoprism/photoprism-vision repository are licensed under the Apache License, Version 2.0 (the “License”).

    +

    Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

    +

    Learn more ›

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/media/colors/index.html b/developer-guide/media/colors/index.html new file mode 100644 index 0000000000..0678729d08 --- /dev/null +++ b/developer-guide/media/colors/index.html @@ -0,0 +1,7650 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Color Profiles - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Color Profiles

    +

    Standard RGB

    +

    sRGB is the default color space used when generating thumbnails. PhotoPrism and web browsers assume this color space for all pictures that do not have an embedded ICC color profile.

    +

    ICC Profiles

    +

    An ICC color profile for wide-gamut displays can optionally be embedded in image and video files. For color profiles other than sRGB and Display P3, the thumbnails must be generated with libvips by setting the PHOTOPRISM_THUMB_LIBRARY config option to vips or auto so that the ICC profiles are preserved:

    + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_THUMB_LIBRARY--thumb-libraryautoimage processing LIBRARY to be used for generating thumbnails (auto, imaging, vips)
    PHOTOPRISM_THUMB_COLOR--thumb-colorautostandard color PROFILE for thumbnails (auto, preserve, srgb, none)
    +

    Image colors may otherwise not be displayed correctly, which is particularly noticeable with ProPhoto RGB and Adobe RGB, as these cover a wide range of colors:

    +

    ICC Profiles

    +

    Color Detection

    +

    Color detection is performed while indexing using a 3x3 thumbnail that covers the top, bottom, and center of an image e.g. https://demo.photoprism.app/library/browse?color=green.

    +

    Learn more ›

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/media/heif/index.html b/developer-guide/media/heif/index.html new file mode 100644 index 0000000000..40f974ccde --- /dev/null +++ b/developer-guide/media/heif/index.html @@ -0,0 +1,7697 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + HEIC / HEIF - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    HEIC / HEIF

    + +

    HEIC / HEIF is a new image file format employing HEVC (h.265) image coding for the best compression ratios currently possible. Newer iPhones use it for internal photo storage. It is supported on iOS 11 and macOS High Sierra and later.

    +

    Testing Conversion and Orientation

    +

    Let's say you have Docker installed and want to test HEIF image conversion and orientation with Debian 12 "Bookworm", you can simply run this command to open a terminal:

    +
    docker run --rm -v ${PWD}:/test -w /test -ti debian:bookworm bash
    +
    +

    This will mount the current working directory as /test. Of course, you can also specify a full path instead of ${PWD}.

    +

    The available Ubuntu, Debian and PhotoPrism images can be found on Docker Hub:

    + +

    Now install exiftool and libheif-examples (includes the heif-convert command) via apt:

    +
    apt update
    +apt install -y libheif-examples exiftool
    +
    +

    Finally, run the heif-convert command (-q 92 is optional and determines the JPEG quality/compression):

    +
    root@1ad9fb887a4f:/test# heif-convert -q 92 IMG_8437.HEIC IMG_8437.HEIC.jpg
    +File contains 1 image
    +Written to IMG_8437.HEIC.jpg
    +
    +

    To view the image metadata, run exiftool -n <filename> and optionally use grep to filter the output:

    +
    root@1ad9fb887a4f:/test# exiftool -n IMG_8437.HEIC.jpg | grep ation
    +File Modification Date/Time     : 2022:09:18 08:16:28+00:00
    +Orientation                     : 6
    +Exposure Compensation           : 0
    +root@1ad9fb887a4f:/test# exiftool -n IMG_8437.HEIC | grep ation
    +File Modification Date/Time     : 2022:09:17 16:57:40+00:00
    +Orientation                     : 6
    +Exposure Compensation           : 0
    +HEVC Configuration Version      : 1
    +Min Spatial Segmentation IDC    : 0
    +Rotation                        : 270
    +
    +

    Rotation and Orientation are the important values you should pay attention to and compare. The rotation is in degrees.

    +

    Exiftool Parameters

    +
      +
    • -n displays the raw values without changes
    • +
    • -j will format the output as JSON
    • +
    • -g groups the output by metadata source
    • +
    +

    Exif Orientation

    +

    The Exif orientation values are numbered from 1 to 8:

    +
      +
    1. = 0 degrees: the correct orientation, no adjustment is required.
    2. +
    3. = 0 degrees, mirrored: image has been flipped back-to-front.
    4. +
    5. = 180 degrees: image is upside down.
    6. +
    7. = 180 degrees, mirrored: image has been flipped back-to-front and is upside down.
    8. +
    9. = 90 degrees: image has been flipped back-to-front and is on its side.
    10. +
    11. = 90 degrees, mirrored: image is on its side.
    12. +
    13. = 270 degrees: image has been flipped back-to-front and is on its far side.
    14. +
    15. = 270 degrees, mirrored: image is on its far side.
    16. +
    +

    Learn more ›

    +

    Software Libraries and References

    + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/media/img/advanced-settings.jpg b/developer-guide/media/img/advanced-settings.jpg new file mode 100644 index 0000000000..19a9433465 Binary files /dev/null and b/developer-guide/media/img/advanced-settings.jpg differ diff --git a/developer-guide/media/img/branches.png b/developer-guide/media/img/branches.png new file mode 100644 index 0000000000..522976b37e Binary files /dev/null and b/developer-guide/media/img/branches.png differ diff --git a/developer-guide/media/img/editPhoto.png b/developer-guide/media/img/editPhoto.png new file mode 100644 index 0000000000..89ac8b7a5d Binary files /dev/null and b/developer-guide/media/img/editPhoto.png differ diff --git a/developer-guide/media/img/icc-profiles.svg b/developer-guide/media/img/icc-profiles.svg new file mode 100644 index 0000000000..6be5961f3f --- /dev/null +++ b/developer-guide/media/img/icc-profiles.svg @@ -0,0 +1,1423 @@ + + + + CIE 1931 Chromaticity Plot • RGB Gamut Comparison + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CIE 1931 Chromaticity Plot • RGB Gamut Comparison + + + + diff --git a/developer-guide/media/img/out_resize_catrom.png b/developer-guide/media/img/out_resize_catrom.png new file mode 100644 index 0000000000..1435697e4d Binary files /dev/null and b/developer-guide/media/img/out_resize_catrom.png differ diff --git a/developer-guide/media/img/out_resize_lanczos.png b/developer-guide/media/img/out_resize_lanczos.png new file mode 100644 index 0000000000..f87f4c6b08 Binary files /dev/null and b/developer-guide/media/img/out_resize_lanczos.png differ diff --git a/developer-guide/media/img/out_resize_linear.png b/developer-guide/media/img/out_resize_linear.png new file mode 100644 index 0000000000..b039044326 Binary files /dev/null and b/developer-guide/media/img/out_resize_linear.png differ diff --git a/developer-guide/media/img/out_resize_nearest.png b/developer-guide/media/img/out_resize_nearest.png new file mode 100644 index 0000000000..573ebc7851 Binary files /dev/null and b/developer-guide/media/img/out_resize_nearest.png differ diff --git a/developer-guide/media/import/index.html b/developer-guide/media/import/index.html new file mode 100644 index 0000000000..6067998e46 --- /dev/null +++ b/developer-guide/media/import/index.html @@ -0,0 +1,7631 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + File Import - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Importing, Indexing, and Uploading

    +

    PhotoPrism is meant to be a complete photo management solution that not only lets you index your existing pictures, but also add or upload new files to your photo collection or delete existing files to free up space.

    +

    You can therefore choose to index your originals directly, leaving all file and folder names unchanged, or use the optional import feature that automatically removes duplicates, gives files a unique name and sorts them by year and month.

    +

    To learn more, follow our First Steps 👣 tutorial which will guide you through the interface and settings.

    +

    Read-Only Mode

    +

    Early in development there was some debate about whether PhotoPrism should be responsible for naming files, see Support Photos In Place on Hard Drive #41. Once you want the software to automatically create new files or merge photo libraries from different devices, this is often the only viable option.

    +

    Besides the index-only functionality for users who want to name their files manually, we have also introduced a read-only mode for those who want to use the software as a gallery with limited features, see Read-Only Mode #56.

    +

    Post Processing

    +

    After importing new pictures, it would be great if users could quickly sort and review them. This way, the library stays clean and organized, even without applying filters to hide bad shots.

    +

    This is common workflow among users of Adobe Bridge, a tool used by many professionals. They go through complete asset collections and sort photos using keyboard shortcuts, where keys toggle pre-defined tags or flags like "favorite".

    +

    Upload Options

    +

    Users can either use WebDAV to upload files and add them to their originals or import folder, or use the Web Upload to add them to a temporary directory on the server, from which they will be imported to the originals folder.

    +
    +

    We might later use WebAssembly or the File API to improve the performance and capabilities of the Web Upload.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/media/index.html b/developer-guide/media/index.html new file mode 100644 index 0000000000..6ba9431732 --- /dev/null +++ b/developer-guide/media/index.html @@ -0,0 +1,7906 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + File Formats - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Supported Media and Sidecar File Formats

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FormatDescriptionTypeExtensions
    AVIFAV1 Image File FormatImage.avif
    AVIFSAV1 Image SequenceImage.avifs, .avis
    BMPBitmapImage.bmp
    GIFGraphics Interchange FormatImage.gif
    HEICHigh Efficiency Image ContainerImage.avci, .avcs, .heic, .heif, .hif
    HEICSHEIC Image SequenceImage.heics, .heifs
    JPGJoint Photographic Experts Group (JPEG)Image.jfi, .jfif, .jif, .jpe, .jpeg, .jpg
    JXLJPEG XLImage.jxl
    MPOStereoscopic JPEG (3D)Image.mpo
    PNGPortable Network GraphicsImage.apng, .pn, .png, .pnga
    PSDAdobe PhotoshopImage.psd
    THMThumbnail ImageImage.thm
    TIFFTag Image File FormatImage.tif, .tiff
    WEBPGoogle WebPImage.webp
    DNGAdobe Digital NegativeRaw.dng
    RAWUnprocessed Sensor DataRaw.3fr, .ari, .arw, .bay, .cap, .cr2, .cr3, .crw, .data, .dcr, .dcs, .drf, .eip, .erf, .fff, .gpr, .iiq, .k25, .kdc, .mdc, .mef, .mos, .mrw, .nef, .nrw, .obm, .orf, .pef, .ptx, .pxn, .r3d, .raf, .raw, .rw2, .rwl, .rwz, .sr2, .srf, .srw, .x3f
    AAEApple Image Edits XMLSidecar.aae
    JSONSerialized JSON Data (Exiftool, Google Photos)Sidecar.json
    MDMarkdown Formatted TextSidecar.markdown, .md
    NFOInfo TextSidecar.nfo
    TXTPlain TextSidecar.txt
    XMLExtensible Markup LanguageSidecar.xml
    XMPAdobe Extensible Metadata PlatformSidecar.xmp
    YMLSerialized YAML Data (Config, Metadata)Sidecar.yaml, .yml
    AIAdobe IllustratorVector.ai
    EPSEncapsulated PostScriptVector.epi, .eps, .eps2, .eps3, .epsf, .epsi, .ept
    PSAdobe PostScriptVector.ps, .ps2, .ps3
    SVGScalable Vector GraphicsVector.svg
    3G2Mobile Multimedia Container (CDMA2000)Video.3g2
    3GPMobile Multimedia Container (3G)Video.3gp
    ASFAdvanced Systems FormatVideo.asf
    AV1AOMedia Video 1Video.av1
    AVCAdvanced Video Coding (H.264, MPEG-4 Part 10)Video.avc
    AVIMicrosoft Audio Video InterleaveVideo.avi
    DVDV VideoVideo.dv
    EVCEssential Video Coding (MPEG-5 Part 1)Video.evc
    FLVAdobe FlashVideo.f4v, .flv
    HEVCHigh Efficiency Video Coding (H.265)Video.hevc
    M2TSBlu-ray MPEG-2 Transport StreamVideo.m2ts
    M4VApple iTunes Multimedia ContainerVideo.m4v
    MJPGMotion JPEGVideo.mjpeg, .mjpg
    MKVMatroska Multimedia ContainerVideo.mkv
    MOVApple QuickTimeVideo.mov, .qt
    MP2MPEG 2 (H.262)Video.mp2, .mpv
    MP4Multimedia Container (MPEG-4 Part 14)Video.mp, .mp4
    MPGMoving Picture Experts Group (MPEG)Video.mpeg, .mpg
    MTSAdvanced Video Coding High Definition (AVCHD)Video.mts
    MXFMaterial Exchange FormatVideo.mxf
    OGVOgg Media (OGG)Video.ogg, .ogv, .ogx
    VVCVersatile Video Coding (H.266)Video.vvc
    WEBMGoogle WebMVideo.webm
    WMVWindows MediaVideo.wmv
    +

    CLI Command

    +

    Run photoprism show file-formats in a terminal to list supported media and sidecar file formats.

    +

    Command Flags:

    +
      +
    • --md, -m format as machine-readable Markdown
    • +
    • --csv, -c export as semicolon separated values
    • +
    • --tsv, -t export as tab separated values
    • +
    • --short, -s hide format descriptions
    • +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/media/live/index.html b/developer-guide/media/live/index.html new file mode 100644 index 0000000000..a8f3f1d0fe --- /dev/null +++ b/developer-guide/media/live/index.html @@ -0,0 +1,7787 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Live Photos - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Hybrid Photo/Video Formats

    +

    Apple iPhone and iPad

    +

    iOS Live Photos consist of a JPEG/HEIC image and a QuickTime AVC/HEVC video, which are both required for viewing.

    +

    We recommend using an app like PhotoSync to upload Live Photos to PhotoPrism, since the iOS web upload usually only submits the HEIC image file without the video.

    +

    Android Devices

    +

    Some Samsung and Google Android devices support taking "Motion Photos" with the included Camera app. Motion Photos are JPEG/HEIC image with a short MP4 video embedded after the image data.

    +

    The image part of these files can be opened in any image viewer that supports JPEG/HEIC, but the video part cannot. However, since the MP4 video is simply appended at the end of the image file, it can be easily read by our software and streamed through the API as needed.

    +

    Introductory Tutorials

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TitleDateURL
    How to detect Android motion photos in FlutterMay 2023https://ente.io/blog/tech/android-motion-photos-flutter/
    Stripping Embedded MP4s out of Android 12 Motion PhotosOct 2021https://mjanja.ch/2021/10/stripping-embedded-mp4s-out-of-android-12-motion-photos/
    Google Pixel "Motion Photo" HowtoMar 2021https://linuxreviews.org/Google_Pixel_%22Motion_Photo%22
    go-mp4: Golang Library and CLI Tool for MP4Jul 2020https://dev.to/sunfishshogi/go-mp4-golang-library-and-cli-tool-for-mp4-52o1
    Working with Motion PhotosJan 2019https://medium.com/android-news/working-with-motion-photos-da0aa49b50c
    Google: Behind the Motion Photos Technology in Pixel 2Mar 2018https://blog.research.google/2018/03/behind-motion-photos-technology-in.html
    +

    Software Libraries and References

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TitleURL
    Web Video Codec Guidehttps://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs
    Media Container Formatshttps://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
    MP4 Signature Formathttps://www.file-recovery.com/mp4-signature-format.htm
    List of file signatures (Wikipedia)https://en.wikipedia.org/wiki/List_of_file_signatures
    Go library for reading and writing MP4 filesabema/go-mp4
    Go library for buffered I/O with io.Seeker interfacesunfish-shogi/bufseekio
    How to use the io.Reader interfacehttps://yourbasic.org/golang/io-reader-interface-explained/
    AV1 Codec ISO Media File Formathttps://aomediacodec.github.io/av1-isobmff
    + +
      +
    • #439 (Samsung: Initial support for Motion Photos)
    • +
    • #1739 (Google: Initial support for Motion Photos)
    • +
    • #2788 (Metadata: Flag Samsung/Google Motion Photos as Live Photos)
    • +
    • cliveontoast/GoMoPho#23 (Google Motion Photos Video Extractor: Add Android 12 Support)
    • +
    + +
      +
    • #3709 (Google: Initial support for Motion Photos)
    • +
    • #3722 (Google: Add support for Motion Photos)
    • +
    • #3660 (Samsung: Improved support for Motion Photos)
    • +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/media/raw/index.html b/developer-guide/media/raw/index.html new file mode 100644 index 0000000000..ef40f86e89 --- /dev/null +++ b/developer-guide/media/raw/index.html @@ -0,0 +1,8195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RAW Images - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    RAW Images

    + +

    Professional and semi-professional photographers often keep their originals in a lossless RAW format, close to how they were taken with the physical sensor, rather than in a compressed image format like JPEG, especially if they shoot with a digital SLR camera. Newer mobile phones may also be able to capture images in RAW mode. Our goal is to provide top-notch support for all RAW images, regardless of camera make and model. A full list of file types and extensions can be found in our Knowledge Base.

    +

    +

    RAW Conversion

    +

    Since web browsers generally cannot display RAW image files directly, they must be converted. This is done during import or initial indexing. It can also be triggered manually in a terminal with the photoprism convert command.

    +

    Adobe XMP

    +

    PhotoPrism currently supports Darktable and RawTherapee as RAW image converters (as well as Sips on macOS). Darktable fully supports XMP sidecar files, RawTherapee might only partially. However, XMP is only a "container" format, so the fields (namespaces) used there to indicate how an image should be converted (as well as other metadata) differ between Lightroom/Photoshop, Darktable, and RawTherapee.

    +

    In other words, just because an application generally supports XMP that doesn't mean it can use metadata created with another application or by another vendor like Adobe. If you think that's confusing, well, that's because it is. You have an open format, but you still suffer from vendor lock-in - probably not entirely unintentional on Adobe's part.

    +

    From our experience, some basic edits done with Adobe tools - such as cropping - might be preserved when you convert the same RAW image with other software like Darktable. Advanced edits, such as lens or color corrections, will likely not be applied.

    +

    Learn more ›

    +

    Darktable

    +

    If installed, Darktable CLI can be used for RAW image conversion. Note that PhotoPrism will only run one instance of darktable-cli at the same time if the PHOTOPRISM_RAW_PRESETS config option is enabled, so existing Darktable presets can be applied (requires an exclusive lock).

    +

    Using Darktable as library

    +

    We had the idea to use cgo and link directly against libdarktable.so to convert RAW images to JPEG, see darktable-dev mailing list. However this requires calling/wrapping a large number of C functions and also has other disadvantages. The idea is postponed until a clear benefit becomes visible. Running darktable-cli or any other binary that does the job (see above) seems to be the way to go.

    +

    RawTherapee

    +

    If installed, RawTherapee CLI can also be used for RAW image conversion. If it used by default if Darktable isn't installed or disabled.

    +

    JPEG Size Limit

    +

    RawTherapee cannot limit the resolution of JPEG files when converting files from other formats such as RAW, DNG, HEIC or AVIF. In general, when converting images, the resolution of the generated JPEG files can be limited with the environment variable PHOTOPRISM_JPEG_SIZE or the CLI parameter --jpeg-size.

    +

    Scriptable Image Processing System (Sips)

    +

    On a Mac, PhotoPrism can convert multiple files at once using Sips (pre-installed on OS X). It is not available for other operating systems.

    +

    Comparison of RAW to JPEG converters

    +
      +
    • darktable - popular open-source photography app and raw developer; available for Mac, Linux, and Windows; supports XMP (compatible with photoshop/lightroom?)
    • +
    • Mac OS X ships with sips: sips -s format jpeg IMAGE.RAW --out IMAGE.JPG
    • +
    • Photivo - open-source photo processor; available for Mac, Linux, and Windows; no XMP support?
    • +
    • RawTherapee - open-source RAW image processing app; available for Mac, Linux, and Windows; no XMP support?
    • +
    • digiKam - open-source digital photo management application based on Qt (KDE); available for Mac, Linux, and Windows; supports XMP (compatible with photoshop/lightroom?)
    • +
    • UFRaw - Unidentified Flying Raw is a utility to read and manipulate raw images from digital cameras
    • +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ToolCommand line optionsCompatible OSJPG Diff*XMP supportPossible settingsEXIF Diff*Compatible with Raspberry (ARM64)
    Darktable1) darktable-cli IMG_0310.CR2 IMG_0310_darktable1.jpg 2) darktable-cli IMG_0310_EDITED.CR2 IMG_0310_EDITED.xmp IMG_0310_EDITED_darktable2.jpgmacOS, Linux, Windows****yes (but seems to be not compatible with adobe xmps)?****yes
    Sipssips -s format jpeg IMG_0310.CR2 --out IMG_0310_sips.jpgmacOS*****no?****not available for ubuntu
    Rawtherapeerawtherapee-cli -o IMG_0310_rawtherapee.jpg -c IMG_0310.CR2macOS, Linux, Windows***no?****yes
    UFrawufraw-batch --out-type=jpg --output=IMG_0310_ufraw.jpg IMG_0310.CR2macOS, Linux, Windows*no?**yes
    ImageMagickmagick IMG_0310.CR2 IMG_0310_magick.jpgmacOS, Linux, Windows*no?**yes
    Digikam?macOS, Linux, Windows-----
    Photiva?macOS, Linux, Windows-----
    * Compared to JPG/EXIF converted from photoshop
    +

    Image Diff

    +

    The following table shows the difference between the JPEG files converted by Darktable, Sips, Rawtherapee, UFraw and ImageMagick compared to Adobe Photoshop. Red are pixel that differ from the photoshop version, white are equal pixels. In total 5 Images have been compared (full results). The diff was created using ewanmellor/git-diff-image.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ToolDiff (left is Photoshop)
    Darktable
    Sips
    Rawtherapee
    UFraw
    ImageMagick
    +

    EXIF Diff

    +

    The following table shows the difference between the JPG files converted by the tools compared to JPG files converted by photoshop. 5 Images have been compared.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    InfoPhotoshopSipsDarktableRaw-therapeeImage MagickUFraw
    Kindidenticalidenticalidenticalidenticalidenticalidentical
    Size2.881.969 bytes (EXIF)/ 2.7M (ls -alh)2.673.777 bytes / 2.5M5.446.710 bytes / 5.2M always bigger3.467.998 bytes / 3.3M3.467.998 bytes / 3.3M1.558.587 bytes / 1.5M always smaller
    Wherereferencedifferentdifferentdifferentdifferentdifferent
    Createdreferencedifferentdifferentdifferentdifferentdifferent
    Modifiedreferencedifferentdifferentdifferentdifferentdifferent
    Dimensionidentical - 5472 × 36485472 × 3648different - 5494 × 3666different - 5488 × 3662different - 5496 × 3670different - 5496 × 3670
    Device Makerefrenceidenticalidenticalidenticalnot setnot set
    Colour spacereferenceidenticalidenticalidenticalidenticalidentical
    Colour profileAdobe RGB (1998)different - Display P3different - sRGBdifferent - RTv2_sRGBnot setnot set
    Focal lengthreferenceidenticalidenticalidenticalnot setnot set
    Alpha Channelreferenceidenticalidenticalidenticalnot setnot set
    Red eyereferenceidenticalidenticalidenticalnot setnot set
    Metering Modereferenceidenticalidenticalidenticalnot setnot set
    F numberreferenceidenticalidenticalidenticalnot setnot set
    Exposure programreferenceidenticalidenticalidenticalnot setnot set
    Exposure timereferenceidenticalidenticalidenticalnot setnot set
    Latitudereferencesometimes very small differencesidenticalidenticalnot setnot set
    Longitudereferencesometimes very small differencesidenticalidenticalnot setnot set
    +

    Adobe Photoshop / Lightroom

    +

    Ideally we can convert images directly with Photoshop or Lightroom, if installed. It seems like Photoshop comes with some sort of command-line automation tool based on NodeJS: +adobe-photoshop/generator-core

    +

    See https://photo.stackexchange.com/questions/39532/command-line-approach-to-develop-raw-images-with-adobe-xmp-sidecars

    +

    This needs further investigation. Contributions welcome!

    +

    Todo / Reading List

    + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/media/samples/index.html b/developer-guide/media/samples/index.html new file mode 100644 index 0000000000..e0a7315b6a --- /dev/null +++ b/developer-guide/media/samples/index.html @@ -0,0 +1,7514 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test Samples - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    File Samples

    +

    We welcome image and video file samples from our community. You can email them to samples@photoprism.app, either as attachments or with a download link.

    +

    Please include the file format and GitHub issue number (if applicable) or other helpful reference (e.g. camera model) in the subject line, and let us know if we have permission to upload your files to dl.photoprism.app/samples for future use.

    +

    + Contact Us + Submit Files + Browse Archive +

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/media/storage/index.html b/developer-guide/media/storage/index.html new file mode 100644 index 0000000000..962754f3dc --- /dev/null +++ b/developer-guide/media/storage/index.html @@ -0,0 +1,7643 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Storage - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    File Storage

    +

    At the moment, all media and sidecar files as well as thumbnail images are stored directly in the server's file system, see Config Options.

    +

    Filesystem Abstraction

    +

    We may eventually want to add a filesystem abstraction e.g. to speed up testing or support cloud storage. Afero seems to be the gold standard for this in the Go world:

    + + + +

    Software Libraries and References

    +
      +
    • rclone/rclone - rsync for cloud storage (Google Drive, AWS, One Drive, ...)
    • +
    • @minio - Object Storage for AI
    • +
    • upper.io/db.v3 - a productive data access layer for Go
    • +
    • LevelDB - fast key-value storage library written at Google (written in Go)
    • +
    • Bleve - full-text search and indexing for Go
    • +
    • TiDB - a distributed HTAP database compatible with the MySQL protocol (written in Go)
    • +
    • KSQL - the Streaming SQL Engine for Apache Kafka
    • +
    • CockroachDB - ultra-resilient SQL for global business (written in Go)
    • +
    • LedisDB - a high performance NoSQL like Redis powered by Go
    • +
    • go-pg/pg - Golang ORM with focus on PostgreSQL features and performance
    • +
    • go-redis/redis - Type-safe Redis client for Golang
    • +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/media/thumbnails/index.html b/developer-guide/media/thumbnails/index.html new file mode 100644 index 0000000000..464991143a --- /dev/null +++ b/developer-guide/media/thumbnails/index.html @@ -0,0 +1,8020 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Thumbnails - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Thumbnail Generation and Storage

    +

    Introduction

    +

    PhotoPrism can use libvips and the native disintegration/imaging package to generate thumbnails for display in search results and in the full-screen viewer.

    +

    The smallest configurable size limit is 720px, which corresponds to the fit_720 thumbnail size. Most sizes up to 720x720 will be generated by default and should therefore always be available.

    +

    Besides their obvious use in the frontend, the indexer also uses thumbnails for color detection, face recognition and image classification, see Advanced Settings in the User Guide.

    +

    Standard Sizes

    +

    The smallest configurable static and dynamic size limit is 720px, so most sizes up to fit_720 are always generated by default. +Higher size limits generate thumbnails with more detail at higher resolutions - either statically (pre-generated while indexing) or on demand if the configuration permits.

    +

    Optional thumbnail sizes cannot be pre-generated and are only rendered on request, for example when sharing an image on Instagram.

    +

    The following overview shows the name, dimensions, and aspect ratio for each thumbnail size as well as a description of how it is used:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameWidthHeightAspect RatioAvailableUsage
    colors331:1AlwaysColor Detection
    tile_5050501:1AlwaysList View
    tile_1001001001:1AlwaysPlaces View
    left_2242242241:1On-DemandTensorFlow
    right_2242242241:1On-DemandTensorFlow
    tile_2242242241:1AlwaysTensorFlow, Mosaic View
    tile_5005005001:1AlwaysCards View
    fit_720720720PreservedAlwaysSD TV, Mobile
    tile_1080108010801:1OptionalInstagram
    fit_128012801024PreservedOn-DemandHD TV, SXGA
    fit_16001600900PreservedOptionalSocial Media
    fit_192019201200PreservedOn-DemandFull HD
    fit_204820482048PreservedOptionalDCI 2K, Tablets
    fit_256025601600PreservedOn-DemandQuad HD, Notebooks
    fit_384038402400PreservedOptional4K Ultra HD
    fit_409640964096PreservedOn-DemandDCI 4K, Retina 4K
    fit_768076804320PreservedOn-Demand8K Ultra HD 2
    +

    internal/thumb/sizes.go

    +

    Color Profiles

    +

    sRGB is the standard color space used when generating thumbnails. An ICC color profile for wide-gamut displays can optionally be embedded.

    +

    Learn more ›

    +

    File Storage

    +

    Generated thumbnail files are stored in the storage/cache/thumbnails folder, where the path and file name depend on the thumbnail size and original file hash, e.g.:

    +
    storage/cache/thumbnails/1/a/3/1a30c1f...9_100x100_center.jpg
    +
    +

    Downscaling Filters

    +

    Linear

    +

    Bilinear interpolation takes a weighted average of the four +neighborhood pixels to calculate its final interpolated +value. The result is a much smoother image than the original +image. When all known pixel distances are equal, then the +interpolated value is simply their sum divided by four. +This technique performs interpolation in both directions, +horizontal and vertical. This technique gives better result +than nearest neighbor interpolation and take less +computation time compared to bicubic interpolation.

    +

    Cubic

    +

    Catmull-Rom is a local interpolating spline developed for +computer graphics purposes. Its initial use was in design +of curves and surfaces, and has recently been used in +several applications. Catmull-Rom splines are a family of +cubic interpolating splines formulated such that the +tangent at each point is calculated using the previous and +next point on the spline. The results are similar to ones +produced by bicubic interpolation with regards to +sharpness, but the Catmull-Rom reconstruction is clearly +superior in smooth signal region.

    +

    Lanczos

    +

    The Lanczos interpolation function is a mathematical formula +used to smoothly interpolate the value of a digital +image between its samples. It maps each sample of the +given image to a translated and scaled copy of the Lanczos +kernel, which is a sinc function windowed by the central +hump of a dilated sinc function. The sum of these +translated and scaled kernels is then evaluated at the +desired pixel. Lanczos interpolation has the best +properties in terms of detail preservation and minimal +generation of aliasing artifacts for geometric +transformations not involving strong down sampling. +However higher order Lanczos interpolation requires high +computational time, which makes it unsuitable for +most commercial software.

    +

    Blackman

    +

    Blackman is a modification of Lanczos that has better control of ringing artifacts.

    +

    Server API

    +

    Like most commercial image hosting services, we have chosen to implement a cookie-free thumbnail API to minimize request latency by avoiding unnecessary network traffic.

    +

    Learn more ›

    +

    Further Reading

    + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/media/videos/index.html b/developer-guide/media/videos/index.html new file mode 100644 index 0000000000..b135c1bee9 --- /dev/null +++ b/developer-guide/media/videos/index.html @@ -0,0 +1,7736 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Videos - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Video File Support

    +

    Codecs and Containers

    +

    For maximum browser compatibility, PhotoPrism can transcode video codecs and containers supported by FFmpeg to MPEG-4 AVC.

    +

    Running the following command in a terminal displays a list of supported codecs:

    +
    ffmpeg -decoders
    +
    +

    See our advanced setup guide to learn how to configure hardware video transcoding.

    +

    Please Note:

    +
      +
    1. Not all video and audio formats can be played with every browser. For example, AAC - the default audio codec for MPEG-4 AVC / H.264 - is supported natively in Chrome, Safari, and Edge, while it is only optionally supported by the OS in Firefox and Opera.
    2. +
    3. HEVC/H.265 video files can have a .mp4 file extension too, which is often associated with AVC only. This is because MP4 is a container format, meaning that the actual video content may be compressed with H.264, H.265, or something else. The file extension doesn't really tell you anything other than that it's probably a video file.
    4. +
    5. In case FFmpeg is disabled or not installed, videos cannot be indexed because still images cannot be created. You should also have ExifTool enabled to extract metadata such as duration, resolution, and codec.
    6. +
    +

    Hybrid Photo/Video Formats

    +

    For more information on hybrid photo/video file formats, e.g. Apple Live Photos and Samsung/Google Motion Photos, see github.com/photoprism/photoprism/tree/develop/pkg/media and /developer-guide/media/live/.

    +

    Standard Resolutions

    +

    The PHOTOPRISM_FFMPEG_SIZE config option allows to limit the resolution of transcoded videos. It accepts the following standard sizes, while other values are automatically adjusted to the next supported size:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SizeUsage
    720SD TV, Mobile
    1280HD TV, SXGA
    1920Full HD
    2048DCI 2K, Tablets
    2560Quad HD, Notebooks
    38404K Ultra HD
    4096DCI 4K, Retina 4K
    76808K Ultra HD 2
    +

    Technical References and Tutorials

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TitleURL
    Web Video Codec Guidehttps://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs
    Web Video Content-Type Headershttps://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter
    Media Container Formatshttps://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
    MP4 Signature Formathttps://www.file-recovery.com/mp4-signature-format.htm
    List of file signatures (Wikipedia)https://en.wikipedia.org/wiki/List_of_file_signatures
    How to use the io.Reader interfacehttps://yourbasic.org/golang/io-reader-interface-explained/
    AV1 Codec ISO Media File Formathttps://aomediacodec.github.io/av1-isobmff
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/metadata/cameras/index.html b/developer-guide/metadata/cameras/index.html new file mode 100644 index 0000000000..62a606f792 --- /dev/null +++ b/developer-guide/metadata/cameras/index.html @@ -0,0 +1,7669 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Camera Models - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    + +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Camera Brands and Models

    +

    Market Share by Brand (2016)

    +

    DSLR Cameras

    + + + + + + + + + + + + + + + + + + + + + +
    BrandShare
    Canon63.3%
    Nikon31.6%
    Ricoh Imaging (Pentax)4.8%
    +

    System Cameras

    + + + + + + + + + + + + + + + + + + + + + +
    BrandShare
    Olympus26.8%
    Canon18.5%
    Sony17.9%
    +

    See also Brands with the highest market share 2016.

    + +

    https://www.flickr.com/cameras

    +

    Camera Models Supported by Photoshop

    +

    https://helpx.adobe.com/camera-raw/kb/camera-raw-plug-supported-cameras.html

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/metadata/colors/index.html b/developer-guide/metadata/colors/index.html new file mode 100644 index 0000000000..253ecfcd03 --- /dev/null +++ b/developer-guide/metadata/colors/index.html @@ -0,0 +1,7867 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Color Detection - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Color Detection

    +

    Color detection is performed while indexing using a 3x3 thumbnail that covers the top, bottom, and center of an image e.g. https://demo.photoprism.app/library/browse?color=green.

    +

    Standard Colors

    +

    The following color names can be used when searching for pictures:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    IDNameCodeSample
    0Black#000000
    1Brown#795548
    2Grey#9E9E9E
    3White#FFFFFF
    4Purple#9C27B0
    5Gold#FFDF00
    6Blue#3F51B5
    7Cyan#00BCD4
    8Teal#009688
    9Green#4CAF50
    ALime#CDDC39
    BYellow#FFEB3B
    CMagenta#FF00FF
    DOrange#FF9800
    ERed#F44336
    FPink#E91E63
    +

    Color Examples

    +

    This overview shows additional examples of matching color codes:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    IDNameCodeSample
    0Black#000000
    1Brown#A1887F #8D6E63 #A07F6C #9B7B5B #75645B #795548 #6D4C41 #5D4037 #9B6136 #C1A487 #AA8062 #6B5546 #B4B59C #B2B49B
    2Grey#E0E0E0 #9E9E9E #757575 #616161 #424242 #847A72 #DFE0E1
    3White#FFFFFF #E4E4E4 #E7E7E7
    4Purple#F3E5F5 #E1BEE7 #CE93D8 #BA68C8 #AB47BC #9C27B0 #9B318F #86007E #8E24AA #7B1FA2 #6A1B9A #4A148C #AA00FF #EDE7F6 #D1C4E9 #B39DDB #9575CD #7E57C2 #5E35B1 #673AB7 #512DA8 #4527A0 #311B92 #B388FF #7C4DFF #8E6493 #5E3A5E #440E79 #483678 #4E3880 #3B0E79
    5Gold#EDDEAC #E8B451 #C08A3E #A27D4b #755531 #D19327 #DEA253 #D5AA6F #F5EAD4
    6Blue#3F51B5 #C5CAE9 #5C6BC0 #3949AB #303F9F #283593 #1A237E #536DFE #3D5AFE #304FFE #2196F3 #BBDEFB #90CAF9 #64B5F6 #42A5F5 #1E88E5 #1976D2 #1565C0 #0D47A1 #82B1FF #448AFF #2979FF #2962FF #03A9F6 #B3E5FC #81D4FA #4FC3F7 #29B6F6 #039BE5 #0288D1 #0277BD #01579B #80D8FF #40C4FF #00B0FF #0091EA #607D8B #78909C #546E7A #37474F #E4EBFD #7DD3EA #076399 #28446B #4AC8F5 #0800F4 #012D5F
    7Cyan#B2EBF2 #80DEEA #4DD0E1 #26C6DA #00B8D4 #00BCD4 #00ACC1 #0097A7 #00838F #006064 #84FFFF #18FFFF #00E5FF
    8Teal#009688 #00897B #00796B #00695C #045D5C #245A5F #03454F #2C545E #174741
    9Green#E8F5E9 #C8E6C9 #ABC7B0 #A5D6A7 #81C784 #66BB6A #4CAF50 #43A047 #388E3C #2E7D32 #1B5E20 #F1F8E9 #DCEDC8 #C5E1A5 #AED581 #8BC34A #9CCC65 #7CB342 #689F38 #558B2F #33691E #B9F6CA #69F0AE #00C853 #00E676 #CCFF90 #B2FF59 #76FF03 #64DD17 #DDD579 #EEECA2 #244E3B #9A9D47 #BEBD76 #5C5A30 #B3C16C #ACA783 #474C25 #CDD087 #796D41
    ALime#F0F4C3 #E6EE9C #DCE775 #D4E157 #CDDC39 #C0CA33 #AFB42B #EEFF41 #C6FF00 #AEEA00
    BYellow#FFF9C4 #FFF59D #FFF176 #FFEE58 #FFFF8D #FFFF00 #FFD54F #FFCA28 #E3CE81 #D1AF52 #EEBB2B #D3A83A #C5A702 #9F8201 #E8CE03
    CMagenta#FF00FF #E500E5 #F000B5 #CE009B #C0055B #B00085 #A82863 #5B002F #4B0121 #860225 #CB023D #64071A #9E0047 #DC7ACF
    DOrange#FF9800 #FFA726 #FB8C00 #F57C00 #EF6C00 #FF9100 #FF6D00 #FD9A31 #7D2704 #FD571F #F86704 #FD9A00 #FE8A00 #F19652 #E58347 #C94C30 #9F5601 #FA6801 #F9A825 #BB723D
    ERed#FF5252 #F44336 #EF5350 #E53935 #F6292E #FC252D #D32F2F #C62828 #BA2830 #B71C1C #D50000 #DB0806 #CF0904 #D81A14 #CC1708 #D80807 #DE2616 #EE240F #A1211F #701219 #511218 #491114
    FPink#FCE4EC #FDC8EB #E79FA6 #F8BBD0 #F48FB1 #FF80AB #FF4081 #F50057 #F06292 #EC407A #E91E63 #D81B60 #C2185B
    +

    HCL vs HSL

    +

    Our color library stores colors in RGB and provides methods to convert them to different color spaces, including HCL and HSL:

    +

    +

    Learn more ›

    +

    Color Profiles

    +

    sRGB is the standard color space used when generating thumbnails. An ICC color profile for wide-gamut displays can optionally be embedded.

    +

    Learn more ›

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/metadata/exif/46595860-a4b7f280-cada-11e8-9ef8-d64c5f560148.png b/developer-guide/metadata/exif/46595860-a4b7f280-cada-11e8-9ef8-d64c5f560148.png new file mode 100644 index 0000000000..4ba71003d3 Binary files /dev/null and b/developer-guide/metadata/exif/46595860-a4b7f280-cada-11e8-9ef8-d64c5f560148.png differ diff --git a/developer-guide/metadata/exif/46595914-d4ff9100-cada-11e8-9482-2bc6c82d81ef.jpg b/developer-guide/metadata/exif/46595914-d4ff9100-cada-11e8-9482-2bc6c82d81ef.jpg new file mode 100644 index 0000000000..3a9aa92cb7 Binary files /dev/null and b/developer-guide/metadata/exif/46595914-d4ff9100-cada-11e8-9482-2bc6c82d81ef.jpg differ diff --git a/developer-guide/metadata/exif/46595915-d4ff9100-cada-11e8-940b-9cd3d91135e7.jpg b/developer-guide/metadata/exif/46595915-d4ff9100-cada-11e8-940b-9cd3d91135e7.jpg new file mode 100644 index 0000000000..5024e489ef Binary files /dev/null and b/developer-guide/metadata/exif/46595915-d4ff9100-cada-11e8-940b-9cd3d91135e7.jpg differ diff --git a/developer-guide/metadata/exif/edit/index.html b/developer-guide/metadata/exif/edit/index.html new file mode 100644 index 0000000000..2ea838e154 --- /dev/null +++ b/developer-guide/metadata/exif/edit/index.html @@ -0,0 +1,7741 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Editing Exif Data - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Editing Exif Data

    + +

    You can install ExifTool via homebrew, another package manager, or from the homepage:

    +

    https://exiftool.org/

    +

    The Exif-Read-Tool can be found on GitHub:

    +

    dsoprea/go-exif

    +

    Show exif data for debugging

    +

    Show exif data read from exiftool

    +

    exiftool -j photo.jpg

    +

    Show exif data read from exif-read-tool

    +

    exif-read-tool -f photo.jpg

    +

    Edit EXIF data for testing

    +

    Set CreateDate

    +

    exiftool -CreateDate="1919:05:04 05:59:26+02:00" dog_toshi_yellow.jpg

    +

    Remove all GPS data

    +

    exiftool -GPS:all= file.jpg

    +

    Remove all exif data

    +

    exiftool -all= foo.jpg

    +

    Further examples can be found here: https://exiftool.org/examples.html

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/metadata/exif/index.html b/developer-guide/metadata/exif/index.html new file mode 100644 index 0000000000..a055b231c3 --- /dev/null +++ b/developer-guide/metadata/exif/index.html @@ -0,0 +1,7656 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Exif Extraction - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Exif Extraction

    + +

    PhotoPrism uses the dsoprea/go-exif package to natively extract EXIF information from images. Exiftool support can be optionally enabled to support a wider range of metadata formats and extraction from video files.

    + +

    Using Exiftool

    +

    Assuming you have Docker installed and want to run exiftool with Debian 12 "Bookworm", you can simply run this command to open a terminal:

    +
    docker run --rm -v ${PWD}:/test -w /test -ti debian:bookworm bash
    +
    +

    This will mount the current working directory as /test. Of course, you can also specify a full path instead of ${PWD}.

    +

    The available Ubuntu, Debian and PhotoPrism images can be found on Docker Hub:

    + +

    Now install exiftool and any other packages you need, e.g. libheif-examples to convert HEIF images to JPEG, via apt:

    +
    apt update
    +apt install -y exiftool libheif-examples
    +
    +

    To view the image metadata, run exiftool -n <filename>:

    +
    root@1ad9fb887a4f:/test# exiftool -n IMG_8437.HEIC.jpg | grep ation
    +File Modification Date/Time     : 2022:09:18 08:16:28+00:00
    +Orientation                     : 6
    +Exposure Compensation           : 0
    +root@1ad9fb887a4f:/test# exiftool -n IMG_8437.HEIC | grep ation
    +File Modification Date/Time     : 2022:09:17 16:57:40+00:00
    +Orientation                     : 6
    +Exposure Compensation           : 0
    +HEVC Configuration Version      : 1
    +Min Spatial Segmentation IDC    : 0
    +Rotation                        : 270
    +
    +

    Exiftool Parameters

    +
      +
    • -n displays the raw values without changes
    • +
    • -j will format the output as JSON
    • +
    • -g groups the output by metadata source
    • +
    +

    Screenshots

    +

    This is how other apps show metadata:

    +

    apple_exif

    +

    photo_2018-10-08_09-16-15

    +

    photo_2018-10-08_09-16-12

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/metadata/geocoding/index.html b/developer-guide/metadata/geocoding/index.html new file mode 100644 index 0000000000..f9a8de624e --- /dev/null +++ b/developer-guide/metadata/geocoding/index.html @@ -0,0 +1,7834 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Reverse Geocoding - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Reverse Geocoding

    +

    If enabled, reverse geocoding enriches photo and video metadata with details such as country, state, city, and category.

    +

    Privacy Policy

    +

    As explained in detail in our Privacy Policy, reverse geocoding depends on retrieving the necessary information from a backend that we provide for this purpose.

    +

    The costs are currently fully covered by us for all users, including non-sponsors, and we ensure a very high level of privacy and confidentiality:

    +
      +
    • API requests are not logged permanently.
    • +
    • Our API approximates the coordinates and encodes them with a fuzzy S2 cell ID that does not include the house number or any other data identifying a specific residential address, except possibly in very sparsely populated areas of the world. Even then, we cannot trace the request back to a person, picture or point in time.
    • +
    • We may store your server's IP address and other HTTP request headers for a limited time to perform authorization checks, prevent abuse, and implement rate limits. Since the traffic is encrypted, no one intercepting the server-to-server communication can see the exact request and response; only the fact that you exchanged data with our backend.
    • +
    +

    Other open source applications sometimes use the free developer APIs operated by openstreetmap.org. In this case, their usage and privacy policies apply, which means that your request data is stored and used to create publicly available reports. This is different from our approach, which focuses on our users' privacy and user experience.

    +

    Example Request

    +

    GET https://places.photoprism.app/v1/location/149ce78563

    +
    {
    +  "id": "s2:149ce78563",
    +  "name": "Elafonisi Kite Club",
    +  "street": "Κεφαλή",
    +  "postcode": "",
    +  "category": "nature",
    +  "timezone": "Europe/Athens",
    +  "lat": 35.269638,
    +  "lng": 23.536951,
    +  "place": {
    +    "id": "gr:GCd9oey68OFr",
    +    "label": "Χρυσοσκαλίτισσα, Greece",
    +    "district": "",
    +    "city": "Χρυσοσκαλίτισσα",
    +    "state": "Αποκεντρωμένη Διοίκηση Κρήτης",
    +    "country": "gr",
    +    "keywords": "χρυσοσκαλίτσα"
    +  },
    +  "events": [],
    +  "licence": "Data © PhotoPrism"
    +}
    +
    +

    Location Data

    +

    See: internal/maps/location.go

    +

    World Maps

    +

    PhotoPrism also includes four high-resolution world maps that allow you to browse photos by location, see Web User Interface > Interactice Maps. Visit try.photoprism.app/library/places to try them on our demo.

    + +

    Event Discovery

    + +

    Libraries

    + +

    Commercial Services

    + +

    Tutorials

    + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/metadata/img/editPhotoLabels.jpeg b/developer-guide/metadata/img/editPhotoLabels.jpeg new file mode 100644 index 0000000000..220a0e0f44 Binary files /dev/null and b/developer-guide/metadata/img/editPhotoLabels.jpeg differ diff --git a/developer-guide/metadata/img/landmark-query.jpg b/developer-guide/metadata/img/landmark-query.jpg new file mode 100644 index 0000000000..ff81f28f52 Binary files /dev/null and b/developer-guide/metadata/img/landmark-query.jpg differ diff --git a/developer-guide/metadata/img/neuralnetworks.png b/developer-guide/metadata/img/neuralnetworks.png new file mode 100644 index 0000000000..674e2c1d52 Binary files /dev/null and b/developer-guide/metadata/img/neuralnetworks.png differ diff --git a/developer-guide/metadata/img/placesPrivacy.jpeg b/developer-guide/metadata/img/placesPrivacy.jpeg new file mode 100644 index 0000000000..fe6406f138 Binary files /dev/null and b/developer-guide/metadata/img/placesPrivacy.jpeg differ diff --git a/developer-guide/metadata/orientation/index.html b/developer-guide/metadata/orientation/index.html new file mode 100644 index 0000000000..6650a02cb0 --- /dev/null +++ b/developer-guide/metadata/orientation/index.html @@ -0,0 +1,7665 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Image Orientation - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Image Orientation

    +

    Using Exiftool

    +

    Assuming you have Docker installed and want to run exiftool with Debian 12 "Bookworm", you can simply run this command to open a terminal:

    +
    docker run --rm -v ${PWD}:/test -w /test -ti debian:bookworm bash
    +
    +

    This will mount the current working directory as /test. Of course, you can also specify a full path instead of ${PWD}.

    +

    The available Ubuntu, Debian and PhotoPrism images can be found on Docker Hub:

    + +

    Now install exiftool and any other packages you need, e.g. libheif-examples to convert HEIF images to JPEG, via apt:

    +
    apt update
    +apt install -y exiftool libheif-examples
    +
    +

    To view the image metadata, run exiftool -n <filename> and optionally use grep to filter the output:

    +
    root@1ad9fb887a4f:/test# exiftool -n IMG_8437.HEIC.jpg | grep ation
    +File Modification Date/Time     : 2022:09:18 08:16:28+00:00
    +Orientation                     : 6
    +Exposure Compensation           : 0
    +root@1ad9fb887a4f:/test# exiftool -n IMG_8437.HEIC | grep ation
    +File Modification Date/Time     : 2022:09:17 16:57:40+00:00
    +Orientation                     : 6
    +Exposure Compensation           : 0
    +HEVC Configuration Version      : 1
    +Min Spatial Segmentation IDC    : 0
    +Rotation                        : 270
    +
    +

    Rotation and Orientation are the important values you should pay attention to and compare. The rotation is in degrees.

    +

    Exiftool Parameters

    +
      +
    • -n displays the raw values without changes
    • +
    • -j will format the output as JSON
    • +
    • -g groups the output by metadata source
    • +
    +

    Exif Values

    +

    The numbers used to specify the image orientation are defined as follows:

    +
      +
    1. = 0 degrees: the correct orientation, no adjustment is required.
    2. +
    3. = 0 degrees, mirrored: image has been flipped back-to-front.
    4. +
    5. = 180 degrees: image is upside down.
    6. +
    7. = 180 degrees, mirrored: image has been flipped back-to-front and is upside down.
    8. +
    9. = 90 degrees: image has been flipped back-to-front and is on its side.
    10. +
    11. = 90 degrees, mirrored: image is on its side.
    12. +
    13. = 270 degrees: image has been flipped back-to-front and is on its far side.
    14. +
    15. = 270 degrees, mirrored: image is on its far side.
    16. +
    +

    Learn more ›

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/metadata/perceptual-hashes/index.html b/developer-guide/metadata/perceptual-hashes/index.html new file mode 100644 index 0000000000..29f8e38411 --- /dev/null +++ b/developer-guide/metadata/perceptual-hashes/index.html @@ -0,0 +1,7707 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Perceptual Hashes - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Perceptual Image Hashes

    +

    Perceptual Image Hashing refers to the use of a fingerprint algorithm to generate a hash that correlates with the appearance of an image without depending on other parameters such as file size, type, name, resolution, or other metadata.

    +

    Sorting by Visual Similarity

    +

    PhotoPrism currently generates a perceptual hash while indexing, which can be used to sort search results by visual similarity, e.g.:

    + +

    Note, though, that it is not yet possible to automatically stack files based on this and that the hash is not 100% precise, so visually different images may have the same hash. This is because it was developed for sorting and not for stacking or finding duplicates.

    +

    Stacking of Similar Files

    +

    Developing a user-friendly web interface for semi-automatic stacking of a large number of images based on their appearance is expected to require a significant amount of work.

    +

    A simpler solution could be to add one or more additional stacking options on the library settings page and allow them to be applied to existing pictures by pressing a button or running a terminal command, but without being able to see the result in advance or perform manual changes.

    +

    See the following issues for related feature requests:

    + + +

    Software Libraries & Examples

    + +

    References, Algorithms & Tutorials

    + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/metadata/xmp/index.html b/developer-guide/metadata/xmp/index.html new file mode 100644 index 0000000000..3038129ea1 --- /dev/null +++ b/developer-guide/metadata/xmp/index.html @@ -0,0 +1,7718 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Adobe XMP - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    + +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Adobe XMP

    +

    XMP (Extensible Metadata Platform) is the standard sidecar file format supported by Adobe Lightroom. While YAML files might be easier to understand, read and edit for humans, using the XML-based XMP format simplifies importing metadata from Lightroom and we can leverage a documented standard. Ideally, data can be kept in sync continuously between PhotoPrism and other photo management applications.

    +

    A proof-of-concept for reading Title, Copyright, Artist and Description is implemented but full support is a lot more work, contributions welcome. One issue is proper XML parsing in Go as basic types like date and time are not supported by xml.Unmarshaler. GPS coordinates are not stored as float but as a string like 52,27.5814N.

    +

    The original plan to build upon go-xmp didn't work out as we couldn't read many fields, so we're using pure Go for now until we find a way to get the data we need with go-xmp. It might be a bug and/or it's an issue with our specific XMP files.

    +

    RAW Conversion

    +

    PhotoPrism currently supports Darktable and RawTherapee as RAW image converters (as well as Sips on macOS). Darktable fully supports XMP sidecar files, RawTherapee might only partially. However, XMP is only a "container" format, so the fields (namespaces) used there to indicate how an image should be converted (as well as other metadata) differ between Lightroom/Photoshop, Darktable, and RawTherapee.

    +

    In other words, just because an application generally supports XMP that doesn't mean it can use metadata created with another application or by another vendor like Adobe. If you think that's confusing, well, that's because it is. You have an open format, but you still suffer from vendor lock-in - probably not entirely unintentional on Adobe's part.

    +

    From our experience, some basic edits done with Adobe tools - such as cropping - might be preserved when you convert the same RAW image with other software like Darktable. Advanced edits, such as lens or color corrections, will likely not be applied.

    +

    Learn more ›

    +

    File Samples

    +

    We would be happy to receive more XMP files for testing, either via pull request or email.

    +

    Specification

    + +

    Open Issues

    +
      +
    • Experiment with Adobe Lightroom to see how it uses sidecar files. The new version doesn't seem to use XMP to automatically sync metadata anymore, probably because Adobe focuses on cloud storage. Needs further investigation.
    • +
    • Create a matrix showing what fields are used/supported by which application/tool (Photoshop, Lightroom, Darktable and others, see
    • +
    • Read http://www.exiv2.org/tags-xmp-crs.html (Camera Raw Schema)
    • +
    +

    Released Features

    + +

    External Resources

    +
      +
    • trimmer-io/go-xmp - A native Go SDK for the Extensible Metadata Platform (XMP)
    • +
    • XMP code in GIMP - Nothing beyond some comments. It was a code drop, we needed the feature, but unfortunately the original contributor left.
    • +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/native-apps/img/prismatic-screenshot.jpg b/developer-guide/native-apps/img/prismatic-screenshot.jpg new file mode 100644 index 0000000000..c74036ad37 Binary files /dev/null and b/developer-guide/native-apps/img/prismatic-screenshot.jpg differ diff --git a/developer-guide/native-apps/index.html b/developer-guide/native-apps/index.html new file mode 100644 index 0000000000..5371b99be6 --- /dev/null +++ b/developer-guide/native-apps/index.html @@ -0,0 +1,7737 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + iOS and Android - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Native Mobile Apps for iOS and Android

    +

    As an addition to our platform-independent Progressive Web App (PWA), the following native apps are being maintained by other companies and individual developers:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NamePlatformDeveloperLicenseDownload
    Gallery for PhotoPrismAndroidOleg KoretskyGPL 3.0Google Play, F-Droid
    Photo MapAndroidDenny WeinbergClosed SourceGoogle Play
    PhotoSynciOS, Androidtouchbyte GmbHClosed SourceApp Store, Google Play
    PhotoflareiOSChris Wunschn/aTestFlight
    PrismaticiOSChris Lin/aTestFlight
    + +

    With this Android app, you can easily browse the pictures in your library and share them with other apps. The app offers a timeline view, authentication, bookmarks, and many other useful features. It can also be installed on Android TV, so you can browse your library with a remote control.

    +
    + Google Play Store + Get it on F-Droid +
    + +

    Photo Map

    +

    Photo Map is a perfect Android app for travelers: It displays all your pictures on an interactive world map, so you never lose track of when and where you took them. With the latest update, videos are now played directly in the app. Also, the number of photos that can be displayed is no longer limited.

    +
    + Google Play Store +
    + +

    PhotoSync

    +

    PhotoSync lets you transfer photos and videos directly from your mobile phone. It backs up your pictures securely in the background or you can manually select files to upload into specific folders.

    +
    + Google Play Store + Apple App Store +
    + +

    Photoflare

    +

    Photoflare is an iOS app that is currently being developed by Chris Wunsch. It is at an early stage and already offers some basic functionality that can be tested via Apple TestFlight.

    +

    Learn more ›

    +

    Prismatic

    +

    Prismatic ScreenshotPrismatic is an iOS app developed by Chris Li that allows you to display pictures from your library in customizable widgets on your phone's home screen.

    +

    You can test this and other features by installing it via Apple TestFlight.

    +

    Give feedback ›

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/pull-requests/index.html b/developer-guide/pull-requests/index.html new file mode 100644 index 0000000000..868bdef033 --- /dev/null +++ b/developer-guide/pull-requests/index.html @@ -0,0 +1,7769 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Pull Requests - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Pull Requests

    +

    Acceptance Criteria

    +

    Because we want to create the best possible product for our users, we have a set of criteria to ensure that all +submissions are acceptable:

    + +
    +

    These guidelines are not intended as a filter or barrier to participation. If you are unfamiliar with +Open Source development, we will help you.

    +
    +

    Contributor License Agreement

    +

    After you submit your first pull request, you will be asked to accept our Contributor License Agreement (CLA). Visit photoprism.app/cla to learn more.

    +

    How to Create and Submit a Pull Request

    +

    Fork our repository

    +
      +
    • Click the Fork button in the header of our main repository
    • +
    • Clone the forked repository on your local computer:
        +
      • git clone https://github.com/[your username]/photoprism
      • +
      +
    • +
    • Connect your local to our "upstream" main repository by adding it as a remote:
        +
      • git remote add upstream https://github.com/photoprism/photoprism.git
      • +
      +
    • +
    • Create a new branch from develop - it should have a short and descriptive name (not "patch-1") that does not already exist, for example:
        +
      • git checkout -b feature/your_feature_name
      • +
      +
    • +
    • See also https://guides.github.com/activities/forking/
    • +
    +

    Make your changes

    +
      +
    • While you are working on it and your pull request is not merged yet, pull in changes from "upstream" often so that you stay up to date and there is a lower risk for merge conflicts:
        +
      • git fetch upstream
      • +
      • git merge upstream/develop
      • +
      +
    • +
    • We recommend running tests after each change to make sure you didn't break anything:
        +
      • make test
      • +
      +
    • +
    • Add tests for any new code
        +
      • If you have questions about how to do this, please ask in your pull request
      • +
      +
    • +
    • Run make fmt to ensure code is properly formatted according to our standards
    • +
    • If all tests are green and you see no other errors, commit your changes. To reference related GitHub issues, please end your commit message with the issue ID like #1234:
        +
      • git status -s
      • +
      • git add .
      • +
      • git commit -m "Your commit message #1234"
      • +
      +
    • +
    +

    When you are ready...

    +
      +
    • Verify you didn't forget to add / commit files, output of git status -s should be empty
    • +
    • Push all commits to your forked remote repository on GitHub:
      git push -u origin feature/your_feature_name
    • +
    • Create a pull request with a helpful description of what it does
    • +
    • Wait for us to perform a code review and fix the remaining issues, if any
    • +
    • Update and/or add documentation if needed
    • +
    • Sign the Contributor License Agreement (CLA)
    • +
    +

    You can also create a pull request if your changes are not yet complete or working. Just let us know +it's in progress, so we don't try to merge them. We can help you with a code review or other feedback +if needed. Please be patient with us.

    +

    Be aware that reviewing, testing and finally merging pull requests requires significant resources on our side. It can therefore take several months if it is not just a small fix, especially if extensive testing is needed to prevent bugs from getting into our stable version.

    +
    +

    Privacy Notice

    +

    We operate a number of web services that help us develop and maintain our software in collaboration with the open source community, for example translate.photoprism.app.

    +

    Because many of these apps and tools were originally developed for internal use without a high level of privacy in mind, we ask that you do not enter personal information such as your real name or personal email address if you want it to remain private.

    +

    Personal details may otherwise show up in logs, source code, translation files, commit messages, and pull request comments.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/security/go/cover.jpg b/developer-guide/security/go/cover.jpg new file mode 100644 index 0000000000..c33699fd21 Binary files /dev/null and b/developer-guide/security/go/cover.jpg differ diff --git a/developer-guide/security/go/index.html b/developer-guide/security/go/index.html new file mode 100644 index 0000000000..b40c6c3354 --- /dev/null +++ b/developer-guide/security/go/index.html @@ -0,0 +1,7517 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Secure Coding in Go - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Secure Coding in Go

    +

    Book Cover

    +

    The Go Language: Web Application Secure Coding Practices is a book written for anyone who is using the Go Programming Language and aims to use it for Web development.

    +

    You can download it in the following formats:

    + +

    This book is collaborative effort of Checkmarx Security Research Team and it follows the OWASP Secure Coding Practices - Quick Reference Guide v2 (stable) release.

    +

    Source: OWASP/Go-SCP

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/security/index.html b/developer-guide/security/index.html new file mode 100644 index 0000000000..dd20315925 --- /dev/null +++ b/developer-guide/security/index.html @@ -0,0 +1,8132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Testing Guide - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Security Testing Guide

    +

    Vulnerability Scanners

    +

    Nuclei Logo

    +

    Nuclei is a fast and customizable vulnerability scanner based on a simple YAML-based DSL. It uses community curated templates to find vulnerabilities in applications.

    +

    On a Mac, you can install it via Homebrew by running the following command:

    +
    brew install nuclei
    +
    +

    Web Security Checklist

    +

    OWASP® Logo

    +

    The following OWASP® Web Application Security Checklist is also available as PDF or Docx for printing:

    +

    Information Gathering

    +
      +
    • Manually explore the site
    • +
    • Spider/crawl for missed or hidden content
    • +
    • Check for files that expose content, such as robots.txt, sitemap.xml, .DS_Store
    • +
    • Check the caches of major search engines for publicly accessible sites
    • +
    • Check for differences in content based on User Agent (eg, Mobile sites, access as a Search engine Crawler)
    • +
    • Perform Web Application Fingerprinting
    • +
    • Identify technologies used
    • +
    • Identify user roles
    • +
    • Identify application entry points
    • +
    • Identify client-side code
    • +
    • Identify multiple versions/channels (e.g. web, mobile web, mobile app, web services)
    • +
    • Identify co-hosted and related applications
    • +
    • Identify all hostnames and ports
    • +
    • Identify third-party hosted content
    • +
    +

    Configuration Management

    +
      +
    • Check for commonly used application and administrative URLs
    • +
    • Check for old, backup and unreferenced files
    • +
    • Check HTTP methods supported and Cross Site Tracing (XST)
    • +
    • Test file extensions handling
    • +
    • Test for security HTTP headers (e.g. CSP, X-Frame-Options, HSTS)
    • +
    • Test for policies (e.g. Flash, Silverlight, robots)
    • +
    • Test for non-production data in live environment, and vice-versa
    • +
    • Check for sensitive data in client-side code (e.g. API keys, credentials)
    • +
    +

    Secure Transmission

    +
      +
    • Check SSL Version, Algorithms, Key length
    • +
    • Check for Digital Certificate Validity (Duration, Signature and CN)
    • +
    • Check credentials only delivered over HTTPS
    • +
    • Check that the login form is delivered over HTTPS
    • +
    • Check session tokens only delivered over HTTPS
    • +
    • Check if HTTP Strict Transport Security (HSTS) in use
    • +
    +

    Authentication

    +
      +
    • Test for user enumeration
    • +
    • Test for authentication bypass
    • +
    • Test for bruteforce protection
    • +
    • Test password quality rules
    • +
    • Test remember me functionality
    • +
    • Test for autocomplete on password forms/input
    • +
    • Test password reset and/or recovery
    • +
    • Test password change process
    • +
    • Test CAPTCHA
    • +
    • Test multi factor authentication
    • +
    • Test for logout functionality presence
    • +
    • Test for cache management on HTTP (eg Pragma, Expires, Max-age)
    • +
    • Test for default logins
    • +
    • Test for user-accessible authentication history
    • +
    • Test for out-of channel notification of account lockouts and successful password changes
    • +
    • Test for consistent authentication across applications with shared authentication schema / SSO
    • +
    +

    Session Management

    +
      +
    • Establish how session management is handled in the application (eg, tokens in cookies, token in URL)
    • +
    • Check session tokens for cookie flags (httpOnly and secure)
    • +
    • Check session cookie scope (path and domain)
    • +
    • Check session cookie duration (expires and max-age)
    • +
    • Check session termination after a maximum lifetime
    • +
    • Check session termination after relative timeout
    • +
    • Check session termination after logout
    • +
    • Test to see if users can have multiple simultaneous sessions
    • +
    • Test session cookies for randomness
    • +
    • Confirm that new session tokens are issued on login, role change and logout
    • +
    • Test for consistent session management across applications with shared session management
    • +
    • Test for session puzzling
    • +
    • Test for CSRF and clickjacking
    • +
    +

    Authorization

    +
      +
    • Test for path traversal
    • +
    • Test for bypassing authorization schema
    • +
    • Test for vertical Access control problems (a.k.a. Privilege Escalation)
    • +
    • Test for horizontal Access control problems (between two users at the same privilege level)
    • +
    • Test for missing authorization
    • +
    +

    Data Validation

    +
      +
    • Test for Reflected Cross Site Scripting
    • +
    • Test for Stored Cross Site Scripting
    • +
    • Test for DOM based Cross Site Scripting
    • +
    • Test for Cross Site Flashing
    • +
    • Test for HTML Injection
    • +
    • Test for SQL Injection
    • +
    • Test for LDAP Injection
    • +
    • Test for ORM Injection
    • +
    • Test for XML Injection
    • +
    • Test for XXE Injection
    • +
    • Test for SSI Injection
    • +
    • Test for XPath Injection
    • +
    • Test for XQuery Injection
    • +
    • Test for IMAP/SMTP Injection
    • +
    • Test for Code Injection
    • +
    • Test for Expression Language Injection
    • +
    • Test for Command Injection
    • +
    • Test for Overflow (Stack, Heap and Integer)
    • +
    • Test for Format String
    • +
    • Test for incubated vulnerabilities
    • +
    • Test for HTTP Splitting/Smuggling
    • +
    • Test for HTTP Verb Tampering
    • +
    • Test for Open Redirection
    • +
    • Test for Local File Inclusion
    • +
    • Test for Remote File Inclusion
    • +
    • Compare client-side and server-side validation rules
    • +
    • Test for NoSQL injection
    • +
    • Test for HTTP parameter pollution
    • +
    • Test for auto-binding
    • +
    • Test for Mass Assignment
    • +
    • Test for NULL/Invalid Session Cookie
    • +
    +

    Denial of Service

    +
      +
    • Test for anti-automation
    • +
    • Test for account lockout
    • +
    • Test for HTTP protocol DoS
    • +
    • Test for SQL wildcard DoS
    • +
    +

    Business Logic

    +
      +
    • Test for feature misuse
    • +
    • Test for lack of non-repudiation
    • +
    • Test for trust relationships
    • +
    • Test for integrity of data
    • +
    • Test segregation of duties
    • +
    +

    Cryptography

    +
      +
    • Check if data which should be encrypted is not
    • +
    • Check for wrong algorithms usage depending on context
    • +
    • Check for weak algorithms usage
    • +
    • Check for proper use of salting
    • +
    • Check for randomness functions
    • +
    +

    HTML 5

    +
      +
    • Test Web Messaging
    • +
    • Test for Web Storage SQL injection
    • +
    • Check CORS implementation
    • +
    • Check Offline Web Application
    • +
    +

    Risky Functionality

    +

    File Uploads

    +
      +
    • Test that acceptable file types are whitelisted
    • +
    • Test that file size limits, upload frequency and total file counts are defined and are enforced
    • +
    • Test that file contents match the defined file type
    • +
    • Test that all file uploads have Anti-Virus scanning in-place.
    • +
    • Test that unsafe filenames are sanitised
    • +
    • Test that uploaded files are not directly accessible within the web root
    • +
    • Test that uploaded files are not served on the same hostname/port
    • +
    • Test that files and other media are integrated with the authentication and authorisation schemas
    • +
    +

    Card Payment

    +
      +
    • Test for known vulnerabilities and configuration issues on Web Server and Web Application
    • +
    • Test for default or guessable password
    • +
    • Test for non-production data in live environment, and vice-versa
    • +
    • Test for Injection vulnerabilities
    • +
    • Test for Buffer Overflows
    • +
    • Test for Insecure Cryptographic Storage
    • +
    • Test for Insufficient Transport Layer Protection
    • +
    • Test for Improper Error Handling
    • +
    • Test for all vulnerabilities with a CVSS v2 score > 4.0
    • +
    • Test for Authentication and Authorization issues
    • +
    • Test for CSRF
    • +
    +

    Source: 0xRadi/OWASP-Web-Checklist

    +

    OpenSSF Security Criteria

    +

    The Open Source Security Foundation maintains standardized security criteria and best practices for open-source projects:

    +

    OpenSSF Best Practices

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/security/nuclei.png b/developer-guide/security/nuclei.png new file mode 100644 index 0000000000..074587e800 Binary files /dev/null and b/developer-guide/security/nuclei.png differ diff --git a/developer-guide/security/openssf.png b/developer-guide/security/openssf.png new file mode 100644 index 0000000000..3c6bf6353e Binary files /dev/null and b/developer-guide/security/openssf.png differ diff --git a/developer-guide/security/owasp.png b/developer-guide/security/owasp.png new file mode 100644 index 0000000000..b0af38b273 Binary files /dev/null and b/developer-guide/security/owasp.png differ diff --git a/developer-guide/security/policy/index.html b/developer-guide/security/policy/index.html new file mode 100644 index 0000000000..950291d90e --- /dev/null +++ b/developer-guide/security/policy/index.html @@ -0,0 +1,7508 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Security Policy - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Vulnerability Disclosure Guidelines

    +

    Visit photoprism.app/security-policy to learn more about our security policy, responsible disclosure, and how you can report issues as a business or organization.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/setup/index.html b/developer-guide/setup/index.html new file mode 100644 index 0000000000..acdfffe806 --- /dev/null +++ b/developer-guide/setup/index.html @@ -0,0 +1,7866 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Build Setup - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Setting Up Your Development Environment

    +

    Step 1: Use Git to Clone the Project from GitHub

    +

    Before running any commands, please make sure you have Git, Make, Docker, and Docker Compose installed on your system. These are available for Mac, Linux, and Windows.1

    +

    In case you are using Ubuntu Linux, you can run this script to install the latest Docker version including the Compose Plugin on your computer in one step:

    +
    bash <(curl -s https://setup.photoprism.app/ubuntu/install-docker.sh)
    +
    +

    When working on Microsoft Windows or Apple macOS, you need to install the latest version of Docker Desktop and also disable "autocrlf" in Git to avoid errors2:

    +
    git config --global core.autocrlf false
    +
    +

    Now change to the directory where you usually keep your development projects, and run this command to download the project from GitHub:

    +
    git clone git@github.com:photoprism/photoprism.git
    +
    +

    Once all code has been downloaded, change to the project directory which should now exist:

    +
    cd photoprism
    +
    +
    +

    Developing on Windows

    +

    Our standard development environment can also be used on Windows if you have Git, Docker Desktop, and a suitable IDE like GoLand installed. However, we recommend running the required docker compose commands manually instead of using make docker-build and make terminal. Learn more ›

    +
    +

    Step 2: Launch Your Local Development Environment

    +

    Pull the latest Docker images and then launch the pre-configured build environment we provide to have an isolated development container pre-installed with all the tools you might need, including the latest versions of Go, NodeJS, and NPM:3

    +
    make docker-build
    +docker compose up
    +
    +

    This environment is for testing and development purposes only. Do not use it in production. Also note that our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    +

    Step 3: Install the Dependencies and Start Developing

    +

    Open a terminal to run commands directly in your local development environment:

    +
    make terminal
    +
    +

    Before starting to build, make sure all dependencies, such as NPM packages and TensorFlow models, are installed:

    +
    make dep
    +
    +

    Congratulations! You can now build the frontend assets (JS), compile the backend binary (Go) and then run a custom PhotoPrism version in your local environment:

    +

    make build-js
    +make build-go
    +./photoprism start
    +
    +After PhotoPrism has been started as shown above, the user interface can be opened in a web browser by navigating to one of these URLs:

    + +

    In the build environment, the default login is admin with the password photoprism. You can disable it with the --public command flag:

    +
    ./photoprism --public start
    +
    +

    You can find the default settings in the compose.yaml or docker-compose.yml file located in the root of the project. Keep them when you run tests. Otherwise, the tests may fail for others, even if they succeed in your local environment.

    +
    +

    You can find a list of all make targets in the Makefile. +For example, make test will run frontend and backend unit tests. Wrong filesystem permissions can be fixed by +running make fix-permissions in a terminal.

    +
    +

    Optional: Build the Frontend in Watch Mode

    +

    The integrated web server not only provides the backend API, but is also used to serve static assets. These can be +automatically rebuilt (updated) when you change a file. To do this, run the following command in a terminal, either +inside or outside the container (outside is faster if your host is not running Linux):

    +
    make watch-js
    +
    +

    Alternatively, you can change to the frontend directory and run NPM directly:

    +
    cd frontend
    +npm run watch
    +
    +

    To update the frontend dependencies, also change to the frontend directory and run:

    +
    npm update
    +
    +

    Optional: Use a Go Debugger

    +

    To debug the backend Go code, first make sure you have built and run the containers as described above:

    +
    make docker-build
    +docker compose up
    +
    +

    Note: If you make changes in the Dockerfile to test things out, you can build and run the photoprism container individually:

    +
    docker compose build photoprism
    +docker compose up photoprism
    +
    +

    Then open a terminal to the container as described above:

    +
    make terminal
    +
    +

    and if you made code changes, make sure the rebuild the Go code:

    +
    make build-go
    +
    +

    You can then run the Delve command from inside the container to start the debugger - the command-line options can be customized based on your needs:

    +
    dlv --listen=:40000 --headless=true --log=true --log-output=debugger,debuglineerr,gdbwire,lldbout,rpc --accept-multiclient --api-version=2 exec ./photoprism -- start
    +
    +

    Once you run this command, you can use VSCode or Goland to add breakpoints and step through breakpoints in the code. Follow the instructions here to set up VSCode, and here to set up Goland to connect to the debugger.

    +

    Once the debugger is running, you can view the app at http://localhost:2342/ and debug the code.

    +

    Questions?

    +
      +
    • Radomir Sohlich wrote a pragmatic introduction to Makefiles for Go developers
    • +
    • we are using Go Modules for managing our dependencies (new in 1.11)
    • +
    • this guide was not tested on Windows, you might need to edit your compose.yaml or docker-compose.yml to make it work with Windows specific paths
    • +
    +

    Apple Silicon, Raspberry Pi, and ARM64

    +

    Our development environment has been built into a single multi-arch image +for 64-bit AMD, Intel, and ARM processors. That means, Apple Silicon, Raspberry Pi +3 / 4, and other ARM64-based devices can pull from the same repository.

    +

    Multi-Arch Docker Builds

    +

    For information about multi-architecture Docker builds, see the following documentation:

    + +

    QEMU Quick Start

    +
      +
    1. install qemu-user-static from docker hub: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes multiarch/qemu-user-static
    2. +
    3. verify that dockers buildx command is installed docker buildx version. if missing, follow install instructions here
    4. +
    5. create buildx builder: docker buildx create --name multiarch-builder && docker buildx inspect --builder multiarch-builder --bootstrap
    6. +
    7. start building: make docker-development-multiarch or make docker-photoprism-multiarch
    8. +
    + + +
    +
    +
      +
    1. +

      Instead of using Docker, you can also set up your own build environment based on the steps documented in the Dockerfiles we provide. For this, you should have at least Go 1.22, TensorFlow for C, Make, NPM 10 and MariaDB 11 installed. Note that the test results will be unreliable without Docker. This method is therefore not well suited for contributors and we cannot provide support if something does not work as expected. 

      +
    2. +
    3. +

      If the Git config value "core.autocrlf" is set to "true", the following error may occur when trying to run shell scripts or Make targets: env: bash\r: No such file or directory 

      +
    4. +
    5. +

      Docker uses human-readable Dockerfiles that contain all the commands a user would invoke in a terminal to assemble a complete application image. 

      +
    6. +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/technologies/broadway/index.html b/developer-guide/technologies/broadway/index.html new file mode 100644 index 0000000000..c6fbc9ed7c --- /dev/null +++ b/developer-guide/technologies/broadway/index.html @@ -0,0 +1,7602 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Broadway - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Broadway

    + +

    Broadway is a display server for using GTK+ applications in a web browser like Chrome or Firefox. It is based on HTML5 and WebSockets. GTK (originally GIMP Toolkit) is a GUI toolkit used by many Linux desktop applications like Darktable, Gimp and RawTherapee. Obviously this technology is ideal for editing images in the browser, at least when the connection is fast enough.

    +

    Docker Configuration

    +
    # Configure broadwayd (HTML5 display server)
    +RUN apt-get update && apt-get install libgtk-3-bin
    +ENV GDK_BACKEND broadway
    +ENV BROADWAY_DISPLAY :5
    +EXPOSE 8080
    +CMD broadwayd -p 8080 -a 0.0.0.0 :5
    +
    +

    When this is configured, all you need to do is open http://localhost:8080 in a browser and start any GTK application in the same container (broadwayd must be running). It should be displayed instantly, otherwise the screen is white, but you shouldn't see an error.

    +

    Screenshot

    +

    Darktable in Chrome via Broadway

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/technologies/docker/index.html b/developer-guide/technologies/docker/index.html new file mode 100644 index 0000000000..1a4d7f7404 --- /dev/null +++ b/developer-guide/technologies/docker/index.html @@ -0,0 +1,7746 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Docker - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Docker

    + +

    Docker is an Open Source container virtualization tool. It is ideal for running +applications on any computer without extensive installation, configuration, or performance overhead.

    +

    We are aware Docker is not widely used by end users despite its many advantages. For this reason, we aim +to provide native binaries for common operating systems at a later time.

    +

    What are the benefits of using Docker?

    +

    (1) Docker uses standard features of the Linux kernel. Containers are nothing new; Solaris Zones were released about 20 years ago and the chroot system call was introduced during development of Version 7 Unix in 1979. It is used ever since for hosting applications exposed to the public Internet. Modern Linux containers are an incremental improvement of this, based on standard functionality that is part of the kernel.

    +

    (2) Docker saves time through simplified deployment and testing. A main advantage of Docker is that application images can be easily made available to users via Internet. It provides a common standard across most operating systems and devices, which saves our team a lot of time that we can then spend more effectively, for example, providing support and developing one of the many features that users are waiting for.

    +

    (3) Dockerfiles are part of the source code repository. Human-readable and versioned Dockerfiles that are part of our public source code help avoid "works for me" moments and other unwelcome surprises by enabling us to have the exact same environment everywhere in development, staging, and production.

    +

    (4) Running applications in containers is more secure. Last but not least, virtually all file format parsers have vulnerabilities that just haven't been discovered yet. This is a known risk that can affect you even if your computer is not directly connected to the Internet. Running apps in a container with limited host access is an easy way to improve security without compromising performance and usability.

    +
    +

    A virtual machine with a dedicated operating system environment provides even more security, but usually has side effects such as lower performance and more difficult handling. Using a VM, however, doesn't prevent you from running containerized apps to get the best of both worlds. This is essentially what happens when you install Docker on virtual cloud servers and operating systems other than Linux.

    +
    +

    Running Docker Images

    +

    Assuming you have Docker installed and want to test Debian 12 "Bookworm", you can simply run this command to open a terminal:

    +
    docker run --rm -v ${PWD}:/test -w /test -ti debian:bookworm bash
    +
    +

    This will mount the current working directory as /test. Of course, you can also specify a full path instead of ${PWD}.

    +

    The available Ubuntu, Debian and PhotoPrism images can be found on Docker Hub:

    + +

    Additional packages can be installed via apt:

    +
    apt update
    +apt install -y exiftool libheif-examples
    +
    +

    Continuous Integration / Deployment

    +

    Build and push of an updated container image to Docker Hub is automatically performed by Travis CI whenever develop is merged into master and the tests are all green. For that reason, we don't use semantic versioning for our binaries and container images. A version string might look like 181112-edc7c2f-Darwin-i386-DEBUG instead. Travis CI uses the photoprism/development image for running unit and integration tests on all branches and for pull requests, see Dockerfile.

    +

    Multi-Stage Build

    +

    When creating new images, Docker supports so called multi-stage builds, that means you can compile an application like PhotoPrism in a container that contains all development dependencies (like source code, debugger, compiler,...) and later copy the binary to a fresh container. This way we could reduce the compressed container size from ~1 GB to less than 200 MB. Most of that is used by Darktable, TensorFlow and Ubuntu 18.04. Our photoprism binary is smaller than 20 MB.

    +

    Example:

    +
    FROM photoprism/development:20181112 as build
    +
    +# Build PhotoPrism
    +WORKDIR "/go/src/github.com/photoprism/photoprism"
    +COPY . .
    +RUN make all install DESTDIR=/opt/photoprism
    +
    +# Base base image as photoprism/development
    +FROM ubuntu:18.04
    +
    +WORKDIR /opt/photoprism
    +
    +# Copy built binaries and assets to this image
    +COPY --from=build /usr/local/bin/photoprism /usr/local/bin/photoprism
    +COPY --from=build /opt/photoprism /opt/photoprism
    +
    +# Expose HTTP port
    +EXPOSE 80
    +
    +# Start PhotoPrism server
    +CMD photoprism start
    +
    +

    Kubernetes

    + +

    External Resources

    + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/technologies/external-apis/index.html b/developer-guide/technologies/external-apis/index.html new file mode 100644 index 0000000000..266907af9a --- /dev/null +++ b/developer-guide/technologies/external-apis/index.html @@ -0,0 +1,7630 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + External APIs - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + + +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/technologies/golang/index.html b/developer-guide/technologies/golang/index.html new file mode 100644 index 0000000000..331df42f31 --- /dev/null +++ b/developer-guide/technologies/golang/index.html @@ -0,0 +1,7672 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Golang - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Golang

    + +

    We have chosen Go because of its simplicity and performance. On top, it enables us to build a single binary for distribution.

    +

    Learning

    + +

    Testing

    + +

    Useful libraries and frameworks

    + +

    Jobs

    +
      +
    • SoundCloud is looking for backend developers (Berlin, permanent, full-time)
    • +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/technologies/tensorflow/index.html b/developer-guide/technologies/tensorflow/index.html new file mode 100644 index 0000000000..a3fdefe52f --- /dev/null +++ b/developer-guide/technologies/tensorflow/index.html @@ -0,0 +1,7636 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TensorFlow - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    TensorFlow

    + +

    Using TensorFlow with Go

    +

    For an introduction please read Understanding Tensorflow using Go.

    +

    The TensorFlow API for Go is well suited to loading existing models and executing them within a Go application. It requires the TensorFlow C library to be installed. A full TensorFlow installation is not needed.

    +

    It is not possible to statically link against the C library, but the issue is known and there might be a fix later this year.

    +

    Vision

    +

    Our long-term goal is to become an open platform for machine learning research based on real-world photo collections.

    +

    External Resources

    + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/technologies/yaml/index.html b/developer-guide/technologies/yaml/index.html new file mode 100644 index 0000000000..496f073e23 --- /dev/null +++ b/developer-guide/technologies/yaml/index.html @@ -0,0 +1,7788 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + YAML - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Introduction to YAML

    +

    YAML is a human-friendly format that we use for metadata exports and configuration files because of its simplicity and widespread support. The name originally meant Yet Another Markup Language. Common file extensions are .ymland .yaml.

    +

    Values are represented in the form key: value with one entry per line:

    +
    Type: image
    +Title: "La Tour Eiffel 🌈"
    +Year: 2014
    +# Key-Value Collection Example:
    +Details:
    +  Notes: "Bonjour\nla France!" 
    +  Keywords: 'paris, france' # Comment
    +
    +

    Basic Rules

    +
      +
    • Keys are case-sensitive
    • +
    • Related values must start at the same indentation level
        +
      • We recommend using 2 spaces, but any number will work as long as it is consistent
      • +
      • Tabs are not allowed for indentation and using them may result in errors
      • +
      +
    • +
    • Comments begin with #, can start anywhere on a line, and continue until the end of the line
    • +
    • You can generally use all Unicode characters in YAML files, including Emojis
        +
      • To avoid ambiguity, it is recommended to enclose text strings in single ' or double quotes ", especially if they contain spaces or Boolean values like "true" or "false"
      • +
      • The difference between single and double quotes is that double quotes support escape sequences like \t for a tab or \n for a new line
      • +
      • Additional special characters may need to be escaped, e.g. the $ sign when working with Docker Compose
      • +
      +
    • +
    +

    Multiple Values

    +

    List are lines that start at the same indentation level and begin with a dash and a space as shown in the example below. +They are commonly used to define service dependencies, exposed network ports, or folders shared between host and +container in docker-compose.yml files:

    +
    services:
    +  photoprism:
    +    depends_on:
    +      - mariadb
    +      - nextcloud
    +    ports:
    +      # "host:container"
    +      - "2342:2342"
    +    volumes:
    +      # "/host/folder:/container/folder"
    +      - "/photos:/photoprism/originals"
    +
    +

    Key-Value Pairs

    +

    Collections of key-value pairs are commonly used to specify the names and values of environment variables in docker-compose.yml files (see below for additional rules). +Similar to lists, the keys of related values start at the same indentation level, but without a dash:

    +
    services:
    +  mariadb:
    +    environment:
    +      MARIADB_AUTO_UPGRADE: "1"
    +      MARIADB_INITDB_SKIP_TZINFO: "1"
    +      MARIADB_ROOT_PASSWORD: "Y(&^UIk34"
    +      MARIADB_DATABASE: photoprism
    +      MARIADB_USER: photoprism
    +      MARIADB_PASSWORD: insecure
    +
    +

    Docker Compose

    +

    When using Docker Compose, some additional rules apply, as compose.yaml or docker-compose.yml files extend the YAML format with features such as variable interpolation.

    +

    Dollar Signs

    +

    If a configuration value in a compose.yaml or docker-compose.yml file contains a literal $ character, for example in a password, you must use $$ (a double dollar sign) to escape it so that e.g. "compo$e" becomes "compo$$e":

    +
    services:
    +  mariadb:
    +    environment:
    +      # sets password to "compo$e"
    +      MARIADB_PASSWORD: "compo$$e" 
    +
    +

    Values that contain a $ are otherwise interpreted as a variable. In this case, both the $VARIABLE and the ${VARIABLE} syntax are supported. Further details on the use of variables can be found in the file format reference.

    +

    True / False

    +

    Boolean variable values like "true", "false", "yes", "no", "on", or "off" must be enclosed in double or single quotes so that they are passed as intended:

    +
    services:
    +  photoprism:
    +    environment:
    +      PHOTOPRISM_DEFAULT_TLS: "true"
    +      PHOTOPRISM_READONLY: "false"
    +
    +

    If you otherwise specify true as a value without quotes, Docker Compose will pass the host variable of the same name to the container instead of setting the value to "true" (results in an empty string if no environment variable with the same name is set on the host):

    +
    services:
    +  photoprism:
    +    environment:
    +      # evaluated as "" (false)
    +      PHOTOPRISM_READONLY: true
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/tests/index.html b/developer-guide/tests/index.html new file mode 100644 index 0000000000..1b3f0aba08 --- /dev/null +++ b/developer-guide/tests/index.html @@ -0,0 +1,7955 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Running Tests - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Running Unit and Acceptance Tests

    +
    +

    In any process, obsessing about the wait times will yield greater enhancements than practically anything else, for longer than you might think. Automation, simplification, etc. are implementation details of that obsession. — Dan North

    +
    +

    Unit Tests

    +

    Go

    +

    To run all unit tests, type make test or go test ./internal/... in a terminal.

    +

    These make targets are currently defined for tests:

    +
      +
    • test: Executes all tests found in a) /internal with a timeout of 20 min and verbose output and b) frontend/tests/unit
    • +
    • test-short: Executes only fast tests found in /internal with a timeout of 5 min and verbose output
    • +
    • test-race: Same as test but with race condition detector (much slower) and higher timeout of 60 min
    • +
    • test-codecov: Same as test but creates a coverage log in coverage.txt and sends it to Codecov (don't use it locally)
    • +
    • test-coverage: Same as test but creates a coverage.txt file as well as a human-readable report in coverage.html; timeout is elevated to 30 min
    • +
    +

    You can run single tests via go test -run in a package directory, e.g. /internal/photoprism:

    +
    $ go test -run NameOfTest
    +
    +

    See docs for more info.

    +

    Test Frameworks

    +

    Go comes with a cool testing framework, it allows you to write test code using the same language, without needing to learn any library or test engine. Go advanced testing tips & tricks contains a lot of useful information. We only import testify/assert to save a few lines for common assertions.

    +

    Todo: Use a SQL mock driver to test database interactions, for example DATA-DOG/go-sqlmock.

    +

    Slow Tests

    +

    Slow tests and benchmarks can be skipped using the -short flag:

    +
    func TestTimeConsuming(t *testing.T) {
    +    if testing.Short() {
    +        t.Skip("skipping test in short mode.")
    +    }
    +    ...
    +}
    +
    +

    To execute:

    +
    go test -short
    +
    +

    Javascript

    +

    To run all javascript unit tests, type make test-js in a terminal.

    +

    In case you want to run a single test add .only to the test you want to run e.g.:

    +
     it.only("should get album id",  () => {
    +        const values = {
    +            ID: 5, AlbumName: "Christmas 2019", 
    +            AlbumSlug: "christmas-2019", AlbumUUID: 66
    +        };
    +        const album = new Album(values);
    +        const result = album.getId();
    +        assert.equal(result, "66");
    +    });
    +
    +

    Test coverage output is saved to frontend/coverage/html

    +

    Test Frameworks

    +

    To test javascript code we use mocha in combination with karma, chai, sinon and the karma-istanbul-coverage-reporter.

    +

    Acceptance Tests

    +

    Before you proceed run

    +
    make dep-acceptance
    +
    +

    Running Tests Inside the Docker Container

    +

    In the development container environment, you can run the tests in headless chrome:

    +
    make acceptance-run-chromium
    +
    + +

    Test Frameworks

    +

    Our goal was to implement UI acceptance tests using JavaScript, so that frontend developers are able to run and write them without learning Go.

    +

    To make a final decision, we compared TestCafe, Cypress and Nightwatch.js. +We agreed on using TestCafe as tests were the most stable and pretty fast (because no long timeouts are needed).

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FeatureTestCafeCypressNightwatch.js
    Supported Browsers LocalChrome,
    Firefox,
    Opera,
    Safari,
    Internet Explorer,
    Microsoft Edge,
    Google Chrome Canary,
    Chromium
    Chrome,
    Chromium,
    Chrome Canary,
    Electron
    Geckodriver,
    Chromedriver,
    Microsoft Webdriver,
    Safaridriver
    Supported Browsers HeadlessChrome,
    Firefox
    ElectronProblems running headless
    Continuous Integration Supportyesyesyes
    Setupeasy via npmeasy via npmeasy via npm
    Usability++++++++
    Speed (3 tests)2 min (headless chrome and firefox)
    1 min (only chrome headless)
    1 min (chrome headed)
    5 min (headless electron)
    2,5 min (chrome headed)
    7 min (chrome headed)
    headless not working
    Stabilityniceunstable --> waiting times neededunstable --> waiting times needed
    Documentation+++++++
    Noteseasy to find elementseasy to find elementsadditional library needed to find selectors by text
    +

    Other test libraries and frameworks we currently don't use:

    + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/translations-weblate/index.html b/developer-guide/translations-weblate/index.html new file mode 100644 index 0000000000..7f655d582f --- /dev/null +++ b/developer-guide/translations-weblate/index.html @@ -0,0 +1,7574 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Using Weblate - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Contributing Translations with Weblate

    +

    We operate translate.photoprism.app so that you can easily add and update user interface translations without having any development skills.

    +

    You can either sign in with your GitHub account or create a new account with an email account you have access to:

    +
      +
    • Provide an alternate address if you want your personal email to remain private as it may appear as authorship in commits, translation files, and pull requests.
    • +
    • You are also not required to provide your full name if you do not wish to do so.
    • +
    • Let us know privately that you have registered so that we can grant you permissions if needed.
    • +
    +

    When you sign up, you will be asked to accept our Contributor License Agreement (CLA). Visit photoprism.app/cla to learn more.

    +
    +
    +
    +
      +
    1. +

      Navigate to https://translate.photoprism.app/

      +
    2. +
    3. +

      Click Register and create an account

      +
    4. +
    +

    Screenshot

    +

    Screenshot

    +
    +
    +
      +
    1. Sign in
    2. +
    3. Select the language you want translate to
    4. +
    +

    Screenshot +3. Open your Dashboard and click Backend (or access to the Backend by clicking here)

    +

    Screenshot +4. Click on View contributor agreement

    +

    Screenshot +5. Accept the contributor agreement and click Submit

    +

    Screenshot +6. Go back to your Dashboard and repeat steps 3-5 for the Frontend-Component

    +
    +
    +

    Your Dashboard shows you how many strings have no translation yet.

    +

    Screenshot

    +
      +
    1. Click on the count
    2. +
    3. Enter the translation and click Save and continue
    4. +
    +

    Screenshot +3. Repeat until all strings are translated

    +

    Your translations will be reviewed and included in one of the next releases.

    +
    +
    +

    If translations are missing, we pre-translate messages using services such as DeepL and Google Translate. +This can lead to grammatical errors and misunderstandings. +Native speakers should check existing translations and improve them if necessary.

    +

    If you are a native speaker and would like to support us by reviewing existing translations, please contact us at hello@photoprism.app so that we can grant you the rights to review.

    +
      +
    1. From your Dashboard open one of the components by clicking on the language
    2. +
    +

    Screenshot +2. Click on Strings waiting for review

    +

    Screenshot +3. Review the translation - edit the string if needed. Then select Approved and click Save and continue

    +

    Screenshot +4. Repeat until all strings are reviewed

    +
    +
    +

    Please contact us at hello@photoprism.app and we will set up a new language for you to translate!

    +
    +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/translations/index.html b/developer-guide/translations/index.html new file mode 100644 index 0000000000..8b7ef37aec --- /dev/null +++ b/developer-guide/translations/index.html @@ -0,0 +1,7789 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Legacy Method - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Translations

    +
    +

    Please don't use the method described here to add/edit translations anymore use this instead!

    +
    +

    PhotoPrism uses gettext for frontend and backend localization. +It is one of the most widely used standards for user interface translation:

    +
      +
    • Human-readable messages such as File not found are used as identifiers to search for appropriate translations. They also serve as default values when no translation is available.
    • +
    • Messages can optionally contain placeholders for numbers and other variables, for example Found %{n} files.
    • +
    +

    We recommend Poedit for creating and updating translations. The download is free +for Mac, Windows and Linux. The source code can be obtained on GitHub.

    +
    +

    If translations are missing, we pre-translate messages using services such as DeepL and Google Translate. This can lead to grammatical errors and misunderstandings. Native speakers should check existing translations and improve them if necessary. You can use the version compare feature on GitHub to detect changes in translation files.

    +
    +

    Frontend

    +

    Localizations can be found in /frontend/src/locales. The POT file, only containing message ids, +is translations.pot.

    +

    *.po files contain localized messages for each +language +identified by their locale, +for example de.po for German and pt_BR.po for Brazilian Portuguese. +You can open, edit and save them with Poedit to update existing translations.

    +

    Add new translation

    +
      +
    • In /frontend run npm run gettext-extract
    • +
    • Install a translation tool e.g. Poedit
    • +
    • Open the /frontend/src/locales/translations.pot file with Poedit
    • +
    • In Poedit click on "Create New Translation" at the bottom and select the language
    • +
    • Now you can start translating
    • +
    • When done, save your translation as *.po file using the language locale (e.g. de.po) as name
    • +
    • Add the new language to the Languages function in /frontend/src/options/options.js
    • +
    • Run npm run gettext-compile to compile existing translations into a single translations.json file
    • +
    • To test your translations you need to build the frontend again using npm run build or npm run watch
    • +
    +

    Update existing translation

    +
      +
    • In /frontend run npm run gettext-extract
    • +
    • Install a translation tool e.g. Poedit
    • +
    • Open the *.po file of your language e.g. /frontend/src/locales/fr.po file with Poedit
    • +
    • In the Poedit menu click "Catalogue" --> "Update from POT File" --> select the translations.pot file from /frontend/src/locales
    • +
    • Now you can start proofreading and adding the missing translations
    • +
    • Once your done save the changes in the *.po file
    • +
    • Run npm run gettext-compile to compile existing translations into a single translations.json file
    • +
    • To test your translations you need to build the frontend again using npm run build or npm run watch
    • +
    +
    +

    A binary *.mo (machine object) file will be automatically saved along with every *.po file. +You won't be able to open those in a text editor, but please include them in git commits or when sending +translations via email. The compiled translations.json file is not required for pull requests +and often causes merge conflicts.

    +
    +

    Backend

    +

    Only asynchronous notifications and certain API responses need translation to provide a +consistent user experience. +Technical log messages should be in English to avoid ambiguities and (even slightly) wrong translations.

    +

    Localizations are kept in /assets/locales. The POT file, only containing message ids, is messages.pot.

    +

    default.po files in sub directories contain localized messages for each +language +identified by their locale, +for example de/default.po for German and pt_BR/default.po for Brazilian Portuguese. +You can open, edit and save them with Poedit. Please also add and commit binary *.mo files, +which will be automatically created by Poedit.

    +

    Add new translation

    +
      +
    • Run make generate to update /assets/locales/messages.pot
    • +
    • Open the /assets/locales/messages.pot file with Poedit
    • +
    • In Poedit click on "Create New Translation" at the bottom and select the language
    • +
    • Now you can start translating
    • +
    • When done, create a new directory (using the locale as name) and save your translation there as default.po
    • +
    +

    Update existing translation

    +
      +
    • Run make generate to update /assets/locales/messages.pot
    • +
    • Open the /assets/locales/fr/default.po file with Poedit
    • +
    • In the Poedit menu click "Catalogue" --> "Update from POT File" --> select the messages.pot file from /assets/locales/
    • +
    • Now you can start proofreading and adding the missing translations
    • +
    • Once you're done, save the changes in the default.po file
    • +
    +
    +

    This will only work when you have gettext installed on your system. We recommend using our latest development +image as described in the setup instructions.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/ui/browsers/index.html b/developer-guide/ui/browsers/index.html new file mode 100644 index 0000000000..7af7b67024 --- /dev/null +++ b/developer-guide/ui/browsers/index.html @@ -0,0 +1,7573 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Browsers - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Browsers

    + +

    We should at least support the latest Firefox and Chrome on Linux, OS X and Windows. Ideally also Safari (OS X) and Explorer/Edge (Windows).

    +

    Testing

    + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/ui/components/index.html b/developer-guide/ui/components/index.html new file mode 100644 index 0000000000..f5622ccf8e --- /dev/null +++ b/developer-guide/ui/components/index.html @@ -0,0 +1,7501 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Components - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Components

    + +

    Our frontend code is structured into reusable VueJS components. So far, it was clear enough for developers to add features and send pull requests, so it's probably not necessary to document individual components in our Developer Guide. Let us know via email or chat when you have any questions!

    +

    Frontend contains general information about the front-end libraries we use and how to trigger a build.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/ui/design/index.html b/developer-guide/ui/design/index.html new file mode 100644 index 0000000000..53e0664036 --- /dev/null +++ b/developer-guide/ui/design/index.html @@ -0,0 +1,7782 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Design & Colors - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Design & Colors

    + +

    We strive for a user interface that is clutter-free, well-organized, and doesn't behave in unexpected ways. It should work across a wide range of devices and be easy to use for everyone.

    +

    Theme Colors

    +

    Sample

    +

    The colors we use should be consistent and functional, for example, provide sufficient contrast. For the included themes, the preferred primary colors are violet and cyan, but other colors can be used as well.

    +

    To avoid distorting the visual impression of photos and videos, large background areas should generally be neutral or just slightly saturated.

    +

    Context Menu

    +

    The context menu at the bottom right should use a color spectrum for the individual actions to reflect the spectral colors of a prism:

    +

    context menu

    +

    Icon Fonts

    +

    We stick to Google's Material icons and use other icons only when absolutely necessary:

    + +

    Inspirational Quotes

    +
    +

    Design is a funny word. Some people think design means how it looks. But of course, if you dig deeper, it's really how it works.
    Steve Jobs

    +

    Choice is the enemy of productivity. Put another way, if your solution does everything, and has no opinions about anything, then it solves nothing.
    Asim Aslam

    +

    Any fool can make something complicated. It takes a genius to make it simple.
    Woody Guthrie

    +
    +

    External Resources

    +

    Color Schemes

    + +

    Web Color Tools

    + + + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/ui/img/editPhotoFiles.jpeg b/developer-guide/ui/img/editPhotoFiles.jpeg new file mode 100644 index 0000000000..69ac848e23 Binary files /dev/null and b/developer-guide/ui/img/editPhotoFiles.jpeg differ diff --git a/developer-guide/ui/img/files-details-new.png b/developer-guide/ui/img/files-details-new.png new file mode 100644 index 0000000000..94406dac62 Binary files /dev/null and b/developer-guide/ui/img/files-details-new.png differ diff --git a/developer-guide/ui/img/login-redesign.png b/developer-guide/ui/img/login-redesign.png new file mode 100644 index 0000000000..46cc76d4fe Binary files /dev/null and b/developer-guide/ui/img/login-redesign.png differ diff --git a/developer-guide/ui/img/new-mosaic-view.png b/developer-guide/ui/img/new-mosaic-view.png new file mode 100644 index 0000000000..4b6b20b724 Binary files /dev/null and b/developer-guide/ui/img/new-mosaic-view.png differ diff --git a/developer-guide/ui/img/violet.jpg b/developer-guide/ui/img/violet.jpg new file mode 100644 index 0000000000..272e785248 Binary files /dev/null and b/developer-guide/ui/img/violet.jpg differ diff --git a/developer-guide/ui/infinite-scrolling/index.html b/developer-guide/ui/infinite-scrolling/index.html new file mode 100644 index 0000000000..983acc058c --- /dev/null +++ b/developer-guide/ui/infinite-scrolling/index.html @@ -0,0 +1,7849 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Infinite Scrolling - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Infinite Scrolling

    + +

    There are two problems to solve when allowing the user to scroll through an "infinite" number of elements/photos:

    +
      +
    1. You can't load an infinite amount of data.
    2. +
    3. You can't render an infinite amount of elements.
    4. +
    +

    The solution to both is to not provide an infinite amount of elements, but instead create the illusion +of an infinite amount of elements by only loading and displaying what the user would actually currently be able to see.

    +

    Loading Data - Progressively

    +

    The problem of infinite data is solved by loading the actually required elements progressively, in fixed amounts (batches).

    +
      +
    • Load a batch of elements. If they don't fill the screen, load the next batch. repeat until the screen is filled.
    • +
    • When the user is getting close to the end of the currently loaded elements, load the next batch of data.
    • +
    +

    In an ideal world the "next batch" is always loaded fast and early enough so that the user doesn't even notice that it +wasn't there from the beginning. This creates the illusion of having an infinite amount of data available. +There are two parameters to consider:

    +
      +
    1. When to start loading the next batch?
        +
      • loading to early results in to much unnecessary data getting laoded.
      • +
      • loading to late results in the user bumping into the end of the list of elements, because the next batch hasn't finished loading yet.
      • +
      +
    2. +
    3. How large are the batches?
        +
      • to small batches can slow down the overall loading time by resulting in overhad because of too many requests.
      • +
      • to large batches need to long to load for a single batch, so that the data may not yet be ready when it is needed.
      • +
      +
    4. +
    +

    The best values for these parameters vary vastly depending on the current network speed, number of elements that fit on the screen and the speed the user is scrolling. Luckily these values don't need to be perfect, just good enough.

    +

    We currently use vue-infinite-scroll to detect when the user is about to reacht the end of the currently loaded list, so we can load the next batch. +The batchsize depends on the current view and is currently somewhere in the range of 50 - 300 elements.

    +

    Rendering elements - Virtualized

    +

    The further a user scrolls, the more batches of elements get loaded and displayed. +The more elements get displayed, the slower the browser gets and the more memory it needs.
    +This means, if we were to just render all loaded elements, the limit how far you can scroll +would be entirely determined by the available cpu and memory of the client.

    +

    Regular virtualization

    +

    This problem is usually solved by virtualization:

    +
      +
    1. determine the scrollposition and screensize of the client.
    2. +
    3. calculate what elements would be on the screen.
    4. +
    5. render only those elements.
    6. +
    +

    The problem with this regular virtualization is that it requires the elements to be positioned absolutely and may prescribe how they are structured. +Implementing it would therefore imply a potentialy larger rewrite and less freedom when designing the elements.

    +

    Pseudo-Virtualization with placeholders

    +

    Using the IntersectionObserver API we can efficiently determine wether something is currently visible or not. +We can use this information to replace all elements that are currently not in the visible area with simple placeholders of the same size. +This drastically reduces the load on the browser, because these (often single-domnode) placeholder-elements require a LOT less ressources.

    +

    This has the huge benefit that it doesn't restrict how components are structured or positioned, while also being easier to implement. +There are however two caveats:

    +
      +
    1. We are still rendering something for every single loaded elements
        +
      • The load on the browser therefore technically still increases (slightly) the more elements are loaded
      • +
      • This however reduces the load so much, that this slight increase per element barely matters at all
      • +
      +
    2. +
    3. The placeholders size should be as close to the originals size as possible.
        +
      • If the sizes don't match, scrolling might become a little janky.
      • +
      +
    4. +
    +

    This type of virtualization more than fast enough, and because we think the benefits outweigh the downsides, we decided go this route.

    +

    Implementation details

    +

    The setup for a component that uses the placeholder-virtualization is as follows:

    +
      +
    1. Add a ref to all the elements whose visibilty needs to be tracked
    2. +
    3. create a single IntersectionObserver in the beforeCreate that calls a (yet to be defined) this.visibilitiesChanged
    4. +
    5. add a watcher that is called when the list of elements changes. call observe on all refs from step 1
    6. +
    7. define a function that takes an IntersectionObserverEntry and returns the index of the corresponding target (for example by adding a data-index-attribute to the observed element)
    8. +
    9. add firstVisibleElementIndex: 0, lastVisibleElementIndex: 0 and visibleElementIndices: new Set() to the components state
    10. +
    11. conditionally render elements whose index is between firstVisibleElementIndex and lastVisibleElementIndex. Render placeholders for all other elements
    12. +
    13. define visibilitiesChanged. Let it call virtualizationTools.updateVisibleElementIndices. Use the result as new this.firstVisibleElementIndex and this.lastVisibileElementIndex
    14. +
    +

    We use the visibleElementIndices-Set to keep track of elements that became visible or invisible.
    +We also use firstVisibleElementIndex and lastVisibleElementIndex for two reasons:

    +
      +
    1. Vue doesn't react to Set-changes (because its identity never changes), so manipulating it doesn't cause a rerender
    2. +
    3. When scrolling very fast, the set may for a very brief moment contain holes (for example it has the indices 1, 2, 4, 5, 6). By implying that everything between the smallest and largest index is visible, these short-lived holes don't have any negative effect (index 3 would be rendered anyway)
    4. +
    +

    As a bonus, you can make the IntersectionObserver only observe for example every 5th element to speed up calculation of intersections. If you do so, you should add for Example -4 to firstVisibleElementIndex and +4 to lastVisibleElementIndex

    +

    Render Performance

    +

    When working with a huge amount of elements, render performance of these elements is critically important. +The better the render performance, the more it actually feels like scrolling through an infinite list. It allowes the user to scroll faster without having to see placeholders and makes the application feel way snappier, especially on lower-end devices.

    +

    Here are some tips on how to gain performance. They are ordered from most to least important and only apply to things that are rendered for every element:

    +
      +
    • Prefer regular HTML-Elements over vue-components
        +
      • Rendering vue components executes a lot of JavaScript, blocking everything else. Rendering regular HTML elements is way faster
      • +
      • Example: use <button> instead of <v-btn>
      • +
      +
    • +
    • Prefer conditional rendering over hiding/showing elements via css
        +
      • showing/hiding via css may prevent rerenders, but it increases the amount of rendered elements
      • +
      • The less elements (and therefore domnodes) are rendered the better
      • +
      +
    • +
    • Use less elements
        +
      • Why use a <v-card><v-img></v-img></v-card> when performance is important and a <div></div> with some css works too?
      • +
      +
    • +
    +

    Memoization

    +

    Memoization is a technique to speed up function calls by caching results. +This can have a noticable impact on render-performance, especially when function +results are used for placeholders

    +

    Example: The texts on the cards in the cards-view. There are function-calls like photo.locationInfo() and photo.getDateString().
    +The resulting values rarely change, but are calculated again and again on every render, resulting in ~280k calls per function when scrolling through ~2k pictures.

    +

    We use memoize-one for much called, non-trivial functions whose parameters rarely change. +The funtions in the Photo model are a prime example for that.

    +

    For this to work the memoized function must be pure, which means its result must not depend on outside factors, but only on its parameters. Calling the same function twice with the same parameters must always return the same result.

    +

    If you want to memoize a function that is not pure you can still do so by moving all its logic into a new, memoized, pure function and having the old funtion just call the memoized one, providing the required parameters. Example: +

    // ------------------ before ------------------
    +isPlayable() {
    +  if (this.Type === MediaAnimated) {
    +    return true;
    +  } else if (!this.Files) {
    +    return false;
    +  }
    +
    +  return this.Files.some((f) => f.Video);
    +}
    +
    +
    // ------------------ after ------------------
    +isPlayable() {
    +  return this.generateIsPlayable(this.Type, this.Files);
    +}
    +
    +generateIsPlayable = memoizeOne((type, files) => {
    +  if (type === MediaAnimated) {
    +    return true;
    +  } else if (!files) {
    +    return false;
    +  }
    +
    +  return files.some((f) => f.Video);
    +})
    +

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/ui/introduction/index.html b/developer-guide/ui/introduction/index.html new file mode 100644 index 0000000000..357203b5db --- /dev/null +++ b/developer-guide/ui/introduction/index.html @@ -0,0 +1,7683 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Introduction - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Introduction

    + +

    Open a terminal and type photoprism start to start the built-in server. It will listen on localhost:2342 by default, see docker-compose.yml and Configuration.

    +

    Frameworks

    +

    Vuetify is a powerful open-source Material Design UI component framework for building modern single-page applications.

    +

    It is based on VueJS, a JavaScript library that combines the best ideas from AngularJS (Google) and React (Facebook); development is community driven and the API fairly stable.

    +

    Vuetify and VueJS are initialized in frontend/src/app.js. Webpack is used as a module loader / bundler. It creates single, optimized JS and CSS files in the server assets public build directory from the original source code. You can find the build configuration in frontend/webpack.config.js.

    +

    For our docs and landing pages, we may use https://materializecss.com/ as a lightweight alternative to Vuetify.

    +

    Components

    +

    Components are reusable user-interface widgets. UI Components contains a list of custom components. Standard components like buttons or forms are well documented on vuetifyjs.com.

    +

    Dependencies

    +

    The full list of dependencies can be found in frontend/package.json. You need to run npm install in the frontend directory to install them (automatically happens during installation, see Makefile). Run npm install -P [package name] to add a new package (library or framework).

    +

    Building

    +

    A build can be triggered by running npm run watch (watches for changes and re-builds when needed) or npm run build (single build) in the frontend directory. NPM is the default package manager that comes with NodeJS, a JavaScript run-time environment that executes JavaScript code outside of a browser.

    +

    External Resources

    + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/ui/maps/index.html b/developer-guide/ui/maps/index.html new file mode 100644 index 0000000000..a28799f716 --- /dev/null +++ b/developer-guide/ui/maps/index.html @@ -0,0 +1,7586 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interactive Maps - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Rendering Interactive Maps in the UI

    +

    PhotoPrism includes four high-resolution world maps that allow you to browse photos by location. +Visit try.photoprism.app/library/places to try them on our demo.

    +

    Places UI Example

    +

    The API keys required to use these maps are unfortunately not free for us due to the number of users we have, see FAQ.

    +

    Mapbox/MapLibre GL JS

    +

    Because Mapbox GL JS is no longer open-source, +we now sponsor and use MapLibre GL JS +for rendering maps in the UI. MapLibre GL is a fork from the last Mapbox GL version available under a permissive +BSD license.

    +

    Statement by former Mapbox engineer Tom MacWright:

    +
    +

    OSS, we hoped, was about enabling people and unlocking people’s ability to collaborate. It turns out that in 2020, it’s mostly helping companies and getting nothing in return. That’s not a dynamic you can build a sustainable business on.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/developer-guide/ui/screenshots/index.html b/developer-guide/ui/screenshots/index.html new file mode 100644 index 0000000000..5db1b2a748 --- /dev/null +++ b/developer-guide/ui/screenshots/index.html @@ -0,0 +1,7951 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Screenshots - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    UI Development in Fast Motion ⏱

    +

    August 15 , 2018

    +

    The first prototype was based on Bootstrap 4.

    +

    +

    September 6, 2018

    +

    We switched to the Vuetify component framework, which comes complete with everything modern Web applications need.

    +

    +

    September 7, 2018

    +

    Navigation was extended.

    +

    +

    September 7, 2018

    +

    We started to experiment with the search form.

    +

    +

    September 8, 2018

    +

    Search dropdowns got populated with options.

    +

    +

    September 10, 2018

    +

    New logo and speed-dial action button.

    +

    +

    September 11, 2018

    +

    +

    September 19, 2018

    +

    Pastel colored buttons and photo selection.

    +

    +

    September 26, 2018

    +

    The detail view.

    +

    +

    July 3, 2019

    +

    Improved UI with flat look, photo upload and powerful filters as well as new pages for searching places and labels.

    +

    +

    +

    +

    January 24, 2020

    +

    +

    +

    +

    +

    January 26, 2020

    +

    +

    June 24, 2020

    +

    +

    January 21, 2021

    +

    +

    October 18, 2021

    +

    +

    March 2, 2022

    +

    +

    +

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/funding/index.html b/funding/index.html new file mode 100644 index 0000000000..994b2b6d2a --- /dev/null +++ b/funding/index.html @@ -0,0 +1,15 @@ + + + + + + Redirecting... + + + + + + +Redirecting... + + diff --git a/getting-started/advanced/backups/index.html b/getting-started/advanced/backups/index.html new file mode 100644 index 0000000000..0eebb96e2e --- /dev/null +++ b/getting-started/advanced/backups/index.html @@ -0,0 +1,7673 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Backup Guide - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Advanced Backup Guide

    +

    At a minimum, a backup of PhotoPrism should include the files in your originals folder and a copy of the index database. We also recommend backing up the storage folder so that you don't need to recreate any thumbnail or sidecar files, and your backup includes the complete configuration.

    +
    +

    The easiest way to create a full backup is to first run the backup command to generate a database dump as described in our Backup Guide. Then back up your originals and storage folders using any standard file backup utility.

    +
    +

    Scheduled Backups

    +

    By default, PhotoPrism 240523-923ee0cf7 and newer versions automatically create daily database backups for you, with up to 3 copies being retained. The schedule, the type of backups, and the number of backups to be retained can be changed in the configuration.

    +

    Backup Command

    +

    You can run the following command in a terminal to manually create a new MariaDB or SQLite database backup:

    +
    photoprism backup -i [filename]
    +
    +

    Or the following if you are using docker-compose:

    +
    docker compose exec -T photoprism photoprism backup -i - > photoprism-db.sql
    +
    +

    As seen above, you can use - as filename to write the backup to stdout. +This is done to ensure the backup resides outside of the container environment.

    +

    If you leave the filename empty, the backup will be written to the default backup folder configured via PHOTOPRISM_BACKUP_PATH.

    +

    If you want, you can also export your cache and thumbnails, but it can also be re-generated after restore. +It will save you from re-generating thumbnails from scratch however.

    +

    Helpful information can be found on GitHub as well.

    +

    Restore Command

    +

    See our regular Backup Guide to learn how to restore backups.

    +

    SQLite Backups

    +
    +

    If you are using a current version, you can create SQL dumps of SQLite with the photoprism backup command. Since the binary SQLite database files are located in the storage folder, they should also be automatically included in any backup.

    +
    +

    In order to create a dump directly with SQLite, you can alternatively run this command:

    +
    docker compose exec -T photoprism sqlite3 /photoprism/storage/index.db .dump > photoprism-db.sql
    +
    +

    With pure docker, you can run the following (or replace docker with podman on Red Hat-based Linux distributions):

    +
    docker exec -t PhotoPrism sqlite3 /photoprism/storage/index.db .dump > photoprism-db.sql
    +
    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/advanced/caching/index.html b/getting-started/advanced/caching/index.html new file mode 100644 index 0000000000..7cd61a680f --- /dev/null +++ b/getting-started/advanced/caching/index.html @@ -0,0 +1,7543 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cache Optimization - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Optimizing Cache Performance

    +

    While we believe this post may be helpful to advanced users, we have not yet reviewed it thoroughly. If you have suggestions for improvement, please let us know by clicking to send a pull request.

    +

    For advanced users only. This guide is maintained by the community and may contain inaccurate or incomplete advice. You can contribute by clicking to send a pull request with your changes.

    +

    Some users might want to place the thumbnail cache on a separate, faster file +system while keeping the actual photo files on large, slow bulk storage. This +should result in faster access to the thumbnails.

    +

    To do this, we add a further volume (-v) parameter to the docker script so we +use an external path (outside the container) for the cache files. You can get +the internal path with photoprism config, or as a docker command in a +running system (for Linux/BSD systems):

    +
    sudo docker exec photoprism photoprism config | grep cache-path
    +
    +

    This should return a line such as:

    +
    cache-path            /home/photoprism/.cache/photoprism
    +
    +

    for the internal path. We now know to add a line like

    +
      -v <MYCACHE_FOLDER>:/home/photoprism/.cache/photoprism \
    +
    +

    to the docker invocation, with your actual path to the cache folder replacing +<MYCACHE_FOLDER>.

    +

    As an example, let's assume a ZFS +filesystem with two pools ("volumes" in +classical terminology): A pool tank in a raidz2 (RAID6) configuration based on +hard drives that holds the original pictures, and a pool dozer in a mirrored +(RAID1) configuration based on SSD or NVMe drives to store the thumbnails. Our +docker script could be:

    +
    docker run -d \
    +  --name photoprism \
    +  -p 2342:2342 \
    +  -v /tank/photos/:/home/photoprism/Pictures/Originals \
    +  -v /dozer/cache/:/home/photoprism/.cache/photoprism \
    +  photoprism/photoprism:latest
    +
    +

    In a case like this, you will probably also want to optimize the datasets ("file +systems") tank/photos and dozer/cache further. For instance, the +original photo files will call for a larger recordsize than the smaller cache +files.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/advanced/databases/index.html b/getting-started/advanced/databases/index.html new file mode 100644 index 0000000000..99bf39d0c5 --- /dev/null +++ b/getting-started/advanced/databases/index.html @@ -0,0 +1,7716 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Database Setup - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Advanced Database Setup

    +

    While we believe this contributed content may be helpful to advanced users, we have not yet thoroughly reviewed it. If you have suggestions for improvement, please let us know by clicking to send a pull request.

    +

    Compatibility

    +

    PhotoPrism is compatible with SQLite 3 and MariaDB 10.5.12+. +Official support for MySQL 8 is discontinued as Oracle seems to have stopped shipping new features and enhancements. +As a result, the testing effort required before each release is no longer feasible.

    +

    Our configuration examples are generally based on the current stable version to take advantage of performance improvements. This does not mean that older versions are no longer supported and you must upgrade immediately. We recommend not using the :latest tag for the MariaDB Docker image and to upgrade manually by changing the tag once we had a chance to test a new major version.

    +

    Storage

    +

    Local Solid-State Drives (SSDs) are best for databases of any kind. Never store database files on an unreliable device such as a USB flash drive, SD card, or shared network folder. These may also have unexpected file size limitations, which is especially problematic for databases that do not split data into smaller files.

    +

    Please do not use a named or anonymous Docker volume for storing MariaDB database files and check the mount path of the volume if you use a custom database image (it may not always be /var/lib/mysql), as both can lead to data loss when the database container is recreated, e.g. after an update of the Docker image.

    +

    Configuration

    +

    When creating a new database, make sure to set the charset and collation as follows:

    +
    CREATE DATABASE photoprism
    +CHARACTER SET = 'utf8mb4'
    +COLLATE = 'utf8mb4_unicode_ci';
    +
    +

    Now create a user and grant privileges for this new database:

    +
    CREATE USER 'photoprism'@'%' IDENTIFIED BY 'insecure';
    +GRANT ALL PRIVILEGES ON photoprism.* to 'photoprism'@'%';
    +FLUSH PRIVILEGES;
    +
    +

    Set the database environment variables for PhotoPrism and MariaDB as follows:

    +
    services:
    +  photoprism:
    +    environment:
    +      PHOTOPRISM_DATABASE_DRIVER: "mysql"
    +      PHOTOPRISM_DATABASE_SERVER: "mariadb:3306"
    +      PHOTOPRISM_DATABASE_NAME: "photoprism"
    +      PHOTOPRISM_DATABASE_USER: "photoprism"
    +      PHOTOPRISM_DATABASE_PASSWORD: "insecure"
    +
    +  mariadb:
    +    environment:
    +      MARIADB_AUTO_UPGRADE: "1"
    +      MARIADB_INITDB_SKIP_TZINFO: "1"
    +      MARIADB_DATABASE: "photoprism"
    +      MARIADB_USER: "photoprism"
    +      MARIADB_PASSWORD: "insecure"
    +      MARIADB_ROOT_PASSWORD: "insecure"
    +
    +
    +

    Set strong passwords if the database is exposed to an external network. Never expose your database to the public Internet in this way, for example, if it is running on a cloud server.

    +
    +

    Schema Migrations

    +

    An index schema migration is performed automatically every time PhotoPrism is (re)started. The following instructions may be helpful in special cases, such as when a temporary problem has prevented a successful migration:

    +

    Run Migrations ›

    +

    Change Database

    +

    Migrate from SQLite to MariaDB ›

    +

    Migrate from MariaDB to SQLite ›

    +
    +

    Bad Performance

    +

    Many users reporting poor performance and high CPU usage have migrated from SQLite to MariaDB, so their database schema is no longer optimized for performance. For example, MariaDB cannot handle rows with text columns in memory and always uses temporary tables on disk if there are any.

    +

    If this is the case, please make sure that your migrated database schema matches that of a fresh, non-migrated installation. It may help to run the migrations manually in a terminal using the migrations subcommands. However, this does not guarantee that all issues such as missing indexes are resolved.

    +

    Due to the amount of time required to review each report, we can only offer this to eligible members and business customers, and not to users who have chosen our free community edition.

    +

    Get Performance Tips › View Database Schema ›

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/advanced/docker-security/index.html b/getting-started/advanced/docker-security/index.html new file mode 100644 index 0000000000..98d59af587 --- /dev/null +++ b/getting-started/advanced/docker-security/index.html @@ -0,0 +1,7718 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Docker Security - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Docker Security Guide

    +

    This documentation is intended for experienced users who want to enhance the security of their installation. If you have suggestions for improvement, please let us know by clicking to send a pull request.

    +

    Get the Latest Security Updates

    +

    Even though PhotoPrism is developed in Go and therefore does not use many of the C libraries installed in our Docker image, external file converters like Darktable and FFmpeg as well as other tools installed as dependencies might use them. They may also be directly affected by recently discovered vulnerabilities for which updates are available.

    +

    To automatically install these updates when the container starts for the first time, you can add PHOTOPRISM_INIT: "update" to the environment section of the photoprism service in your compose.yaml or docker-compose.yml:

    +
    services:
    +  photoprism:
    +    environment:
    +      PHOTOPRISM_INIT: "update"
    +      ...
    +    volumes:
    +      - ...
    +
    +

    This can be combined with other init actions such as https, gpu and/or tensorflow, e.g. PHOTOPRISM_INIT: "update https gpu tensorflow". For the changes to take effect, run the following to restart the services (--force-recreate will always recreate the containers to apply available updates, even if their configuration has not been changed):

    +
    docker compose stop
    +docker compose up -d --force-recreate
    +
    +

    We also recommend making sure that the latest Docker version and security updates are automatically installed on your host operating system.

    +
    +

    Note that our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    +
    +

    Run Services as Non-Root User

    +

    It is recommended that you run the photoprism service as a non-root user by setting either the user service property or the PHOTOPRISM_UID and PHOTOPRISM_GID environment variable in your compose.yaml or docker-compose.yml file:

    + + + + + + + + + + + + + + + + + + + + +
    EnvironmentDefaultDescription
    PHOTOPRISM_UID0run as a non-root user after initialization (supported: 0, 33, 50-99, 500-600, 900-1250, and 2000-2100)
    PHOTOPRISM_GID0run with a specific group id after initialization, can optionally be used together with PHOTOPRISM_UID (supported: 0, 33, 44, 50-99, 105, 109, 115, 116, 500-600, 900-1250, and 2000-2100)
    +

    If you are using hardware video transcoding, it should depend on the owner of the video device which user and group you choose so that the service has permission to access it.

    +

    Finally, remember to update the file permissions and/or owner with the chmod and chown commands when you make changes to the UID or GID, and restart the services for your changes to take effect:

    +
    docker compose stop
    +docker compose up -d
    +
    +

    Remove Passwords From the Environment

    +

    Passwords specified directly in a docker-compose.yml file or otherwise passed to the container environment may pose a security risk. As an alternative, they can be set in an options.yml file located in the config storage folder:

    +
    AdminPassword: "my super secret password"
    +DatabasePassword: "my super secret password"
    +
    +

    Likewise, MariaDB can be configured to use Docker secret files. For details, see the Docker Compose Documentation.

    +

    The following is an example of the changes to your compose.yaml or docker-compose.yml file. Note that this example includes only the additional lines required to pass secret files to the MariaDB container:

    +
    secrets:
    +  # Secrets are single-line text files where the sole
    +  # content is the secret. Paths in this example assume
    +  # that secrets are kept in local ".secrets" folder. 
    +  DB_ROOT_PWD:
    +    file: .secrets/db_root_pwd.txt
    +  DB_PWD:
    +    file: .secrets/db_pwd.txt
    +
    +services:
    +  mariadb:
    +    environment:
    +      # Change the env variables to _FILE and point them to
    +      # the file locations within the container.
    +      MARIADB_PASSWORD_FILE: /run/secrets/DB_PWD
    +      MARIADB_ROOT_PASSWORD_FILE: /run/secrets/DB_ROOT_PWD
    +    secrets:
    +      # Give the container access to the secrets to mount
    +      # the files within the container.
    +      - DB_ROOT_PWD
    +      - DB_PWD
    +
    +

    Rootless Docker

    +

    In addition, you can run the Docker daemon as a non-root user in rootless mode. Configuring this is beyond the scope of this guide. For more information and instructions, see the Docker Security Documentation.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/advanced/docker-volumes/index.html b/getting-started/advanced/docker-volumes/index.html new file mode 100644 index 0000000000..6ccaed6177 --- /dev/null +++ b/getting-started/advanced/docker-volumes/index.html @@ -0,0 +1,7717 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Docker Volumes - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Docker Volume Mounts

    +

    When using Docker, all application services run in isolated containers, so you must explicitly mount the host folders you want to use. Be aware that PhotoPrism and MariaDB cannot see folders that have not been mounted. This is an important security feature.

    +
    +

    It is important that the originals, storage, and database folders are located on persistent volumes. We recommend changing the relative paths used in our examples to absolute paths and to avoid using named or anonymous Docker volumes to prevent potential data loss when the container is recreated, e.g. after an update of the Docker image.

    +
    +

    Originals Folder

    +

    The originals folder contains your original photo and video files.

    +

    ~/Pictures will be mounted by default, where ~ is a shortcut for your home directory:

    +
    volumes:
    +  # "/host/folder:/photoprism/folder"  # example
    +  - "~/Pictures:/photoprism/originals"
    +
    +

    You can mount any folder accessible from the host, including network shares. Additional directories can also be mounted as sub folders of /photoprism/originals (depending on overlay file system support):

    +
    volumes:
    +  - "/home/username/Pictures:/photoprism/originals"
    +  - "/example/friends:/photoprism/originals/friends"
    +  - "/mnt/photos:/photoprism/originals/media"
    +
    +

    On Windows, prefix the host path with the drive letter and use / instead of \ as separator:

    +
    volumes:
    +  - "D:/Example/Pictures:/photoprism/originals"
    +
    +
    +

    If read-only mode is enabled, all features that require write permission to the originals folder are disabled, e.g. WebDAV, uploading and deleting files. Set PHOTOPRISM_READONLY to "true" in docker-compose.yml for this. You can mount a folder with the :ro flag to make Docker block write operations as well.

    +
    +

    Storage Folder

    +

    SQLite, config, cache, backup, thumbnail and sidecar files are saved in the storage folder:

    +
      +
    • a storage folder mount must always be configured in your compose.yaml or docker-compose.yml file so that you do not lose these files after a restart or upgrade
    • +
    • never configure the storage folder to be inside the originals folder unless the name starts with a . to indicate that it is hidden
    • +
    • we recommend placing the storage folder on a local SSD drive for best performance
    • +
    • mounting symbolic links or using them inside the storage folder is currently not supported
    • +
    • avoid using a named or anonymous Docker volume for permanently storing files, as this can lead to data loss when the container is recreated, e.g. after an update of the Docker image
    • +
    +
    +

    Should you later want to move your instance to another host, the easiest and most time-saving way is to copy the entire storage folder along with your originals and database.

    +
    +

    Import Folder

    +

    You can optionally mount an import folder from which files can be transferred to the originals folder in a structured way that avoids duplicates:

    +
      +
    • imported files receive a canonical filename and will be organized by year and month
    • +
    • never configure the import folder to be inside the originals folder, as this will cause a loop by importing already indexed files
    • +
    +
    +

    You can safely skip this. Adding files via Web Upload and WebDAV remains possible, unless read-only mode is enabled or the features have been disabled.

    +
    +

    MariaDB Database

    +

    Our example includes a pre-configured MariaDB database server that stores it files in the database folder by default. If you remove it and provide no other database server credentials, SQLite database files will be created in the storage folder.

    +

    Please do not use a named or anonymous Docker volume for storing MariaDB database files and check the mount path of the volume if you use a custom database image (it may not always be /var/lib/mysql), as both can lead to data loss when the database container is recreated, e.g. after an update of the Docker image.

    +
    +

    Never store database files on an unreliable device such as a USB flash drive, SD card, or shared network folder. These may also have unexpected file size limitations, which is especially problematic for databases that do not split data into smaller files. We strongly recommend using SSD storage for databases only.

    +
    +

    Network Storage

    +

    Shared folders that have already been mounted on your host under a drive letter or path can be used with Docker containers like any other directory. +In addition, certain types of network storage like NFS (Unix/Linux) and CIFS (Windows/Mac) can also be mounted directly with Docker Compose.

    +

    For more information, see the Network Storage section of our Docker Troubleshooting Guide.

    +

    Configure Network Storage ›

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/advanced/https/index.html b/getting-started/advanced/https/index.html new file mode 100644 index 0000000000..1fbdff8e36 --- /dev/null +++ b/getting-started/advanced/https/index.html @@ -0,0 +1,15 @@ + + + + + + Redirecting... + + + + + + +Redirecting... + + diff --git a/getting-started/advanced/kubernetes/index.html b/getting-started/advanced/kubernetes/index.html new file mode 100644 index 0000000000..363ae48185 --- /dev/null +++ b/getting-started/advanced/kubernetes/index.html @@ -0,0 +1,7649 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Using Kubernetes - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Running PhotoPrism with Kubernetes

    +

    While we believe this contributed content may be helpful to advanced users, we have not yet thoroughly reviewed it. If you have suggestions for improvement, please let us know by clicking to submit a change request.

    +

    At a minimum, you can just define a Kubernetes Service and a StatefulSet and be up and running. For more real-world usage, you'll probably want to at least include persistent storage, and possibly some Ingress rules for exposing PhotoPrism outside your cluster.

    +

    Before you proceed, please ensure that your server has at least 4 GB of swap configured and avoid setting a hard memory limit as this can cause unexpected restarts when the indexer temporarily needs more memory to process large files. Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    +

    For those familiar with Helm, a PhotoPrism Helm chart is available.

    +

    Once you've got PhotoPrism deployed, you can exec into the running container and photoprism import your photos.

    +

    Here's an example of a YAML file that creates the following Kubernetes objects:

    +
      +
    • Namespace
    • +
    • Service exposing PhotoPrism on port 80
    • +
    • StatefulSet with persistent NFS volumes
    • +
    • Secret which stores the database DSN and admin password
    • +
    • Ingress rule for a Kubernetes ingress controller
    • +
    • Annotations for a Kubernetes Certificate Manager
    • +
    +
    apiVersion: v1
    +kind: Namespace
    +metadata:
    +  name: photoprism
    +---
    +apiVersion: v1
    +kind: Secret
    +metadata:
    +  name: photoprism-secrets
    +  namespace: photoprism
    +stringData:
    +  PHOTOPRISM_ADMIN_PASSWORD: <your admin password here>
    +  PHOTOPRISM_DATABASE_DSN: username:password@tcp(db-server-address:3306)/dbname?charset=utf8mb4,utf8&parseTime=true
    +---
    +apiVersion: apps/v1
    +kind: StatefulSet
    +metadata:
    +  name: photoprism
    +  namespace: photoprism
    +spec:
    +  selector:
    +    matchLabels:
    +      app: photoprism
    +  serviceName: photoprism
    +  replicas: 1
    +  template:
    +    metadata:
    +      labels:
    +        app: photoprism
    +    spec:
    +      containers:
    +      - name: photoprism
    +        image: photoprism/photoprism:latest
    +        env:
    +        - name: PHOTOPRISM_DEBUG
    +          value: "true"
    +        - name: PHOTOPRISM_DATABASE_DRIVER
    +          value: mysql
    +        - name: PHOTOPRISM_HTTP_HOST
    +          value: 0.0.0.0
    +        - name: PHOTOPRISM_HTTP_PORT
    +          value: "2342"
    +        # Load database DSN & admin password from secret
    +        envFrom:
    +        - secretRef:
    +            name: photoprism-secrets
    +            optional: false
    +        ports:
    +        - containerPort: 2342
    +          name: http
    +        volumeMounts:
    +        - mountPath: /photoprism/originals
    +          name: originals
    +        - mountPath: /photoprism/import
    +          name: import
    +        - mountPath: /photoprism/storage
    +          name: storage
    +        readinessProbe:
    +          httpGet:
    +            path: /api/v1/status
    +            port: http
    +      volumes:
    +      - name: originals
    +        nfs:
    +          path: /originals
    +          # readOnly: true # Disables import and upload!
    +          server: my.nas.host
    +      - name: import
    +        nfs:
    +          path: /import
    +          server: my.nas.host
    +      - name: storage
    +        nfs:
    +          path: /storage
    +          server: my.nas.host
    +---
    +apiVersion: v1
    +kind: Service
    +metadata:
    +  name: photoprism
    +  namespace: photoprism
    +spec:
    +  ports:
    +  - name: http
    +    port: 80
    +    protocol: TCP
    +    targetPort: http
    +  selector:
    +    app: photoprism
    +  type: ClusterIP
    +---
    +apiVersion: networking.k8s.io/v1
    +kind: Ingress
    +metadata:
    +  annotations:
    +    # For nginx ingress controller:
    +    kubernetes.io/ingress.class: nginx
    +    # Default is very low so most photo uploads will fail:
    +    nginx.ingress.kubernetes.io/proxy-body-size: "512M"
    +    # If using cert-manager:
    +    cert-manager.io/cluster-issuer: letsencrypt-prod
    +    kubernetes.io/tls-acme: "true"
    +  name: photoprism
    +  namespace: photoprism
    +spec:
    +  rules:
    +  - host: photoprism.my.domain
    +    http:
    +      paths:
    +      - backend:
    +          service:
    +            name: photoprism
    +            port:
    +              name: http
    +        path: /
    +        pathType: Prefix
    +  tls:
    +  - hosts:
    +    - photoprism.my.domain
    +    secretName: photoprism-cert
    +
    +

    To run this locally, you can use minikube +or a similar local cluster deployer.

    +

    Once your cluster is up and running with your kubectl commands. Simply copy the above YAML +markup to a file, make the necessary changes, and use the kubectl CLI command to deploy:

    +
    kubectl create -f photoprism.yaml
    +
    +

    If you prefer to use helm, see p80n/photoprism-helm.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/advanced/migrations/index.html b/getting-started/advanced/migrations/index.html new file mode 100644 index 0000000000..03c7bdd0f9 --- /dev/null +++ b/getting-started/advanced/migrations/index.html @@ -0,0 +1,7663 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index Schema - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Performing Index Migrations

    +
    +

    When using Docker Compose, you can prepend commands like docker compose exec [service] [command] to run them in a service container. +Should this fail with no container found, make sure the service has been started, you have specified an existing service (usually photoprism) and you are in the folder where your compose.yaml or docker-compose.yml file is located.

    +
    +

    Show Migration Status

    +

    Run the photoprism migrations ls command in a terminal to see a list of all migrations and their current status:

    +
    $ photoprism migrations ls
    +
    +|-----------------|---------|---------------------|---------------------|--------|
    +|       ID        | Dialect |     Started At      |     Finished At     | Status |
    +|-----------------|---------|---------------------|---------------------|--------|
    +| 20211121-094727 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20211124-120008 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-030000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-040000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-050000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-060000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-061000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-070000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-071000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-080000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-081000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-083000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-090000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-091000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220329-093000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220421-200000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220521-000001 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220521-000002 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +| 20220521-000003 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |
    +|-----------------|---------|---------------------|---------------------|--------|
    +
    +

    Run Specific Migrations

    +

    To explicitly re-run specific migrations, you can pass them as arguments to the photoprism migrations run command:

    +
    $ photoprism migrations run 20220521-000003
    +
    +INFO[2022-07-12T11:45:29Z] migrate: 20220521-000003 successful [12.967654ms] 
    +INFO[2022-07-12T11:45:29Z] migration completed in 40.89123ms            
    +INFO[2022-07-12T11:45:29Z] closed database connection 
    +
    +

    Retry Failed Migrations

    +

    To automatically retry previously failed migrations, pass the -f flag to the photoprism migrations run command:

    +
    $ photoprism migrations run -f
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/advanced/migrations/mariadb-to-sqlite/index.html b/getting-started/advanced/migrations/mariadb-to-sqlite/index.html new file mode 100644 index 0000000000..b894d23806 --- /dev/null +++ b/getting-started/advanced/migrations/mariadb-to-sqlite/index.html @@ -0,0 +1,7527 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MariaDB to SQLite - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Migrating from MariaDB to SQLite

    +

    While we believe this contributed content may be helpful to advanced users, we have not yet thoroughly reviewed it. If you have suggestions for improvement, please let us know by clicking to submit a change request.

    +
      +
    • Install techouse/mysql-to-sqlite3 on your host.
    • +
    • Stop Photoprism: docker compose stop photoprism
    • +
    • Add the port to the MariaDB service
    • +
    • On the host now run sudo mysql2sqlite -f <PATH_TO_STORAGE_MOUNT>/storage/index.db -d photoprism -u root --mysql-password 'insecure'
    • +
    • Shutdown your current stack fully: docker compose down
    • +
    • Edit your compose.yaml or docker-compose.yml:
    • +
    • Remove the MariaDB service.
    • +
    • Change in the Photoprism settings to use the sqlite driver and remove the other database settings
    • +
    • Start your stack again with docker compose up -d
    • +
    • If this worked you may want to delete the old mountpoint for the MariaDB database.
    • +
    +
    +

    Bad Performance

    +

    Many users reporting poor performance and high CPU usage have migrated, so their database schema is no longer optimized for performance, for example, because indexes are missing or columns have the wrong data type.

    +

    If this is the case, please make sure that your migrated database schema matches that of a fresh, non-migrated installation. It may help to run the migrations manually in a terminal using the migrations subcommands. However, this does not guarantee that all issues such as missing indexes are resolved.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/advanced/migrations/sqlite-to-mariadb/index.html b/getting-started/advanced/migrations/sqlite-to-mariadb/index.html new file mode 100644 index 0000000000..fee7093405 --- /dev/null +++ b/getting-started/advanced/migrations/sqlite-to-mariadb/index.html @@ -0,0 +1,7555 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SQLite to MariaDB - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Migrating from SQLite to MariaDB

    +

    While we believe this contributed content may be helpful to advanced users, we have not yet thoroughly reviewed it. If you have suggestions for improvement, please let us know by clicking to submit a change request.

    +
      +
    • Install techouse/sqlite3-to-mysql on your host. (openSUSE: zypper in python-sqlite3-to-mysql)
    • +
    • Shutdown your current stack: docker compose down
    • +
    • Add the current snippet of the MariaDB to your Sqlite Photoprism docker compose with the addition of the extra ports + section where you expose port 3306 to the Host. In my case this looked like attachment 1.
    • +
    • Start the stack again: docker compose up -d
    • +
    • Stop Photoprism: docker compose stop photoprism
    • +
    • On the host now run sudo sqlite3mysql -f <PATH_TO_STORAGE_MOUNT>/storage/index.db -d photoprism -u root -p and enter the MariaDB password when prompted (default is insecure).
    • +
    • Shutdown your current stack again: docker compose down
    • +
    • Edit your compose.yaml or docker-compose.yml so it uses the MariaDB database you added before. Don't forget to remove the ports + section of the MariaDB Container.
    • +
    • Start your stack again with docker compose up -d
    • +
    • If this worked you may want to delete the file index.db in the storage mount since it contains out of date + information.
    • +
    +

    Attachment 1:

    +
    services:
    +  mariadb:
    +    restart: unless-stopped
    +    image: mariadb:11
    +    ports:
    +      - 3306:3306 # Expose Port 3306 
    +    security_opt:
    +      - seccomp:unconfined
    +      - apparmor:unconfined
    +    command: --innodb-buffer-pool-size=1G >
    +      --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4
    +      --collation-server=utf8mb4_unicode_ci --max-connections=512
    +      --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
    +    volumes:
    +      - "./database:/var/lib/mysql" # DO NOT REMOVE
    +    environment:
    +      MARIADB_AUTO_UPGRADE: "1"
    +      MARIADB_INITDB_SKIP_TZINFO: "1"
    +      MARIADB_DATABASE: "photoprism"
    +      MARIADB_USER: "photoprism"
    +      MARIADB_PASSWORD: "insecure"
    +      MARIADB_ROOT_PASSWORD: "insecure"
    +
    +
    +

    Bad Performance

    +

    Many users reporting poor performance and high CPU usage have migrated from SQLite to MariaDB, so their database schema is no longer optimized for performance. For example, MariaDB cannot handle rows with text columns in memory and always uses temporary tables on disk if there are any.

    +

    If this is the case, please make sure that your migrated database schema matches that of a fresh, non-migrated installation. It may help to run the migrations manually in a terminal using the migrations subcommands. However, this does not guarantee that all issues such as missing indexing are resolved.

    +

    View Database Schema ›

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/advanced/nginx-proxy-setup/index.html b/getting-started/advanced/nginx-proxy-setup/index.html new file mode 100644 index 0000000000..0f733874ee --- /dev/null +++ b/getting-started/advanced/nginx-proxy-setup/index.html @@ -0,0 +1,7882 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NGINX Proxy Setup - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Advanced NGINX Proxy Setup

    +

    While we believe this contributed content may be helpful to advanced users, we have not yet thoroughly reviewed it. If you have suggestions for improvement, please let us know by clicking to submit a change request.

    +
    +

    Getting Support

    +

    Since NGINX is notoriously difficult to configure, we are unable to provide technical support for NGINX-related issues such as failed uploads, connection errors, broken thumbnails, and video playback problems. If you cannot resolve these on your own, we recommend that you ask their community for advice or use Traefik instead, which is easier to configure and more convenient to handle overall.

    +
    +

    Using a reverse proxy in front of PhotoPrism has various benefits:

    +
      +
    • Make use of HTTP/2
    • +
    • Add encryption
    • +
    • Perform traffic optimization
    • +
    • Enhance security (NGINX may block dangerous request patterns the embedded Go-based HTTP server does not know about)
    • +
    +

    If you consider exposing your PhotoPrism instance to the evil internet, you should at least secure it with a proper HTTPS encryption.

    +

    This guide aims for a little more advanced setup, with good security in mind.

    +

    Trough the entire guide we use photoprism.example.com as domain. You have to change all these occurrences with your own domain / DynDNS.

    +

    Also, this was all tested on ubuntu/20.04 - But the commands should work on a lot of other versions as well. +If you use CentOS, SuSE, or any other distro, look up the commands for your package manager.

    +

    Setup

    +

    Domain setup

    +

    First, we need to decide what we want to use as domain / address. We will use a proper domain in this guide, but it will technically also work with just a static IP address.

    +

    If you're lucky enough to have your own domain, just create a subdomain and use this for PhotoPrism.

    +

    If you want to expose your instance hosted at home, just use a DynDNS provider (just duckduckgo it, there a multiple ones that are free).

    +
    +

    Tip

    +

    if you're new to DynDNS it might be good to know that most routers do support DynDNS. +Therefore only minimal setup is required. +Just search for your router name + DynDNS ;)

    +
    +

    Install NGINX on your machine

    +

    If not already done, install the webserver on your machine which you wish to use as proxy. +This also can be the same host that is running the docker container. +

    apt-get update && apt-get install -y nginx apache2-utils
    +

    +
    +

    Info

    +

    We use apache2-utils later for the password generation of the optional extra security with http basic auth.

    +
    +

    Acquire a certificate

    +

    Install cerbot

    +

    Luckily nowadays there is a free certificate authority called Let's Encrypt. +You can just use their certbot tool and acquire a new certificate in seconds.

    +

    For Ubuntu / Debian the package is directly in the repository. +Therefore, you can easily install it using this command:

    +
    apt-get update && apt-get install -y certbot python3-certbot-nginx
    +
    +

    Generate the certificate

    +

    Now, just request a new certificate by using this command:

    +
    certbot -d photoprism.example.com
    +
    +

    After that you should have a new certificate in: /etc/letsencrypt/live/photoprism.example.com/

    +
    +

    Note

    +

    In order for Let's Encrypt to work, the server has to be accessible from the internet on port 443

    +
    +
    +

    Tip

    +

    Please refer to Let's Encrypts documentation for automated certificate renewal. The certificates are valid for around 90 days.

    +
    +

    Setup NGINX

    +

    Now that we have our certificate its time to setup the NGINX itself. +Since it is best practice to create a configuration file per application / domain, this is exactly what we gonna do.

    +

    Create a new file /etc/nginx/sites-available/photoprism.example.com and put the following content in it. +Keep in mind to change the domain (there a multiple entries!) +

    # PhotoPrism Nginx config with SSL HTTP/2 and reverse proxy
    +# This file gives you an example on how to secure you PP instance with SSL
    +server {
    +    listen       80;
    +    listen [::]:80; # HTTP IPv6
    +    # enforce https
    +    return 301 https://$server_name$request_uri;
    +}
    +server {
    +
    +    listen 443 ssl http2; # Listen on port 443 and enable ssl and HTTP/2
    +    listen [::]:443 ssl http2; # Same for IPv6
    +
    +    # Put your domain name in here.
    +    server_name  photoprism.example.com;
    +
    +    # - - - - - - - - - -
    +    # SSL security
    +    # - - - - - - - - - -
    +    ssl_certificate          /etc/letsencrypt/live/photoprism.example.com/fullchain.pem;
    +    ssl_certificate_key      /etc/letsencrypt/live/photoprism.example.com/privkey.pem;
    +
    +    # Since the PP API is also used on Android, we have to keep TLS1.2 in here for a while.
    +    # A lot of the older Android devices do not support TLS1.3 yet :/
    +    ssl_protocols            TLSv1.2 TLSv1.3;
    +
    +    # Use good and strong ciphers, disable weak and old ciphers
    +    ssl_ciphers              HIGH:!RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS;
    +
    +    # Enable HSTS (https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security)
    +    add_header Strict-Transport-Security "max-age=172800; includeSubdomains";
    +
    +    # This checks if the certificate has been invalidated by the certificate authority
    +    # You can remove this section if you use self-singed certificates...
    +    # Enable OCSP stapling (http://blog.mozilla.org/security/2013/07/29/ocsp-stapling-in-firefox)
    +    ssl_stapling on;
    +    ssl_stapling_verify on;
    +    ssl_trusted_certificate /etc/letsencrypt/live/photoprism.example.com/fullchain.pem;
    +
    +    # DNS Servers to use for OCSP lookups
    +    resolver 8.8.8.8 1.1.1.1 9.9.9.9 valid=300s;
    +    resolver_timeout 5s;
    +
    +    # - - - - - - - - -
    +    # Reverse Proxy
    +    # - - - - - - - - -
    +    proxy_redirect           off;
    +    proxy_set_header         X-Real-IP $remote_addr;                        # Let PP know the clients real IP
    +    proxy_set_header         X-Forwarded-For $proxy_add_x_forwarded_for;    # Let PP know that a proxy did forward this request
    +    proxy_set_header         Host $http_host;                               # Set Proxy host info
    +
    +    proxy_http_version 1.1;                                                 # Required for WebSocket connection
    +    proxy_set_header Upgrade $http_upgrade;                                 # Allow protocol switch to websocket
    +    proxy_set_header Connection "upgrade";                                  # Do protocol switch
    +    proxy_set_header X-Forwarded-Proto $scheme;                             # Let PP know that this connection used HTTP or HTTPS
    +
    +    client_max_body_size 500M;                                              # Bump the max body size, you may want to upload huge stuff via the upload GUI
    +    proxy_buffering off;                                                    # Do not hold back the request while the client sends data, give the stream directly to PP
    +
    +    location / {
    +            # Optional; additional protection with Basic Auth.
    +            # Note: This breaks WebDAV without additional configuration
    +            #       You also have to create a .htpasswd file using the command:
    +            #       "htpasswd -c /etc/nginx/.pp_htpasswd my_secret_user"
    +            # - - -
    +            # auth_basic           "PhotoPrism Pre Auth";
    +            # auth_basic_user_file /etc/nginx/.pp_htpasswd;
    +
    +            # pipes the traffic to PhotoPrism
    +            # Change this to your PhotoPrisms IP / DNS
    +            proxy_pass http://docker.homenet:2342;
    +    }
    +}
    +

    +

    Have a look at the individual comments in the configuration for a further description.

    +
    +

    Tip

    +

    Don't forget to change the PhotoPrism IP / DNS on the bottom of the config... ;)

    +
    +

    Once you've changed everything you need, let's restart nginx:

    +

    sudo nginx -t
    +sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
    +systemctl reload nginx
    +
    +Now, you should have access to PhotoPrism.

    +
    +

    Warning

    +

    Make sure to setup proper firewalling on the machine that is running PhotoPrism. +Have a look at ufw for simple firewall rules.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/advanced/openid-connect/index.html b/getting-started/advanced/openid-connect/index.html new file mode 100644 index 0000000000..1ac7eca7b6 --- /dev/null +++ b/getting-started/advanced/openid-connect/index.html @@ -0,0 +1,7973 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OpenID Connect - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Single Sign-On via OpenID Connect

    +

    OpenID Connect (OIDC) allows users to log in and optionally register through an external identity provider instead of manually entering a username and password:

    +

    oidc-login

    +

    Config Options

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_OIDC_URI--oidc-uriissuer URI for single sign-on via OpenID Connect, e.g. https://accounts.google.com
    PHOTOPRISM_OIDC_CLIENT--oidc-clientclient ID for single sign-on via OpenID Connect
    PHOTOPRISM_OIDC_SECRET--oidc-secretclient SECRET for single sign-on via OpenID Connect
    PHOTOPRISM_OIDC_PROVIDER--oidc-providercustom identity provider NAME, e.g. Google
    PHOTOPRISM_OIDC_ICON--oidc-iconcustom identity provider icon URI
    PHOTOPRISM_OIDC_REDIRECT--oidc-redirectautomatically redirect unauthenticated users to the configured identity provider
    PHOTOPRISM_OIDC_REGISTER--oidc-registerallow new users to create an account when they sign in with OpenID Connect
    PHOTOPRISM_OIDC_USERNAME--oidc-usernamepreferred_usernamepreferred username CLAIM for new OpenID Connect users (preferred_username, name, nickname, email)
    PHOTOPRISM_OIDC_WEBDAV--oidc-webdavallow new OpenID Connect users to use WebDAV when they have a role that allows it
    PHOTOPRISM_DISABLE_OIDC--disable-oidcdisable single sign-on via OpenID Connect, even if an identity provider has been configured
    +
    +

    Your PhotoPrism instance and the OpenID Connect Identity Provider (IdP) must be accessible via HTTPS and have valid TLS certificates configured for it. Please also make sure that the hostname in the Redirect URL configured on the IdP matches the Site URL used by PhotoPrism. Single sign-on via OIDC can otherwise not be enabled.

    +
    +

    Identity Providers

    +

    To authenticate users via OIDC, you can either set up and use a self-hosted identity provider such as ZITADEL or Keycloak, or configure a public identity provider service such as those operated by Google, Microsoft, GitHub, or Amazon.

    +

    Single sign-on can be configured automatically if the identity provider offers a standardized /.well-known/openid-configuration endpoint for service discovery, for example:

    + +

    Issuer URI

    +

    The Issuer URI in your configuration must match the issuer value returned by the /.well-known/openid-configuration endpoint of your OpenID Connect Identity Provider (IdP), for example https://accounts.google.com if you use Google for authentication.

    +
    +

    You may not modify the URI in any way, e.g. by adding or omitting slashes at the end. If the values do not match, the validation will fail and users cannot be redirected to your provider's login page. For security reasons, only a generic error message is displayed in this case.

    +
    +

    Redirect URL

    +

    The Redirect URL that must be specified when registering a new client with an Identity Provider is as follows, where {hostname} must be replaced by the hostname in the Site URL, e.g. configured via PHOTOPRISM_SITE_URL:

    +
    https://{hostname}/api/v1/oidc/redirect
    +
    +
    +

    Note that both the Site URL configured for your instance and the Redirect URL must start with https:// and that their hostnames must match, as the use of secure connections is a strict requirement for OpenID Connect.

    +
    +

    Preferred Username

    +

    When a new user signs in with OpenID Connect1, their preferred username may already be registered. In this case, a random 6-digit number is appended to resolve the conflict.

    +

    The config option PHOTOPRISM_OIDC_USERNAME allows you to change the preferred username for new accounts from preferred_username to name, nickname, or verified email. Names are changed to lowercase handles so that, for example, "Jens Mander" becomes "jens.mander".

    +

    Existing Accounts

    +

    Super admins can manually connect existing user accounts2 under Settings > Users by changing the authentication to OIDC and then setting the Subject ID to match the account identifier from the configured Identity Provider:

    +

    Edit Dialog

    +

    The Edit Account dialog may additionally contain a text field for the Issuer URL. It does not need to be entered manually as it is set automatically after the first login.

    +

    Alternatively, you can run the following command in a terminal to allow authentication via OIDC and set a Subject ID to connect existing accounts:

    +
    photoprism users mod --auth=oidc --auth-id=[sub] [username]
    +
    +

    Learn more ›

    +

    Passwords

    +

    Changing the authentication of an account to OIDC does not remove a previously set password, so that it can still be used to log in (optionally also in combination with 2FA).

    +

    If a local password has been set for an account, you can remove it by running the following command in a terminal:

    +
    photoprism passwd --rm [username]
    +
    +

    Super admins can alternatively set the account password to a long random value through the Admin Web UI or CLI to effectively prevent local authentication.

    +

    Learn more ›

    +

    Deleting Accounts

    +

    Deleted accounts remain linked to the Subject ID, so logging in via OIDC is no longer possible and no new account can be registered for the same Subject ID either.

    +

    If you wish to change the connected user account or create a new account instead, you must therefore change the authentication of the old account e.g. to None before deleting it:

    +
    photoprism users mod --auth=none [username]
    +
    +

    To restore a previously deleted account, admins can follow the same steps as for creating a new account with the same username through the Admin Web UI or the photoprism users add command. You will then be asked if you want to restore the account.

    +

    Learn more ›

    +

    Frequently Asked Questions

    +

    Is it possible to set a default role for new OIDC users?

    +

    For security reasons, our Personal Editions currently default to the Guest role, which admins can then upgrade after checking the eligibility of newly registered accounts. If you run our Pro Edition in a trusted corporate network with appropriate security measures - including for the OIDC provider - it can be configured to give new accounts a higher authorization level by default.

    +

    Please note in this context that using an external Identity Provider for authorization, and not just for authentication, can easily lead to security issues such as the following, for which we do not want to get a CVE assigned nor do we want to be responsible for any private pictures of our users getting leaked as a result:

    + +

    Can I configure a custom claim for the preferred username?

    +

    You can choose between preferred_username, name, nickname and email, where preferred_username is the default. The other options are used as a fallback if no value is returned for the configured claim.

    +

    Note that it is not possible to use a non-standard claim such as username, as this could lead to conflicts and potential security issues, e.g. if the value is not unique or not reliably set.

    +

    Learn more ›

    +
    +
    +
      +
    1. +

      PHOTOPRISM_OIDC_REGISTER must be set to "true" to allow new users to create an account 

      +
    2. +
    3. +

      Admins cannot change the authentication of their own user account through the Admin Web UI so that they do not accidentally lock themselves out e.g. by setting it to None

      +
    4. +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/advanced/scalability/index.html b/getting-started/advanced/scalability/index.html new file mode 100644 index 0000000000..590c6f4f0d --- /dev/null +++ b/getting-started/advanced/scalability/index.html @@ -0,0 +1,7510 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Horizontal Scaling - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Deployment in High-Availability Environments and Horizontal Scaling in the Cloud

    +

    Since we strive to make effective use of our resources, and most users run PhotoPrism on NAS devices at home or on small cloud server instances, our public documentation and development activities generally focus on these usage scenarios.

    +

    So while our freely available Community Edition works well even with millions of files if your server meets the requirements (vertical scaling), it is important to note that the architecture and feature set would look different in some aspects – with other trade-offs – had the focus been on high availability and horizontal scaling in the cloud instead. For example, you might then split the backend into independently scalable microservices and not use a regular file system as storage for indexing.

    +

    As an enterprise customer with specific scalability or availability requirements, you are welcome to contact us for a free consultation to determine the feasibility and implementation options.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/advanced/transcoding/index.html b/getting-started/advanced/transcoding/index.html new file mode 100644 index 0000000000..e2fd4c1aee --- /dev/null +++ b/getting-started/advanced/transcoding/index.html @@ -0,0 +1,8087 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Video Transcoding - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Video Transcoding

    +

    AVC Encoders

    +

    The encoder used by FFmpeg can be configured with PHOTOPRISM_FFMPEG_ENCODER in your compose.yaml or docker-compose.yml config file:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EncoderValue
    Software H.264software
    Apple Video Toolboxapple
    Intel Quick Syncintel
    NVIDIA H.264nvidia
    Raspberry Pi / Video4Linuxraspberry
    Video Acceleration APIvaapi
    +

    It defaults to software if no value is set or hardware transcoding fails. Please refer to the FFmpeg documentation for a full list of encoders and their implementation status. We welcome contributions to support additional encoders.

    +
    +

    For video transcoding to work, FFmpeg must be enabled and installed. When using our Docker images, it is already pre-installed. In addition, the service must have permission to use the related video devices. This depends on your hardware and operating system, so we can only give you examples that may need to be changed to work for you.

    +
    +

    Size Limit

    +

    The PHOTOPRISM_FFMPEG_SIZE config option allows to limit the resolution of transcoded videos. It accepts the following standard sizes, while other values are automatically adjusted to the next supported size:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SizeUsage
    720SD TV, Mobile
    1280HD TV, SXGA
    1920Full HD
    2048DCI 2K, Tablets
    2560Quad HD, Notebooks
    38404K Ultra HD
    4096DCI 4K, Retina 4K
    76808K Ultra HD 2
    +
    +

    When transcoding videos, the original aspect ratio is maintained and smaller videos will not be upscaled.

    +
    +
    +

    Note that MPEG-4 AVC videos are not re-encoded if they exceed the configured resolution limit.

    +
    +

    Bitrate Limit

    +

    You can limit the bitrate of the AVC encoder with the config option PHOTOPRISM_FFMPEG_BITRATE. Keep in mind that this is a "soft limit", so the actual bitrate varies and depends on the encoder used as well as the specific FFmpeg parameters, which in turn depend on the encoder. It may also depend on the operating system and the GPU drivers.

    +

    If the bitrate is significantly exceeded in your environment and you want improvements to be implemented, we recommend that you take a look at the FFmpeg documentation and the parameters in our source code so you can tell us which parameters should be changed to make it work for you.

    +

    Note that MPEG-4 AVC videos are not re-encoded if they exceed the configured bitrate limit. To reduce the size of AVC videos, you can manually replace the original files with a smaller version or wait for a future release that offers this functionality.

    +
    +

    Already transcoded video files are not automatically re-transcoded when the limit is changed. To do this, you must manually remove the *.avc files in the sidecar storage folder and run the photoprism convert command in a terminal.

    +
    +

    Software Transcoding

    +

    Unless you have a lot of high-resolution videos in your library, we recommend keeping the default settings to use the standard software codec for video transcoding. It has a high quality and does not require any special permissions or additional drivers.

    +

    Since FFmpeg 7 has significant performance optimizations compared to version 6.1.1 that ships with Ubuntu 24.04, users of our Docker image can choose to install the latest FFmpeg build available at johnvansickle.com/ffmpeg by adding PHOTOPRISM_INIT: "ffmpeg" to the environment section of their compose.yaml or docker-compose.yml file:

    +
    services:
    +  photoprism:
    +    environment:
    +      PHOTOPRISM_INIT: "ffmpeg"
    +
    +

    Note that this version cannot be used with hardware transcoding and that it may support a different set of file formats.

    +

    GPU Drivers

    +

    Depending on your hardware, it may be necessary to install additional packages for FFmpeg to use the AVC encoding device.

    +

    One way to do this automatically is to set PHOTOPRISM_INIT to "gpu tensorflow" when using our Docker images. Note that this is experimental and not required for most encoders.

    +

    See the related installation script on GitHub for details. We welcome contributions to support additional devices or update package names if needed.

    +
    +

    Most users can either skip PHOTOPRISM_INIT completely or just use PHOTOPRISM_INIT: "tensorflow" to install a special version of TensorFlow that improves indexing performance if the server CPU supports AVX, which is independent of video transcoding and the type of GPU.

    +
    +

    Intel Quick Sync

    +

    To enable Intel Quick Sync hardware video transcoding, add the intel target to PHOTOPRISM_INIT, choose the intel encoder, and share the /dev/dri devices with the photoprism service:

    +
    services:
    +  photoprism:
    +    environment:
    +      PHOTOPRISM_FFMPEG_ENCODER: "intel"
    +      PHOTOPRISM_INIT: "intel"
    +      ...
    +    devices:
    +      - "/dev/dri:/dev/dri"
    +    volumes:
    +      - ...
    +
    +

    In addition, you can choose to run the photoprism service as a non-root user by setting either the user service property or the PHOTOPRISM_UID and PHOTOPRISM_GID environment variables in your compose.yaml or docker-compose.yml file:

    + + + + + + + + + + + + + + + + + + + + +
    EnvironmentDefaultDescription
    PHOTOPRISM_UID0run as a non-root user after initialization (supported: 0, 33, 50-99, 500-600, 900-1250, and 2000-2100)
    PHOTOPRISM_GID0run with a specific group id after initialization, can optionally be used together with PHOTOPRISM_UID (supported: 0, 33, 44, 50-99, 105, 109, 115, 116, 500-600, 900-1250, and 2000-2100)
    +

    Which user and group you choose should depend on the owner of the /dev/dri video device so that the service has permission to access it.

    +

    Finally, remember to update the file permissions and/or owner with the chmod and chown commands when you make changes to the UID or GID, and restart the services for your changes to take effect:

    +
    docker compose stop
    +docker compose up -d
    +
    +
    +

    Older Intel hardware may not support certain video codecs and resolutions. In this case, it is not possible to use hardware transcoding for these videos. We may later add a configuration option that allows you to downscale videos.

    +
    +

    NVIDIA Container Toolkit

    +

    For hardware transcoding with an NVIDIA graphics card, the NVIDIA Container Toolkit must be installed on the host computer first. Instructions can be found in their installation guide.

    +

    Once the toolkit is installed, choose the nvidia encoder and add a deploy section to the photoprism service:

    +
    services:
    +  photoprism:    
    +    environment:
    +      PHOTOPRISM_FFMPEG_ENCODER: "nvidia"
    +      PHOTOPRISM_INIT: "tensorflow"
    +      NVIDIA_VISIBLE_DEVICES: "all"
    +      NVIDIA_DRIVER_CAPABILITIES: "compute,video,utility"
    +      ...
    +    volumes:
    +      - ...
    +    deploy:
    +      resources:
    +        reservations:
    +          devices:
    +            - driver: "nvidia"
    +              count: 1
    +              capabilities: [gpu]
    +    ...
    +
    +

    Now restart the services for your changes to take effect:

    +
    docker compose stop
    +docker compose up -d
    +
    +
    +

    We also provide a ready-to-use docker-compose.yml example for your convenience. +Note that older hardware may not support certain video codecs and resolutions.

    +
    +

    Raspberry Pi

    +

    Experimental hardware-accelerated transcoding on a Raspberry Pi (and compatible devices) can be enabled by choosing the raspberry encoder:

    +
    PHOTOPRISM_FFMPEG_ENCODER: "raspberry"
    +
    +

    The Docker container must also have access to one or more video devices. For the raspberry encoder, for example, you add:

    +
    devices:
    + - "/dev/video11:/dev/video11"
    +
    +

    Additional advanced configuration options are available to improve stability if needed:

    +
    PHOTOPRISM_FFMPEG_BUFFERS: "64" # FFmpeg capture buffers (default: 32)
    +
    +

    Now restart the services for your changes to take effect:

    +
    docker compose stop
    +docker compose up -d
    +
    +
    +

    Some server configurations, especially Raspberry Pi's, may experience memory allocation issues when using hardware acceleration. Carefully monitor your server's logs and increase the available GPU and/or CMA memory allocations if necessary. Note that the Raspberry Pi hardware currently only supports video resolutions up to 2160p.

    +
    +

    Other Hardware

    +

    If you want to use other hardware for transcoding, choose the appropriate AVC encoder and share the required devices with the photoprism service, as shown in the examples above. Then restart the services for the changes to take effect.

    +

    Which devices need to be shared and whether additional drivers are required depends on your specific hardware. For more information, see the FFmpeg documentation.

    +

    Troubleshooting

    +

    Enabling Trace Log Mode

    +

    A good way to troubleshoot configuration issues is to increase the log level. To enable trace log mode, set PHOTOPRISM_LOG_LEVEL to "trace" in the environment: section of the photoprism service (or use the --trace flag when running the photoprism command directly):

    +
    services:
    +  photoprism:
    +    environment:
    +      PHOTOPRISM_LOG_LEVEL: "trace"
    +      ...
    +
    +

    Then restart all services for your changes to take effect:

    +
    docker compose stop
    +docker compose up -d
    +
    +

    Viewing Docker Service Logs

    +

    You can run this command to check the server logs for warnings and errors, including the last 100 messages (omit --tail=100 to see them all, and -f to output only the last logs without watching them):

    +
    docker compose logs -f --tail=100 
    +
    +

    Learn more ›

    +
    +

    If FFmpeg is disabled or not installed, videos cannot be indexed because still images cannot be created. +You should also have ExifTool enabled to extract metadata such as duration, resolution, and codec. +Note that your hardware may not support certain video codecs and resolutions. In this case, the software encoder is used automatically.

    +
    +
    +

    Our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    +
    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/cloud/digitalocean/index.html b/getting-started/cloud/digitalocean/index.html new file mode 100644 index 0000000000..d5d70f4c51 --- /dev/null +++ b/getting-started/cloud/digitalocean/index.html @@ -0,0 +1,7871 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DigitalOcean - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Using our DigitalOcean 1-Click App

    +

    PhotoPrism can be deployed at DigitalOcean with just a few clicks. +If you have no DigitalOcean account yet, you may use this sign-up link to receive a $100, 60-day account credit:

    +

    +Sign up at DigitalOcean +

    + +

    Install PhotoPrism

    + +

    Screenshot

    +

    Configure Your Droplet

    +

    Choose an Image

    +

    The PhotoPrism image will be pre-selected

    +

    Screenshot

    +

    Choose a Plan

    +

    We recommend hosting PhotoPrism on a server with at least 2 cores and 3 GB of physical memory. Indexing and searching can be slow on smaller Droplets, depending on how many and what types of files you upload.

    +
    +

    While PhotoPrism has been reported to work on Droplets with less memory, we take no responsibility for instability or performance problems. RAW image conversion and TensorFlow are disabled on Droplets with 1 GB or less memory.

    +
    +

    Screenshot

    +

    Choose a Datacenter Region

    +

    Screenshot

    +

    Screenshot

    +

    Choose an Authentication Mode

    +

    Screenshot

    +

    Finalize Your Droplet

    +

    Finalize your droplet and click Create Droplet +Screenshot

    +

    Screenshot

    +

    Your droplet is now being created.

    +

    Admin Password

    +
      +
    • Click More
    • +
    +

    Screenshot

    +
      +
    • Click Access console
    • +
    +

    Screenshot

    +
      +
    • Launch the console as root
    • +
    +

    Screenshot

    +
      +
    • Within the console type cat /root/.initial-password.txt and click enter
    • +
    • Copy your initial password
    • +
    +

    Open PhotoPrism

    +
      +
    • Click Get started
    • +
    +

    Screenshot

    +
      +
    • Click Quick access
    • +
    +

    Screenshot

    +
    +

    Info

    +

    In case you have no domain and let's encrypt set up you will see the notice "Your connection is not private". +Click Advanced and click Open page.

    +
    +
      +
    • Use username "admin" and your initial password to sign in
    • +
    • You may change your password using the Web UI
    • +
    +

    First Steps

    +
      +
    1. Configure library and advanced settings according to your needs.
    2. +
    3. Choose whether you want to index your originals so that the existing file and folder names are preserved, or import them so that they are automatically organized by year and month.
    4. +
    5. To add new pictures, you can either copy them to the import or originals folder e.g. using WebDAV, or upload them with a browser, which will import them automatically after upload.
    6. +
    7. Start indexing or importing
    8. +
    9. Finally, set up automatic syncing from your mobile phone and install the Progressive Web App (PWA) on your desktop, tablet, and phone home screens as needed.
    10. +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/cloud/img/1-do-setup.png b/getting-started/cloud/img/1-do-setup.png new file mode 100644 index 0000000000..abc239fcd0 Binary files /dev/null and b/getting-started/cloud/img/1-do-setup.png differ diff --git a/getting-started/cloud/img/2-do-setup.png b/getting-started/cloud/img/2-do-setup.png new file mode 100644 index 0000000000..98f54d7162 Binary files /dev/null and b/getting-started/cloud/img/2-do-setup.png differ diff --git a/getting-started/cloud/img/3-do-setup.png b/getting-started/cloud/img/3-do-setup.png new file mode 100644 index 0000000000..e84a9b38f6 Binary files /dev/null and b/getting-started/cloud/img/3-do-setup.png differ diff --git a/getting-started/cloud/img/4-do-setup.png b/getting-started/cloud/img/4-do-setup.png new file mode 100644 index 0000000000..866fb6b0b6 Binary files /dev/null and b/getting-started/cloud/img/4-do-setup.png differ diff --git a/getting-started/cloud/img/5-do-setup.png b/getting-started/cloud/img/5-do-setup.png new file mode 100644 index 0000000000..bbb1e26848 Binary files /dev/null and b/getting-started/cloud/img/5-do-setup.png differ diff --git a/getting-started/cloud/img/6-do-setup-edited.png b/getting-started/cloud/img/6-do-setup-edited.png new file mode 100644 index 0000000000..2992bfe2be Binary files /dev/null and b/getting-started/cloud/img/6-do-setup-edited.png differ diff --git a/getting-started/cloud/img/7-do-setup.png b/getting-started/cloud/img/7-do-setup.png new file mode 100644 index 0000000000..0ce780aa7e Binary files /dev/null and b/getting-started/cloud/img/7-do-setup.png differ diff --git a/getting-started/cloud/img/create-photoprism-droplet.png b/getting-started/cloud/img/create-photoprism-droplet.png new file mode 100644 index 0000000000..b876ef9b5b Binary files /dev/null and b/getting-started/cloud/img/create-photoprism-droplet.png differ diff --git a/getting-started/cloud/img/do-access-console-edited.png b/getting-started/cloud/img/do-access-console-edited.png new file mode 100644 index 0000000000..9fd7adb888 Binary files /dev/null and b/getting-started/cloud/img/do-access-console-edited.png differ diff --git a/getting-started/cloud/img/do-get-started-edited.png b/getting-started/cloud/img/do-get-started-edited.png new file mode 100644 index 0000000000..fac184cc55 Binary files /dev/null and b/getting-started/cloud/img/do-get-started-edited.png differ diff --git a/getting-started/cloud/img/do-launch-droplet-console.png b/getting-started/cloud/img/do-launch-droplet-console.png new file mode 100644 index 0000000000..d62fe0a753 Binary files /dev/null and b/getting-started/cloud/img/do-launch-droplet-console.png differ diff --git a/getting-started/cloud/img/do-more-options-edited.png b/getting-started/cloud/img/do-more-options-edited.png new file mode 100644 index 0000000000..1637d93bd3 Binary files /dev/null and b/getting-started/cloud/img/do-more-options-edited.png differ diff --git a/getting-started/cloud/img/do-quick-access.png b/getting-started/cloud/img/do-quick-access.png new file mode 100644 index 0000000000..9a6c24b970 Binary files /dev/null and b/getting-started/cloud/img/do-quick-access.png differ diff --git a/getting-started/cloud/img/pikapods-appstore.png b/getting-started/cloud/img/pikapods-appstore.png new file mode 100644 index 0000000000..dd059beeef Binary files /dev/null and b/getting-started/cloud/img/pikapods-appstore.png differ diff --git a/getting-started/cloud/img/pikapods-overview.png b/getting-started/cloud/img/pikapods-overview.png new file mode 100644 index 0000000000..c23351e27e Binary files /dev/null and b/getting-started/cloud/img/pikapods-overview.png differ diff --git a/getting-started/cloud/img/pikapods-step-1.png b/getting-started/cloud/img/pikapods-step-1.png new file mode 100644 index 0000000000..52628fab1b Binary files /dev/null and b/getting-started/cloud/img/pikapods-step-1.png differ diff --git a/getting-started/cloud/img/pikapods-step-2.png b/getting-started/cloud/img/pikapods-step-2.png new file mode 100644 index 0000000000..24ec3c1bfb Binary files /dev/null and b/getting-started/cloud/img/pikapods-step-2.png differ diff --git a/getting-started/cloud/img/pikapods-step-3.png b/getting-started/cloud/img/pikapods-step-3.png new file mode 100644 index 0000000000..3bee01d037 Binary files /dev/null and b/getting-started/cloud/img/pikapods-step-3.png differ diff --git a/getting-started/cloud/pikapods/index.html b/getting-started/cloud/pikapods/index.html new file mode 100644 index 0000000000..eea74991d4 --- /dev/null +++ b/getting-started/cloud/pikapods/index.html @@ -0,0 +1,7688 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PikaPods - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    PikaPods Open Source App Hosting

    +
    +

    Trusted Partner

    +

    PikaPods has partnered with us to offer you this officially supported, cloud-hosted solution.1 Your personal app instance is ready to use in just a few steps and includes member features like premium themes and high-resolution world maps - at no additional cost! New customers also receive a $5 welcome credit.

    +
    +

    Setup

    +

    This step-by-step guide explains how to set up a new PhotoPrism instance at PikaPods.

    +

    1. Create Account

    +

    Sign up at www.pikapods.com/register with your contact details. +You will then receive a confirmation email with an activation link that you must click to continue.

    +

    Before proceeding, we recommend that you enter your credit card information first to avoid usage restrictions.

    +

    2. Start PhotoPrism

    +

    If it isn't already selected, go to Available Apps and select PhotoPrism, then click Run Your Own:

    +

    Screenshot

    +

    Continue by entering a Pod Name and selecting a Region:

    +

    Screenshot

    +

    Click ENV VARS to specify the initial password for the "admin" user account:

    +

    Screenshot

    +

    In RESOURCES, you can configure the storage space available for photos and videos, as well as the compute resources PhotoPrism can use, such as for indexing and face recognition.

    +
    +

    PhotoPrism currently requires at least 2 CPUs and 8 GB of memory. We are working to lower these minimum requirements.

    +
    +

    The approximate price per month is shown at the bottom:

    +

    Screenshot

    +

    Finally, click Add Pod to complete the setup and start your instance.

    +

    3. Add Your Files

    +

    PhotoPrism is now fully set up and ready to use. To log in, click Open Pod, enter the username "admin" and the password you have specified:

    +

    Screenshot

    +

    If you want to change your password, you can do so in Settings > Account. +Our First Steps 👣 tutorial will guide you through the user interface and settings to ensure your library is indexed according to your individual preferences.

    +
    +
    +
      +
    1. +

      A share of the revenue helps fund the development of PhotoPrism 

      +
    2. +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/config-files/defaults/index.html b/getting-started/config-files/defaults/index.html new file mode 100644 index 0000000000..a904fa0e20 --- /dev/null +++ b/getting-started/config-files/defaults/index.html @@ -0,0 +1,7663 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + defaults.yml - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    defaults.yml

    +

    Global config defaults, including the config and storage paths to be used, can be defined with a defaults.yml file in the /etc/photoprism directory (requires root privileges). +It is possible to use the environment variable PHOTOPRISM_DEFAULTS_YAML or the command flag --defaults-yaml to load the defaults from another file instead.

    +

    Keep in mind that any changes to the config options, either through the UI, config files, or by setting environment variables, always require a restart to take effect.

    +
    +

    A defaults.yml file affects all users and should only contain options for which you want to set a global default.

    +
    +

    File Format

    +

    You can use any text editor to create or modify YAML config files. When specifying values, make sure that their data type matches the documentation, e.g. bool values must be either true or false (without quotes, unlike in docker-compose.yml files) and int values must be whole numbers, as shown in this example:

    +
    ConfigPath: "~/.config/photoprism"
    +StoragePath: "~/.photoprism"
    +OriginalsPath: "~/Pictures"
    +ImportPath: "/media"
    +AdminUser: "admin"
    +AdminPassword: "insecure"
    +AuthMode: "password"
    +DatabaseDriver: "sqlite"
    +HttpHost: "127.0.0.1"
    +HttpPort: 2342
    +HttpCompression: "gzip"
    +DisableTLS: false
    +DefaultTLS: true
    +Experimental: false
    +DisableWebDAV: false
    +DisableSettings: false
    +DisableTensorFlow: false
    +DisableFaces: false
    +DisableClassification: false
    +DisableVectors: false
    +DisableRaw: false
    +RawPresets: false
    +JpegQuality: 85
    +DetectNSFW: false
    +UploadNSFW: true
    +
    +

    To avoid ambiguity, it is recommended to enclose text strings in " (double quotes), especially if they contain spaces, a colon, or other special characters.

    +
    +

    File and directory paths may be specified using ~ as a placeholder for the home directory of the current user, e.g. ~/Pictures. Relative paths can also be specified via ./pathname. +If no explicit originals, import and/or assets path has been configured, a list of default directory paths will be searched and the first existing directory will be used for the respective path.

    +
    +

    Supported Options

    +

    ↪ see options.yml

    +

    Overriding Defaults

    +

    Defaults can be overridden by values in an options.yml file as well as by command flags and environment variables.

    +

    To override values with an options.yml file, you can specify its path (without the file name) by adding the ConfigPath option to your defaults.yml. Alternatively, you can use the command flag --config-path or the environment variable PHOTOPRISM_CONFIG_PATH. By default, it is located in the config subdirectory of the storage path.

    +

    The values in an options.yml file are not global by default and can be used to customize individual instances. +In both files you can set any of the supported options.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/config-files/index.html b/getting-started/config-files/index.html new file mode 100644 index 0000000000..26371714ba --- /dev/null +++ b/getting-started/config-files/index.html @@ -0,0 +1,9007 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + options.yml - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    options.yml

    +

    As an alternative to configuring your instance with environment variables, it may be more +convenient to use an options.yml file located in your config path, for example if PhotoPrism was +installed through an app store or with +the installation packages we provide.

    +

    You can specify a custom config path by adding the ConfigPath option to a ↪ defaults.yml file in +the /etc/photoprism directory (requires root privileges). It is also possible to use the command flag --config-path +or the environment variable PHOTOPRISM_CONFIG_PATH for this. By default, it is a subdirectory of the storage +path.

    +

    If you use a third-party integration or package, you should find the exact location of the configuration files in the +corresponding documentation.

    +
    +

    Note that changes to the options.yml file require a restart to take effect and +that config values changed through the web interface will also be saved to this +file. We therefore recommend that you only edit it manually while your instance is stopped.

    +
    +

    File Format

    +

    You can use any text editor to create or modify YAML config files. When +specifying values, make sure that their data type matches the documentation, e.g. bool +values must be either true or false (without quotes, +unlike in docker-compose.yml files) and int values must be +whole numbers, as shown in this example:

    +
    Debug: false
    +AdminUser: "admin"
    +AdminPassword: "insecure"
    +DatabaseUser: "photoprism"
    +DatabasePassword: "insecure"
    +DatabaseName: "photoprism"
    +DatabaseDriver: "mysql"
    +DatabaseServer: "localhost:3306"
    +HttpPort: 2342
    +SiteCaption: "AI-Powered Photos App"
    +SiteDescription: ""
    +SiteAuthor: ""
    +SiteUrl: "http://localhost:2342/"
    +
    +

    To avoid ambiguity, it is recommended to enclose text strings in " (double quotes), especially if they contain spaces, +a colon, or other special characters.

    +
    +

    File and directory paths may be specified using ~ as a placeholder for the home directory of the current user, e.g. ~/Pictures. Relative paths can also be specified via ./pathname. +If no explicit originals, import and/or assets path has been configured, a list of default directory paths will be searched and the first existing directory will be used for the respective path.

    +
    +

    Global Defaults

    +

    Global configuration defaults, including the storage paths to be used, can optionally be specified in a +↪ defaults.yml file.

    +

    Current Values

    +

    Run photoprism --help in a terminal to get +an overview of the command flags and environment variables available for configuration. Their +current values can be displayed with the photoprism config command.

    +

    Below are the corresponding names of the config options that you can use in the options.yml +and defaults.yml files, grouped by purpose.

    +

    Config Options

    +

    Authentication

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeCLI Flag
    AuthModestring--auth-mode
    Publicbool--public
    AdminUserstring--admin-user
    AdminPasswordstring--admin-password
    PasswordLengthint--password-length
    PasswordResetUristring--password-reset-uri
    OIDCUristring--oidc-uri
    OIDCClientstring--oidc-client
    OIDCSecretstring--oidc-secret
    OIDCScopesstring--oidc-scopes
    OIDCProviderstring--oidc-provider
    OIDCIconstring--oidc-icon
    OIDCRedirectbool--oidc-redirect
    OIDCRegisterbool--oidc-register
    OIDCUsernamestring--oidc-username
    OIDCWebDAVbool--oidc-webdav
    DisableOIDCbool--disable-oidc
    SessionMaxAgeint64--session-maxage
    SessionTimeoutint64--session-timeout
    SessionCacheint64--session-cache
    +

    Logging

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeCLI Flag
    LogLevelstring--log-level
    Prodbool--prod
    Debugbool--debug
    Tracebool--trace
    +

    Storage

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeCLI Flag
    ConfigPathstring--config-path
    OriginalsPathstring--originals-path
    OriginalsLimitint--originals-limit
    ResolutionLimitint--resolution-limit
    UsersPathstring--users-path
    StoragePathstring--storage-path
    ImportPathstring--import-path
    ImportDeststring--import-dest
    CachePathstring--cache-path
    TempPathstring--temp-path
    AssetsPathstring--assets-path
    +

    Sidecar Files

    + + + + + + + + + + + + + + + + + + + + +
    NameTypeCLI Flag
    SidecarPathstring--sidecar-path
    SidecarYamlbool--sidecar-yaml
    +

    Backup

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeCLI Flag
    BackupPathstring--backup-path
    BackupSchedulestring--backup-schedule
    BackupRetainint--backup-retain
    BackupDatabasebool--backup-database
    BackupAlbumsbool--backup-albums
    +

    Indexing

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeCLI Flag
    IndexWorkersint--index-workers
    IndexSchedulestring--index-schedule
    WakeupIntervaltime.Duration--wakeup-interval
    AutoIndexint--auto-index
    AutoImportint--auto-import
    +

    Feature Flags

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeCLI Flag
    ReadOnlybool--read-only
    Experimentalbool--experimental
    DisableSettingsbool--disable-settings
    DisableBackupsbool--disable-backups
    DisableRestartbool--disable-restart
    DisableWebDAVbool--disable-webdav
    DisablePlacesbool--disable-places
    DisableTensorFlowbool--disable-tensorflow
    DisableFacesbool--disable-faces
    DisableClassificationbool--disable-classification
    DisableFFmpegbool--disable-ffmpeg
    DisableExifToolbool--disable-exiftool
    DisableVipsbool--disable-vips
    DisableSipsbool--disable-sips
    DisableDarktablebool--disable-darktable
    DisableRawTherapeebool--disable-rawtherapee
    DisableImageMagickbool--disable-imagemagick
    DisableHeifConvertbool--disable-heifconvert
    DisableVectorsbool--disable-vectors
    DisableJpegXLbool--disable-jpegxl
    DisableRawbool--disable-raw
    RawPresetsbool--raw-presets
    ExifBruteForcebool--exif-bruteforce
    DetectNSFWbool--detect-nsfw
    UploadNSFWbool--upload-nsfw
    DefaultLocalestring--default-locale
    DefaultTimezonestring--default-timezone
    +

    Customization

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeCLI Flag
    DefaultThemestring--default-theme
    AppNamestring--app-name
    AppModestring--app-mode
    AppIconstring--app-icon
    AppColorstring--app-color
    LegalInfostring--legal-info
    LegalUrlstring--legal-url
    WallpaperUristring--wallpaper-uri
    +

    Site Information

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeCLI Flag
    SiteUrlstring--site-url
    SiteAuthorstring--site-author
    SiteTitlestring--site-title
    SiteCaptionstring--site-caption
    SiteDescriptionstring--site-description
    SitePreviewstring--site-preview
    CdnUrlstring--cdn-url
    CdnVideobool--cdn-video
    CORSOriginstring--cors-origin
    CORSHeadersstring--cors-headers
    CORSMethodsstring--cors-methods
    +

    Web Server

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeCLI Flag
    HttpsProxystring--https-proxy
    HttpsProxyInsecurebool--https-proxy-insecure
    TrustedProxies[]string--trusted-proxy
    ProxyProtoHeaders[]string--proxy-proto-header
    ProxyProtoHttps[]string--proxy-proto-https
    DisableTLSbool--disable-tls
    DefaultTLSbool--default-tls
    TLSEmailstring--tls-email
    TLSCertstring--tls-cert
    TLSKeystring--tls-key
    HttpModestring--http-mode
    HttpCompressionstring--http-compression
    HttpCachePublicbool--http-cache-public
    HttpCacheMaxAgeint--http-cache-maxage
    HttpVideoMaxAgeint--http-video-maxage
    HttpHoststring--http-host
    HttpPortint--http-port
    +

    Database Connection

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeCLI Flag
    DatabaseDriverstring--database-driver
    DatabaseDsnstring--database-dsn
    DatabaseNamestring--database-name
    DatabaseServerstring--database-server
    DatabaseUserstring--database-user
    DatabasePasswordstring--database-password
    DatabaseTimeoutint--database-timeout
    DatabaseConnsint--database-conns
    DatabaseConnsIdleint--database-conns-idle
    +

    File Conversion

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeCLI Flag
    FFmpegBinstring--ffmpeg-bin
    FFmpegEncoderstring--ffmpeg-encoder
    FFmpegSizeint--ffmpeg-size
    FFmpegBitrateint--ffmpeg-bitrate
    FFmpegMapVideostring--ffmpeg-map-video
    FFmpegMapAudiostring--ffmpeg-map-audio
    ExifToolBinstring--exiftool-bin
    SipsBinstring--sips-bin
    SipsExcludestring--sips-exclude
    DarktableBinstring--darktable-bin
    DarktableCachePathstring--darktable-cache-path
    DarktableConfigPathstring--darktable-config-path
    DarktableExcludestring--darktable-exclude
    RawTherapeeBinstring--rawtherapee-bin
    RawTherapeeExcludestring--rawtherapee-exclude
    ImageMagickBinstring--imagemagick-bin
    ImageMagickExcludestring--imagemagick-exclude
    HeifConvertBinstring--heifconvert-bin
    RsvgConvertBinstring--rsvgconvert-bin
    +

    Security Tokens

    + + + + + + + + + + + + + + + + + + + + +
    NameTypeCLI Flag
    DownloadTokenstring--download-token
    PreviewTokenstring--preview-token
    +

    Preview Images

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeCLI Flag
    ThumbLibrarystring--thumb-library
    ThumbColorstring--thumb-color
    ThumbFilterstring--thumb-filter
    ThumbSizeint--thumb-size
    ThumbSizeUncachedint--thumb-size-uncached
    ThumbUncachedbool--thumb-uncached
    +

    Image Quality

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeCLI Flag
    JpegQualityint--jpeg-quality
    JpegSizeint--jpeg-size
    PngSizeint--png-size
    +

    Daemon Mode

    +

    If you start the server as a daemon in the background, you can additionally specify a filename for the log and the process ID:

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeCLI Flag
    PIDFilenamestring--pid-filename
    LogFilenamestring--log-filename
    DetachServerbool--detach-server
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/config-files/settings/index.html b/getting-started/config-files/settings/index.html new file mode 100644 index 0000000000..051344a7aa --- /dev/null +++ b/getting-started/config-files/settings/index.html @@ -0,0 +1,7754 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + settings.yml - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    settings.yml

    +

    User interface, download, and indexing preferences are stored in a settings.yml file, located in the config path. If it does not exist yet, it will be created automatically.

    +

    Experienced users may edit this file directly to change certain settings, as not all of them can be changed through the user interface.

    +
    +

    Note that changes to the settings.yml file require a restart to take effect and that settings changed through the web interface will also be saved to this file. We therefore recommend that you only edit it manually while your instance is stopped.

    +
    +

    File Format

    +

    You can use any text editor to create or modify YAML config files. However, it is important that related values, e.g. key-value pairs, start at the same indentation level and that spaces are used for indentation, for example:

    +
    Index:
    +  Path: "/"
    +  Rescan: false
    +
    +

    To avoid ambiguity, it is recommended to enclose text strings in " (double quotes), especially if they contain spaces, a colon, or other special characters.

    +

    Global Defaults

    +

    User settings can optionally be initialized from a settings.yml file located in the same folder as the ↪ defaults.yml file, e.g. /etc/photoprism.

    +

    Sections

    +

    User Interface

    +
    UI:
    +  Scrollbar: true
    +  Zoom: false
    +  Theme: default
    +  Language: en
    +
    +

    If you set Scrollbar to false, the browser scrollbar will be hidden regardless of which device you use and which page you are on. This is generally not recommended, but can be useful e.g. when taking screenshots.

    +

    Setting Zoom to true allows you to enlarge the user interface with gestures on mobile devices, making the app feel more like a regular web page. This can be useful for visually impaired users so that they can magnify text and images when needed.

    +

    Theme and Language change the theme and language of the user interface and correspond to the settings dropdowns you find when navigating to Settings > General.

    +

    File Downloads

    +
    Download:
    +  Name: file
    +  Disabled: false
    +  Originals: true
    +  MediaRaw: false
    +  MediaSidecar: false
    +
    +

    The Name setting determines which file names are used when downloading pictures from search results or in the photo viewer. Currently, the following options are supported:

    +
      +
    • file uses the actual file name in the originals folder
    • +
    • original uses the original file name if the file was imported
    • +
    • share uses a share-friendly file name based on the title and creation date
    • +
    +

    Note that your choice will not affect the file names in ZIP archives when you download complete albums, as they always use share-friendly names. However, we may add settings for this in a future release.

    +

    In case Originals is set to true, only the files in the originals folder will be downloaded, but not any files that were automatically created in the sidecar folder. This is the recommended default.

    +

    If you set MediaRaw to true, RAW image files are downloaded automatically, for example when you click the download button in the photo viewer.

    +

    Setting MediaSidecar to true will also download sidecar files as used for XMP metadata. This is generally not recommended except for some professional workflows.

    +

    Media Library

    +
    Import:
    +  Path: /
    +  Move: false
    +Index:
    +  Path: /
    +  Convert: true
    +  Rescan: false
    +  SkipArchived: false
    +Stack:
    +  UUID: true
    +  Meta: true
    +  Name: false
    +
    +

    These settings affect which files are stacked, as well as your preferences when indexing or importing files.

    +

    You can also change these settings by navigating to Settings > Library and in the Library UI, so it is generally not necessary to edit them directly in the config file.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/config-options/index.html b/getting-started/config-options/index.html new file mode 100644 index 0000000000..a71603bd6e --- /dev/null +++ b/getting-started/config-options/index.html @@ -0,0 +1,9296 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Config Options - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Config Options

    +
    +

    Note that changes to the config options listed below always require a restart to take effect.1 Instead of using environment variables, you can alternatively use an ↪ options.yml file to configure your instance.

    +
    +

    Authentication

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_AUTH_MODE--auth-modepasswordauthentication MODE (public2, password)
    PHOTOPRISM_ADMIN_USER, PHOTOPRISM_ADMIN_USERNAME--admin-useradminUSERNAME of the superadmin account that is created on first startup
    PHOTOPRISM_ADMIN_PASSWORD--admin-passwordinitial PASSWORD of the superadmin account (8-72 characters)
    PHOTOPRISM_PASSWORD_LENGTH--password-length8minimum password LENGTH in characters plus
    PHOTOPRISM_OIDC_URI--oidc-uriissuer URI for single sign-on via OpenID Connect, e.g. https://accounts.google.com
    PHOTOPRISM_OIDC_CLIENT--oidc-clientclient ID for single sign-on via OpenID Connect
    PHOTOPRISM_OIDC_SECRET--oidc-secretclient SECRET for single sign-on via OpenID Connect
    PHOTOPRISM_OIDC_PROVIDER--oidc-providercustom identity provider NAME, e.g. Google
    PHOTOPRISM_OIDC_ICON--oidc-iconcustom identity provider icon URI
    PHOTOPRISM_OIDC_REDIRECT--oidc-redirectautomatically redirect unauthenticated users to the configured identity provider
    PHOTOPRISM_OIDC_REGISTER--oidc-registerallow new users to create an account when they sign in with OpenID Connect
    PHOTOPRISM_OIDC_USERNAME--oidc-usernamepreferred_usernamepreferred username CLAIM for new OpenID Connect users (preferred_username, name, nickname, email)
    PHOTOPRISM_OIDC_WEBDAV--oidc-webdavallow new OpenID Connect users to use WebDAV when they have a role that allows it
    PHOTOPRISM_DISABLE_OIDC--disable-oidcdisable single sign-on via OpenID Connect, even if an identity provider has been configured
    PHOTOPRISM_SESSION_MAXAGE--session-maxage1209600session expiration time in SECONDS, doubled for accounts with 2FA (-1 to disable)
    PHOTOPRISM_SESSION_TIMEOUT--session-timeout604800session idle time in SECONDS, doubled for accounts with 2FA (-1 to disable)
    PHOTOPRISM_SESSION_CACHE--session-cache900session cache duration in SECONDS (60-3600)
    +

    Logging

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_LOG_LEVEL--log-levelinfolog message verbosity LEVEL (trace, debug, info, warning, error, fatal, panic)
    PHOTOPRISM_DEBUG--debugenable debug mode, show non-essential log messages
    PHOTOPRISM_TRACE--traceenable trace mode, show all log messages
    +

    Storage

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_CONFIG_PATH--config-pathconfig storage PATH, values in options.yml override CLI flags and environment variables if present
    PHOTOPRISM_DEFAULTS_YAML--defaults-yaml/etc/photoprism/defaults.ymlload config defaults from FILE if exists, does not override CLI flags and environment variables
    PHOTOPRISM_ORIGINALS_PATH--originals-pathstorage PATH of your original media files (photos and videos)
    PHOTOPRISM_ORIGINALS_LIMIT--originals-limit1000maximum size of media files in MB (1-100000; -1 to disable)
    PHOTOPRISM_RESOLUTION_LIMIT--resolution-limit150maximum resolution of media files in MEGAPIXELS (1-900; -1 to disable)
    PHOTOPRISM_USERS_PATH--users-pathusersrelative PATH to create base and upload subdirectories for users
    PHOTOPRISM_STORAGE_PATH--storage-pathwritable storage PATH for sidecar, cache, and database files
    PHOTOPRISM_IMPORT_PATH--import-pathbase PATH from which files can be imported to originals optional
    PHOTOPRISM_IMPORT_DEST--import-destrelative originals PATH to which the files should be imported by default optional
    PHOTOPRISM_CACHE_PATH--cache-pathcustom cache PATH for sessions and thumbnail files optional
    PHOTOPRISM_TEMP_PATH--temp-pathtemporary file PATHoptional
    PHOTOPRISM_ASSETS_PATH--assets-pathassets PATH containing static resources like icons, models, and translations
    +

    Sidecar Files

    + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_SIDECAR_PATH--sidecar-pathcustom relative or absolute sidecar PATHoptional
    PHOTOPRISM_SIDECAR_YAML--sidecar-yamltruecreate YAML sidecar files to back up picture metadata
    +

    Backup

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_BACKUP_PATH--backup-pathcustom base PATH for creating and restoring backups optional
    PHOTOPRISM_BACKUP_SCHEDULE--backup-scheduledailybackup SCHEDULE in cron format (e.g. "0 12 * * *" for daily at noon) or at a random time (daily, weekly)
    PHOTOPRISM_BACKUP_RETAIN--backup-retain3NUMBER of index backups to keep (-1 to keep all)
    PHOTOPRISM_BACKUP_DATABASE--backup-databasetruecreate regular backups based on the configured schedule
    PHOTOPRISM_BACKUP_ALBUMS--backup-albumstruecreate YAML files to back up album metadata
    +

    Indexing

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_INDEX_WORKERS, PHOTOPRISM_WORKERS--index-workers4maximum NUMBER of indexing workers, default depends on the number of physical cores
    PHOTOPRISM_INDEX_SCHEDULE--index-scheduleindexing SCHEDULE in cron format (e.g. "@every 3h" for every 3 hours; "" to disable)
    PHOTOPRISM_WAKEUP_INTERVAL--wakeup-interval15m0sTIME between facial recognition, file sync, and metadata worker runs (1-86400s)
    PHOTOPRISM_AUTO_INDEX--auto-index300delay before automatically indexing files in SECONDS when uploading via WebDAV (-1 to disable)
    PHOTOPRISM_AUTO_IMPORT--auto-import-1delay before automatically importing files in SECONDS when uploading via WebDAV (-1 to disable)
    +

    Feature Flags

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_READONLY--read-onlydisable features that require write permission for the originals folder
    PHOTOPRISM_EXPERIMENTAL--experimentalenable new features currently under development
    PHOTOPRISM_DISABLE_SETTINGS--disable-settingsdisable the settings user interface and server API, e.g. in combination with public mode
    PHOTOPRISM_DISABLE_BACKUPS--disable-backupsprevent database and album backups as well as YAML sidecar files from being created
    PHOTOPRISM_DISABLE_RESTART--disable-restartprevent admins from restarting the server through the user interface
    PHOTOPRISM_DISABLE_WEBDAV--disable-webdavprevent other apps from accessing PhotoPrism as a shared network drive
    PHOTOPRISM_DISABLE_PLACES--disable-placesdisable interactive world maps and reverse geocoding
    PHOTOPRISM_DISABLE_TENSORFLOW--disable-tensorflowdisable features depending on TensorFlow, e.g. image classification and face recognition
    PHOTOPRISM_DISABLE_FACES--disable-facesdisable face detection and recognition (requires TensorFlow)
    PHOTOPRISM_DISABLE_CLASSIFICATION--disable-classificationdisable image classification (requires TensorFlow)
    PHOTOPRISM_DISABLE_FFMPEG--disable-ffmpegdisable video transcoding and thumbnail extraction with FFmpeg
    PHOTOPRISM_DISABLE_EXIFTOOL--disable-exiftooldisable metadata extraction with ExifTool (required for full Video, Live Photo, and XMP support)
    PHOTOPRISM_DISABLE_VIPS--disable-vipsdisable image processing and conversion with libvips
    PHOTOPRISM_DISABLE_SIPS--disable-sipsdisable file conversion using the sips command under macOS
    PHOTOPRISM_DISABLE_DARKTABLE--disable-darktabledisable conversion of RAW images with Darktable
    PHOTOPRISM_DISABLE_RAWTHERAPEE--disable-rawtherapeedisable conversion of RAW images with RawTherapee
    PHOTOPRISM_DISABLE_IMAGEMAGICK--disable-imagemagickdisable conversion of image files with ImageMagick
    PHOTOPRISM_DISABLE_HEIFCONVERT--disable-heifconvertdisable conversion of HEIC images with libheif
    PHOTOPRISM_DISABLE_RSVGCONVERT--disable-rsvgconvertdisable conversion of SVG graphics with librsvg plus
    PHOTOPRISM_DISABLE_VECTORS--disable-vectorsdisable vector graphics support plus
    PHOTOPRISM_DISABLE_JPEGXL--disable-jpegxldisable JPEG XL file format support
    PHOTOPRISM_DISABLE_RAW--disable-rawdisable indexing and conversion of RAW images
    PHOTOPRISM_RAW_PRESETS--raw-presetsenables applying user presets when converting RAW images (reduces performance)
    PHOTOPRISM_EXIF_BRUTEFORCE--exif-bruteforcealways perform a brute-force search if no Exif headers were found
    PHOTOPRISM_DETECT_NSFW--detect-nsfwflag newly added pictures as private if they might be offensive (requires TensorFlow)
    PHOTOPRISM_UPLOAD_NSFW--upload-nsfwallow uploads that might be offensive (detecting unsafe content requires TensorFlow)
    +

    Customization

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_DEFAULT_LOCALE--default-localeendefault user interface language CODE
    PHOTOPRISM_DEFAULT_TIMEZONE--default-timezoneUTCdefault time zone NAME, e.g. for scheduling backups
    PHOTOPRISM_DEFAULT_THEME--default-themedefault user interface theme NAME
    PHOTOPRISM_APP_NAME--app-nameprogressive web app NAME when installed on a device
    PHOTOPRISM_APP_MODE--app-modestandaloneprogressive web app MODE (fullscreen, standalone, minimal-ui, browser)
    PHOTOPRISM_APP_ICON--app-iconhome screen ICON (logo, app, crisp, mint, bold, square)
    PHOTOPRISM_APP_COLOR--app-color#000000splash screen COLOR code
    PHOTOPRISM_LEGAL_INFO--legal-infolegal information TEXT, displayed in the page footer
    PHOTOPRISM_LEGAL_URL--legal-urllegal information URL
    PHOTOPRISM_WALLPAPER_URI--wallpaper-urilogin screen background image URI
    +

    Site Information

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_SITE_URL--site-urlhttp://localhost:2342/public site URL
    PHOTOPRISM_SITE_AUTHOR--site-authorsite OWNER, copyright, or artist
    PHOTOPRISM_SITE_TITLE--site-titlesite TITLE
    PHOTOPRISM_SITE_CAPTION--site-captionAI-Powered Photos Appsite CAPTION
    PHOTOPRISM_SITE_DESCRIPTION--site-descriptionsite DESCRIPTIONoptional
    PHOTOPRISM_SITE_PREVIEW--site-previewsharing preview image URL
    PHOTOPRISM_CDN_URL--cdn-urlcontent delivery network URL
    PHOTOPRISM_CDN_VIDEO--cdn-videostream videos over the specified CDN
    PHOTOPRISM_CORS_ORIGIN--cors-originorigin URL from which browsers are allowed to perform cross-origin requests (leave blank to disable or use * to allow all)
    PHOTOPRISM_CORS_HEADERS--cors-headersAccept, Accept-Ranges, Content-Disposition, Content-Encoding, Content-Range, Locationone or more HEADERS that browsers should see when performing a cross-origin request
    PHOTOPRISM_CORS_METHODS--cors-methodsGET, HEAD, OPTIONSone or more METHODS that may be used when performing a cross-origin request
    +

    Proxy Servers

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_HTTPS_PROXY--https-proxyproxy server URL to be used for outgoing connections optional
    PHOTOPRISM_HTTPS_PROXY_INSECURE--https-proxy-insecureignore invalid HTTPS certificates when using a proxy
    PHOTOPRISM_TRUSTED_PROXY--trusted-proxy172.16.0.0/12CIDR ranges or IPv4/v6 addresses from which reverse proxy headers can be trusted, separated by commas
    PHOTOPRISM_PROXY_PROTO_HEADER--proxy-proto-headerX-Forwarded-Protoproxy protocol header NAME
    PHOTOPRISM_PROXY_PROTO_HTTPS--proxy-proto-httpshttpsforwarded HTTPS protocol NAME
    +

    Web Server

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_DISABLE_TLS--disable-tlsdisable HTTPS/TLS even if the site URL starts with https:// and a certificate is available
    PHOTOPRISM_DEFAULT_TLS--default-tlsdefault to a self-signed HTTPS/TLS certificate if no other certificate is available
    PHOTOPRISM_TLS_CERT--tls-certpublic HTTPS certificate FILE (.crt), ignored for Unix domain sockets
    PHOTOPRISM_TLS_KEY--tls-keyprivate HTTPS key FILE (.key), ignored for Unix domain sockets
    PHOTOPRISM_DISABLE_STS--disable-stsdisable HTTP Strict-Transport-Security (STS) header
    PHOTOPRISM_STS_SECONDS--sts-seconds31536000TIME for the browser to remember that the site is to be accessed only via HTTPS (0 to disable) plus
    PHOTOPRISM_STS_SUBDOMAINS--sts-subdomainsrule applies to all subdomains as well plus
    PHOTOPRISM_STS_PRELOAD--sts-preloadsubmit to Google's HSTS preload service plus
    PHOTOPRISM_AUTH_LIMIT--auth-limit60maximum number of consecutive invalid access TOKENS from a single IP plus
    PHOTOPRISM_AUTH_INTERVAL--auth-interval10saverage DURATION between invalid access tokens from a single IP (0-86400s) plus
    PHOTOPRISM_LOGIN_LIMIT--login-limit10maximum number of consecutive failed LOGINS from a single IP plus
    PHOTOPRISM_LOGIN_INTERVAL--login-interval1m0saverage DURATION between failed logins from a single IP (0-86400s) plus
    PHOTOPRISM_IPS_LIMIT--ips-limit3maximum number of malicious request ATTEMPTS before a client IP is blocked (-1 to disable) plus
    PHOTOPRISM_IPS_INTERVAL--ips-interval1h0m0saverage DURATION between malicious request attempts from a single IP (0-86400s) plus
    PHOTOPRISM_HTTP_CSP--http-cspHTTP Content-Security-Policy (CSP) HEADERplus
    PHOTOPRISM_HTTP_CTO--http-ctonosniffHTTP X-Content-Type-Options HEADERplus
    PHOTOPRISM_HTTP_COOP--http-coopsame-originHTTP Cross-Origin-Opener-Policy (COOP) HEADERplus
    PHOTOPRISM_HTTP_REFERRER_POLICY--http-referrer-policysame-originHTTP Referrer-Policy HEADERplus
    PHOTOPRISM_HTTP_FRAME_OPTIONS--http-frame-optionsDENYHTTP X-Frame-Options HEADERplus
    PHOTOPRISM_HTTP_XSS_PROTECTION--http-xss-protection1; mode=blockHTTP X-XSS-Protection HEADERplus
    PHOTOPRISM_HTTP_MODE--http-modeWeb server MODE (debug, release, test)
    PHOTOPRISM_HTTP_COMPRESSION--http-compressionWeb server compression METHOD (gzip, none)
    PHOTOPRISM_HTTP_CACHE_PUBLIC--http-cache-publicallow static content to be cached by a CDN or caching proxy
    PHOTOPRISM_HTTP_CACHE_MAXAGE--http-cache-maxage2592000time in SECONDS until cached content expires
    PHOTOPRISM_HTTP_VIDEO_MAXAGE--http-video-maxage21600time in SECONDS until cached videos expire
    PHOTOPRISM_HTTP_HOST--http-host0.0.0.0Web server IP address or Unix domain socket, e.g. unix:/var/run/photoprism.sock
    PHOTOPRISM_HTTP_PORT--http-port2342Web server port NUMBER, ignored for Unix domain sockets
    PHOTOPRISM_HTTP_HOSTNAME--http-hostnameserve requests for this HOSTNAME only plus
    +

    Database Connection

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_DATABASE_DRIVER--database-driversqlitedatabase DRIVER (sqlite, mysql)
    PHOTOPRISM_DATABASE_DSN--database-dsndatabase connection DSN (sqlite file, optional for mysql)
    PHOTOPRISM_DATABASE_NAME--database-namephotoprismdatabase schema NAME
    PHOTOPRISM_DATABASE_SERVER--database-serverdatabase HOST incl. port e.g. "mariadb:3306" (or socket path)
    PHOTOPRISM_DATABASE_USER--database-userphotoprismdatabase user NAME
    PHOTOPRISM_DATABASE_PASSWORD--database-passworddatabase user PASSWORD
    PHOTOPRISM_DATABASE_TIMEOUT--database-timeout15timeout in SECONDS for establishing a database connection (1-60)
    PHOTOPRISM_DATABASE_CONNS--database-conns0maximum NUMBER of open database connections
    PHOTOPRISM_DATABASE_CONNS_IDLE--database-conns-idle0maximum NUMBER of idle database connections
    +

    File Conversion

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_FFMPEG_BIN--ffmpeg-binffmpegFFmpeg COMMAND for video transcoding and thumbnail extraction
    PHOTOPRISM_FFMPEG_ENCODER--ffmpeg-encoderlibx264FFmpeg AVC encoder NAME
    PHOTOPRISM_FFMPEG_SIZE--ffmpeg-size4096maximum video size in PIXELS (720-7680)
    PHOTOPRISM_FFMPEG_BITRATE--ffmpeg-bitrate50maximum video BITRATE in Mbit/s
    PHOTOPRISM_FFMPEG_MAP_VIDEO--ffmpeg-map-video0:v:0video STREAMS that should be transcoded
    PHOTOPRISM_FFMPEG_MAP_AUDIO--ffmpeg-map-audio0:a:0?audio STREAMS that should be transcoded
    PHOTOPRISM_EXIFTOOL_BIN--exiftool-binexiftoolExifTool COMMAND for extracting metadata
    PHOTOPRISM_SIPS_BIN--sips-binsipsSips COMMAND for media file conversion macOS only
    PHOTOPRISM_SIPS_EXCLUDE, PHOTOPRISM_SIPS_BLACKLIST--sips-excludeavif, avifs, thmfile EXTENSIONS not to be used with Sips macOS only
    PHOTOPRISM_DARKTABLE_BIN--darktable-bindarktable-cliDarktable CLI COMMAND for RAW to JPEG conversion
    PHOTOPRISM_DARKTABLE_EXCLUDE, PHOTOPRISM_DARKTABLE_BLACKLIST--darktable-excludethmfile EXTENSIONS not to be used with Darktable
    PHOTOPRISM_DARKTABLE_CACHE_PATH--darktable-cache-pathcustom Darktable cache PATH
    PHOTOPRISM_DARKTABLE_CONFIG_PATH--darktable-config-pathcustom Darktable config PATH
    PHOTOPRISM_RAWTHERAPEE_BIN--rawtherapee-binrawtherapee-cliRawTherapee CLI COMMAND for RAW to JPEG conversion
    PHOTOPRISM_RAWTHERAPEE_EXCLUDE, PHOTOPRISM_RAWTHERAPEE_BLACKLIST--rawtherapee-excludedng, thmfile EXTENSIONS not to be used with RawTherapee
    PHOTOPRISM_IMAGEMAGICK_BIN--imagemagick-binconvertImageMagick CLI COMMAND for image file conversion
    PHOTOPRISM_IMAGEMAGICK_EXCLUDE, PHOTOPRISM_IMAGEMAGICK_BLACKLIST--imagemagick-excludeheif, heic, heics, avif, avifs, jxl, thmfile EXTENSIONS not to be used with ImageMagick
    PHOTOPRISM_HEIFCONVERT_BIN--heifconvert-binheif-convertlibheif HEIC image conversion COMMAND
    PHOTOPRISM_RSVGCONVERT_BIN--rsvgconvert-binrsvg-convertlibrsvg SVG graphics conversion COMMANDplus
    +

    Security Tokens

    + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_DOWNLOAD_TOKEN--download-tokenDEFAULT download URL token for originals (leave blank for a random value)
    PHOTOPRISM_PREVIEW_TOKEN--preview-tokenDEFAULT thumbnail and video streaming URL token (leave blank for a random value)
    +

    Preview Images

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_THUMB_LIBRARY--thumb-libraryautoimage processing LIBRARY to be used for generating thumbnails (auto, imaging, vips)
    PHOTOPRISM_THUMB_COLOR--thumb-colorautostandard color PROFILE for thumbnails (auto, preserve, srgb, none)
    PHOTOPRISM_THUMB_FILTER--thumb-filterautodownscaling filter NAME (imaging best to worst: blackman, lanczos, cubic, linear, nearest)
    PHOTOPRISM_THUMB_SIZE--thumb-size1920maximum size of pre-generated thumbnails in PIXELS (720-7680)
    PHOTOPRISM_THUMB_SIZE_UNCACHED--thumb-size-uncached7680maximum size of thumbnails generated on demand in PIXELS (720-7680)
    PHOTOPRISM_THUMB_UNCACHED--thumb-uncachedgenerate missing thumbnails on demand (high memory and cpu usage)
    +

    Image Quality

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_JPEG_QUALITY--jpeg-quality83higher values increase the image QUALITY and file size (25-100)
    PHOTOPRISM_JPEG_SIZE--jpeg-size7680maximum size of generated JPEG images in PIXELS (720-30000)
    PHOTOPRISM_PNG_SIZE--png-size7680maximum size of generated PNG images in PIXELS (720-30000)
    +

    Face Recognition

    +
    +

    To recognize faces, PhotoPrism first extracts crops from your images using a library based on pixel intensity comparisons. These are then fed into TensorFlow to compute 512-dimensional vectors for characterization. In the final step, the DBSCAN algorithm attempts to cluster these so-called face embeddings, so they can be matched to persons with just a few clicks. A reasonable range for the similarity distance between face embeddings is between 0.60 and 0.70, with a higher value being more aggressive and leading to larger clusters with more false positives. To cluster a smaller number of faces, you can reduce the core to 3 or 2 similar faces.

    +
    +

    We recommend that only advanced users change these parameters:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_FACE_SIZE--face-size50minimum size of faces in PIXELS (20-10000)
    PHOTOPRISM_FACE_SCORE--face-score9.000000minimum face QUALITY score (1-100)
    PHOTOPRISM_FACE_OVERLAP--face-overlap42face area overlap threshold in PERCENT (1-100)
    PHOTOPRISM_FACE_CLUSTER_SIZE--face-cluster-size80minimum size of automatically clustered faces in PIXELS (20-10000)
    PHOTOPRISM_FACE_CLUSTER_SCORE--face-cluster-score15minimum QUALITY score of automatically clustered faces (1-100)
    PHOTOPRISM_FACE_CLUSTER_CORE--face-cluster-core4NUMBER of faces forming a cluster core (1-100)
    PHOTOPRISM_FACE_CLUSTER_DIST--face-cluster-dist0.640000similarity DISTANCE of faces forming a cluster core (0.1-1.5)
    PHOTOPRISM_FACE_MATCH_DIST--face-match-dist0.460000similarity OFFSET for matching faces with existing clusters (0.1-1.5)
    +

    Daemon Mode

    +

    If you start the server as a daemon in the background, you can additionally specify a filename for the log and the process ID:

    + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_PID_FILENAME--pid-filenameprocess id FILEdaemon-mode only
    PHOTOPRISM_LOG_FILENAME--log-filenameserver log FILEdaemon-mode only
    +

    Docker Image

    +

    The following variables are used by our Docker images only and have no effect otherwise:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentDefaultDescription
    PHOTOPRISM_UID0run as a non-root user after initialization (supported: 0, 33, 50-99, 500-600, 900-1250, and 2000-2100)
    PHOTOPRISM_GID0run with a specific group id after initialization, can optionally be used together with PHOTOPRISM_UID (supported: 0, 33, 44, 50-99, 105, 109, 115, 116, 500-600, 900-1250, and 2000-2100)
    PHOTOPRISM_UMASK0002file-creation mode (default: u=rwx,g=rwx,o=rx)
    PHOTOPRISM_INITrun/install on first startup (options: update https gpu ffmpeg tensorflow davfs clitools clean)
    PHOTOPRISM_DISABLE_CHOWNfalsedisable updating storage permissions via chmod and chown on startup
    +
    +
    +
      +
    1. +

      If you are using Docker Compose, you can open a terminal, run docker compose stop, and then run docker compose up -d to restart all services. 

      +
    2. +
    3. +

      Enabling public mode is not recommended for instances installed on a server outside your home network, as this allows others to access your pictures without authentication. 

      +
    4. +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/docker-compose/index.html b/getting-started/docker-compose/index.html new file mode 100644 index 0000000000..798907a222 --- /dev/null +++ b/getting-started/docker-compose/index.html @@ -0,0 +1,8273 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Docker Compose - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Setup Using Docker Compose

    +

    With Docker Compose, you use a YAML file to configure all application services so you can easily start them with a single command. +Before you proceed, make sure you have Docker installed on your system. It is available for Mac, Linux, and Windows.

    +

    Alternatively, Podman Compose is supported as a drop-in replacement for Docker Compose on Red Hat-compatible Linux distributions like RHEL, CentOS, Fedora, AlmaLinux, and Rocky Linux.

    +

    Step 1: Configure

    +
    +
    +
    +

    Download our docker-compose.yml example +(right click and Save Link As... or use wget) to a folder of your choice, +and change the configuration as needed:

    +
    wget https://dl.photoprism.app/docker/docker-compose.yml
    +
    +

    Commands on Linux may have to be prefixed with sudo when not running as root. +Note that this will point the home directory shortcut ~ to /root in the volumes: +section of your config file. Kernel security modules such as AppArmor and SELinux +have been reported to cause issues.

    +

    We recommend that your server has at least 4 GB of swap configured and to avoid setting a hard memory limit, as this can lead to unexpected restarts when the indexer temporarily needs more memory to process large files. Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    +
    +
    +

    Download our docker-compose.yml example +(right click and Save Link As... or use wget) to a folder of your choice, +and change the configuration as needed:

    +
    wget https://dl.photoprism.app/podman/docker-compose.yml
    +
    +

    Alternatively, you can run these commands to install Podman and download the default configuration to /opt/photoprism:

    +
    mkdir -p /opt/photoprism
    +cd /opt/photoprism
    +curl -sSf https://dl.photoprism.app/podman/install.sh | bash
    +
    +

    Please keep in mind to replace the docker and docker compose commands with podman and podman-compose when following the examples in our documentation.

    +

    We recommend that your server has at least 4 GB of swap configured and to avoid setting a hard memory limit, as this can lead to unexpected restarts when the indexer temporarily needs more memory to process large files. Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    +
    +
    +

    Download our docker-compose.yml example for +the Raspberry Pi and other ARM64-based devices (right click and Save Link As... or use wget) to a folder of your choice, +and change the configuration as needed:

    +
    wget https://dl.photoprism.app/docker/arm64/docker-compose.yml
    +
    +

    Mostly the same installation instructions as for regular Linux servers apply. +Commands may have to be prefixed with sudo when not running as root.

    +

    Please verify if your device meets the system requirements and has +at least 4 GB of swap configured before you continue. Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    +
    +
    +

    Download our docker-compose.yml example for +older ARMv7-based devices (right click and Save Link As... or use wget) to a folder of your choice, +and change the configuration as needed:

    +
    wget https://dl.photoprism.app/docker/armv7/docker-compose.yml
    +
    +

    Mostly the same installation instructions as for regular Linux servers apply. +Commands may have to be prefixed with sudo when not running as root.

    +

    Please verify if your device meets the system requirements and has +at least 4 GB of swap configured before you continue. Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    +
    +
    +

    Download our docker-compose.yml example for Windows +(right click and Save Link As...) to a folder of your choice, and change the configuration as needed:

    +

    https://dl.photoprism.app/docker/windows/docker-compose.yml

    +

    It is important to increase the Docker memory limit to 4 GB or more when using Hyper-V. The default of 2 GB can reduce indexing performance and cause unexpected restarts. Also make sure you configure at least 4 GB of swap space. Docker Desktop uses dynamic memory allocation with WSL 2, meaning you do not need to change any memory-related settings (depending on which version of Windows and Docker you are using).

    +

    Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    +
    +

    Running the following commands will automatically download all required config files and start the server for you:

    +
    curl.exe -o install.bat https://dl.photoprism.app/docker/windows/install.bat
    +install.bat
    +
    +

    Before you run this, make sure you are in the directory where you want to install PhotoPrism and that Docker Desktop is installed and started on your PC.

    +
    +
    +
    +

    Download our docker-compose.yml example for macOS +(right click and Save Link As...) to a folder of your choice, and change the configuration as needed:

    +

    https://dl.photoprism.app/docker/macos/docker-compose.yml

    +

    It is important to increase the Docker memory limit to 4 GB or more, +as the default of 2 GB can reduce indexing performance and cause unexpected restarts.

    +

    Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    +
    +
    +
    +
    +

    When editing YAML files, please make sure that related values remain on the same indentation level and that lists start with a dash. +If the value of an environment variable contains a literal $ sign, for example in a password, it must be escaped with $$ (a double dollar sign) so that e.g. "compo$e" becomes "compo$$e".

    +
    +
    +

    Always change PHOTOPRISM_ADMIN_PASSWORD so that the app starts with a secure initial password. +Never use easy-to-guess passwords or default values like insecure on publicly accessible servers. +There is no default in case no password was provided. A minimum length of 8 characters is required.

    +
    +

    Database

    +

    Our example includes a pre-configured MariaDB database server. If you remove it and provide no other database server credentials, SQLite database files will be created in the storage folder. Local SSD storage is best for databases of any kind.

    +

    Never store database files on an unreliable device such as a USB flash drive, SD card, or shared network folder. These may also have unexpected file size limitations, which is especially problematic for databases that do not split data into smaller files.

    +
    +

    You cannot change the database password with MARIADB_PASSWORD after MariaDB has been started for the first time. However, choosing a secure password is not essential if you do not share the database with other applications or expose it over a network. To enable automatic schema updates when upgrading to a new major version, please make sure that MARIADB_AUTO_UPGRADE is set to a non-empty value.

    +
    +

    Volumes

    +

    You must explicitly specify the directories you want to mount from your host, since PhotoPrism can't see files in folders that have not been shared. This is an important security feature and allows for a flexible configuration without having to change any other variables.

    +
    +

    It is important that all folders are mounted to persistent volumes. We recommend changing the relative paths used in our examples to absolute paths and to avoid using named or anonymous volumes in order to prevent potential data loss when the container is recreated, e.g. after an update of the Docker image.

    +
    +
    /photoprism/originals
    +

    The originals folder contains your original photo and video files. ~/Pictures will be mounted by default, where ~ is a shortcut for your home directory:

    +
    services:
    +  photoprism:
    +    volumes:
    +      - "~/Pictures:/photoprism/originals"
    +
    +

    We recommend that you change ~/Pictures to the directory where your existing media files are, for example:

    +
          - "/mnt/photos:/photoprism/originals"
    +
    +

    Additional directories can be mounted as sub folders of /photoprism/originals (depending on overlay filesystem support):

    +
        volumes:
    +      - "/mnt/photos:/photoprism/originals"
    +      - "/mnt/videos:/photoprism/originals/videos"
    +
    +

    On Windows, prefix the host path with the drive letter and use / instead of \ as separator:

    +
        volumes:
    +      - "D:/Example/Pictures:/photoprism/originals"
    +
    +
    +

    If read-only mode is enabled, all features that require write permission to the originals folder +are disabled, e.g. WebDAV, uploading and deleting files. Set PHOTOPRISM_READONLY to "true" +in docker-compose.yml for this. In addition, you can mount volumes with the :ro flag +so that writes are also blocked by Docker.

    +
    +
    /photoprism/storage
    +

    The storage folder is used to save config, cache, backup, thumbnail, and sidecar files. It must always be specified so that you do not lose these files after a restart or upgrade. +If available, we recommend you put the storage folder on a local SSD drive for best performance. You can otherwise keep the default and store the files in a folder relative to the current directory:

    +
    services:
    +  photoprism:
    +    volumes:
    +      - "./storage:/photoprism/storage"
    +
    +
    +

    Never configure the storage folder to be inside the originals folder unless the name starts with a . to indicate that it is hidden. +Should you later want to move your instance to another host, the easiest and most time-saving way is to copy the entire storage folder along with your originals and database.

    +
    +
    /photoprism/import
    +

    You can optionally mount an import folder from which files can be transferred to the originals folder in a structured way that avoids duplicates, for example:

    +
    services:
    +  photoprism:
    +    volumes:
    +      - "/mnt/media/usb:/photoprism/import"
    +
    +

    Imported files receive a canonical filename and will be organized by year and month. You should never configure the import folder to be inside the originals folder, as this will cause a loop by importing already indexed files.

    +
    +

    Even if you don't specify an import folder, adding files via Web Upload and WebDAV remains possible unless read-only mode is enabled or the features have been disabled.

    +
    +

    Step 2: Start the server

    +

    Open a terminal and change to the folder in which your config file has been saved.1 +Run this command to start the application and database services in the background:

    +
    docker compose up -d
    +
    +

    Note that our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    +

    Now open the Web UI by navigating to http://localhost:2342/. You should see a login screen. +Sign in with the user admin and the initial password configured via PHOTOPRISM_ADMIN_PASSWORD. +You may change it on the account settings page. +Enabling public mode will disable authentication.

    +
    +

    It can be helpful to keep Docker running in the foreground while debugging so that log messages are displayed directly. To do this, omit the -d parameter when restarting.

    +

    Should the server already be running, or you see no errors, you may have started it +on a different host and/or port. There could also be an issue with your browser, +ad blocker, or firewall settings.

    +
    +
    +

    You cannot change the password with PHOTOPRISM_ADMIN_PASSWORD after the app has been started for the first time. To change the admin password, run the docker compose exec photoprism photoprism passwd [username] command in a terminal. You can also run docker compose exec photoprism photoprism reset to delete the existing index database and start from scratch.

    +
    +

    The server port and other config options can be changed in docker-compose.yml at any time. +Remember to restart the services for changes to take effect:

    +
    docker compose stop
    +docker compose up -d
    +
    +

    Step 3: Index Your Library

    +

    Our First Steps 👣 tutorial guides you through the user interface and settings to ensure your library is indexed according to your individual preferences.

    + + +

    Easy, isn't it?

    +

    PhotoPrism® Plus

    +

    Our members can activate additional features by logging in with the admin user created during setup and then following the steps described in our activation guide. Thank you for your support, which has been and continues to be essential to the success of the project!

    +

    Compare Memberships › View Membership FAQ ›

    +
    +

    We recommend that new users install our free Community Edition before signing up for a membership.

    +
    +

    Troubleshooting

    +

    If your server runs out of memory, the index is frequently locked, or other system resources are running low:

    + +

    Other issues? Our troubleshooting checklists help you quickly diagnose and solve them.

    +
    +

    You are welcome to ask for help in our community chat. +Sponsors receive direct technical support via email. +Before submitting a support request, try to determine the cause of your problem.

    +
    +

    Command-Line Interface

    +

    Introduction

    +

    photoprism help lists all commands and config options available in the current version:

    +
    docker compose exec photoprism photoprism help
    +
    +

    Use the --help flag to see a detailed command description, for example:

    +
    docker compose exec photoprism photoprism backup --help
    +
    +

    PhotoPrism's command-line interface is also well suited for job automation using a scheduler.

    +
    +

    When using Docker Compose, you can prefix the commands you want to run with docker compose exec [service] to execute them in the specified service container. +If this fails with no container found, please make sure that the service has been started, you have specified an existing service (usually photoprism) and you are in the folder where your config file is located.

    +
    +

    Opening a Terminal

    +

    To open a terminal session as the default user:

    +
    docker compose exec photoprism bash
    +
    +

    Since the above will open the terminal as root by default, we recommend that you pass the -u flag to explicitly open a non-root session if PhotoPrism is running under a specific user account, for example:

    +
    docker compose exec -u 1000 photoprism bash
    +
    +

    This avoids potential filesystem permission issues that can occur when a command creates new files or folders, e.g. to store thumbnails.

    +

    Changing the User ID

    +

    Specifying a user with the -u flag is possible for all commands you run with Docker and Docker Compose. In the following examples, it is omitted for brevity. +Note, however, that commands that you run without an explicit user ID might be executed as root. +The currently supported user ID ranges are 0, 33, 50-99, 500-600, 900-1250, and 2000-2100.

    +
    +

    We recommend running the photoprism service as a non-root user by setting either the user service property or the PHOTOPRISM_UID environment variable in your config file. Don't forget to update file permissions and/or ownership with the chown command when you make changes.

    +
    +

    Examples

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ActionCommand
    Start Servicesdocker compose up -d
    Stop Servicesdocker compose stop
    Download Updatesdocker compose pull
    Uninstalldocker compose rm -s -v
    Watch Logsdocker compose logs -f --tail=100
    Display Config Valuesdocker compose exec photoprism photoprism show config
    Show Migration Statusdocker compose exec photoprism photoprism migrations ls
    Repeat Failed Migrationsdocker compose exec photoprism photoprism migrations run -f
    Reset Databasedocker compose exec photoprism photoprism reset --yes
    Backup Databasedocker compose exec photoprism photoprism backup -a -i
    Restore Databasedocker compose exec photoprism photoprism restore -a -i
    Change Passworddocker compose exec photoprism photoprism passwd [username]
    Show User Management Commandsdocker compose exec photoprism photoprism users help
    Reset User Accountsdocker compose exec photoprism photoprism users reset --yes
    Reset Sessions and Access Tokensdocker compose exec photoprism photoprism auth reset --yes
    Show Face Recognition Commandsdocker compose exec photoprism photoprism faces help
    Index Facesdocker compose exec photoprism photoprism faces index
    Reset People & Facesdocker compose exec photoprism photoprism faces reset -f
    Transcode Videos to AVCdocker compose exec photoprism photoprism convert
    Regenerate Thumbnailsdocker compose exec photoprism photoprism thumbs -f
    Update Indexdocker compose exec photoprism photoprism index --cleanup
    Move to Originalsdocker compose exec photoprism photoprism import [path]
    Copy to Originalsdocker compose exec photoprism photoprism cp [path]
    +

    Note that our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    +
    +

    Complete Rescan

    +

    docker compose exec photoprism photoprism index -f rescans all originals, including already indexed and unchanged files. +This may be necessary after major upgrades and after migrations of the database schema, especially if search results are missing or incorrect. Note you can also start a rescan from the user interface by navigating to Library > Index, checking "Complete Rescan" and then clicking "Start". Manually entered information such as labels, people, titles or descriptions will not be modified when indexing, even if you perform a "complete rescan".

    +
    +
    +
    +
      +
    1. +

      The default name for Docker Compose config files is compose.yaml or docker-compose.yml. For simplicity, it does not need to be specified if you run commands in the same directory. Config files for other apps and instances should be placed in separate folders. 

      +
    2. +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/docker/index.html b/getting-started/docker/index.html new file mode 100644 index 0000000000..d01bb60ec6 --- /dev/null +++ b/getting-started/docker/index.html @@ -0,0 +1,8178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Pure Docker - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Running PhotoPrism with Docker

    +

    We recommend using Docker Compose because it is easier and provides more convenience for running multiple services than the pure Docker command-line interface. +Before you proceed, make sure you have Docker installed on your system. It is available for Mac, Linux, and Windows.

    +

    Alternatively, Podman is supported as a drop-in replacement for Docker on Red Hat-compatible Linux distributions like RHEL, CentOS, Fedora, AlmaLinux, and Rocky Linux.

    +

    Step 1: Start the server

    +
    +
    +
    +

    Open a terminal and run this command to start the app after replacing ~/Pictures with +the folder containing your pictures:

    +
    docker run -d \
    +  --name photoprism \
    +  --security-opt seccomp=unconfined \
    +  --security-opt apparmor=unconfined \
    +  -p 2342:2342 \
    +  -e PHOTOPRISM_UPLOAD_NSFW="true" \
    +  -e PHOTOPRISM_ADMIN_PASSWORD="insecure" \
    +  -v /photoprism/storage \
    +  -v ~/Pictures:/photoprism/originals \
    +  photoprism/photoprism
    +
    +
    +
    +

    Open a terminal and run this command to start the app after replacing ~/Pictures with +the folder containing your pictures:

    +
    podman run -d \
    +  --name photoprism \
    +  --privileged \
    +  --security-opt seccomp=unconfined \
    +  --security-opt apparmor=unconfined \
    +  -p 2342:2342 \
    +  -e PHOTOPRISM_UPLOAD_NSFW="true" \
    +  -e PHOTOPRISM_ADMIN_PASSWORD="insecure" \
    +  -v /photoprism/storage \
    +  -v ~/Pictures:/photoprism/originals \
    +  photoprism/photoprism
    +
    +

    Please keep in mind to replace the docker command with podman when following the examples in our documentation.

    +
    +
    +
    +

    The server port and other config options can be changed as needed. If you provide no database server credentials, SQLite database files will be created in the storage folder. Note, however, that SQLite is not a good choice for users who require scalability and high performance. We therefore do not recommend using this example to set up a production environment without modifying it, e.g. to connect it to an existing MariaDB database instance.

    +
    +

    Always change PHOTOPRISM_ADMIN_PASSWORD so that the app starts with a secure initial password. +Never use easy-to-guess passwords or default values like insecure on publicly accessible servers. +There is no default in case no password was provided. A minimum length of 8 characters is required.

    +
    +

    Commands on Linux may have to be prefixed with sudo when not running as root. +Note that this will point the home directory shortcut ~ to /root in volume mounts. +Kernel security modules such as AppArmor and SELinux have been reported to cause +issues.

    +

    When the app has been started, open the Web UI by navigating to http://localhost:2342/. You should see a login screen. +Sign in with the user admin and the password configured via PHOTOPRISM_ADMIN_PASSWORD. +You may change it on the account settings page. +Enabling public mode will disable authentication.

    +
    +

    It can be helpful to keep Docker running in the foreground while debugging +so that log messages are displayed directly. To do this, omit the -d parameter when restarting.

    +

    Should the server already be running, or you see no errors, you may have started it +on a different host and/or port. There could also be an issue with your browser, +ad blocker, or firewall settings.

    +
    +
    +

    You cannot change the password with PHOTOPRISM_ADMIN_PASSWORD after the app has been started for the first time. To change the admin password, run the docker exec -ti photoprism photoprism passwd [username] command in a terminal. You can also run docker exec -ti photoprism photoprism reset to delete the existing index database and start from scratch.

    +
    +

    Volumes

    +

    Since the app is running inside a container, you have to explicitly mount the host folders you want to use. +PhotoPrism won't be able to see folders that have not been mounted. That's an important security feature.

    +
    /photoprism/originals
    +

    The originals folder contains your original photo and video files. They are mounted from ~/Pictures in the example +above, where ~ is a shortcut for your home directory.

    +

    You may mount any folder accessible from the host instead, +including network drives. Additional directories can +be mounted as sub folders of /photoprism/originals:

    +
    -v ~/Example:/photoprism/originals/Example
    +
    +
    +

    If read-only mode is enabled, all features that require write permission to the originals folder +are disabled, e.g. WebDAV, uploading and deleting files. Run the app with -e PHOTOPRISM_READONLY="true" +for this. You can mount a folder with the :ro flag to make Docker block write operations as well.

    +
    +
    /photoprism/storage
    +

    SQLite, config, cache, backup, thumbnail and sidecar files are saved in the storage folder:

    +
      +
    • a storage folder must always be mounted so that you do not lose these files after a restart or upgrade
    • +
    • never configure the storage folder to be inside the originals folder unless the name starts with a . to indicate that it is hidden
    • +
    • we recommend placing the storage folder on a local SSD drive for best performance
    • +
    • mounting symbolic links or using them inside the storage folder is currently not supported
    • +
    +

    Using our example, an anonymous volume is created and mounted as storage folder. You can mount a specific host folder instead, just as with originals, which is better for production environments.

    +
    +

    Should you later want to move your instance to another host, the easiest and most time-saving way is to copy the entire storage folder along with your originals and database.

    +
    +
    /photoprism/import
    +

    You can optionally mount an import folder from which files can be transferred to the originals folder +in a structured way that avoids duplicates:

    +
      +
    • imported files receive a canonical filename and will be organized by year and month
    • +
    • never configure the import folder to be inside the originals folder, as this will cause a loop by importing already indexed files
    • +
    +
    +

    You can safely skip this. Adding files via Web Upload +and WebDAV remains possible, unless read-only mode +is enabled or the features have been disabled.

    +
    +

    Step 2: First steps

    +

    Our First Steps 👣 tutorial guides you through the user interface and settings to ensure your library is indexed according to your individual preferences.

    + + +

    Easy, isn't it?

    +

    Step 3: When you're done...

    +

    You can stop PhotoPrism and start it again using the following commands:

    +
    docker stop photoprism
    +docker start photoprism
    +
    +

    To remove the container completely:

    +
    docker rm -f photoprism
    +
    +

    PhotoPrism® Plus

    +

    Our members can activate additional features by logging in with the admin user created during setup and then following the steps described in our activation guide. Thank you for your support, which has been and continues to be essential to the success of the project!

    +

    Compare Memberships › View Membership FAQ ›

    +
    +

    We recommend that new users install our free Community Edition before signing up for a membership.

    +
    +

    Troubleshooting

    +

    If your server runs out of memory, the index is frequently locked, or other system resources are running low:

    + +

    Other issues? Our troubleshooting checklists help you quickly diagnose and solve them.

    +
    +

    You are welcome to ask for help in our community chat. +Sponsors receive direct technical support via email. +Before submitting a support request, try to determine the cause of your problem.

    +
    +

    Command-Line Interface

    +

    Introduction

    +

    photoprism help lists all commands and config options available in the current version:

    +
    docker exec -ti photoprism photoprism help
    +
    +

    Use the --help flag to see a detailed command description, for example:

    +
    docker exec -ti photoprism photoprism backup --help
    +
    +

    PhotoPrism's command-line interface is also well suited for job automation using a +scheduler.

    +
    +

    When using Docker, you can prepend commands like docker exec -ti [container] [command] to run them in a container. Should this fail with no container found, make sure the container has been started and you have specified an existing container name or id.

    +
    +

    Opening a Terminal

    +

    To open a terminal session, you can run the following (replace $UID with the user ID to be used or omit the -u flag altogether to open the terminal as root):

    +
    docker exec -ti -u $UID photoprism bash
    +
    +

    Passing the -ti flag is important for interactive commands to work, for example if you need to confirm an action.

    +

    Changing the User ID

    +

    Specifying a user with the -u flag is possible for all commands you run with Docker. In the following examples, it is omitted for brevity. +Note, however, that commands that you run without an explicit user ID might be executed as root. +The currently supported user ID ranges are 0, 33, 50-99, 500-600, 900-1250, and 2000-2100.

    +

    Examples

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ActionCommand
    Start PhotoPrismdocker start photoprism
    Stop PhotoPrismdocker stop photoprism
    Download Updatedocker pull photoprism/photoprism:latest
    Uninstalldocker rm -f photoprism
    View Logsdocker logs --tail=100 -f photoprism
    Display Config Valuesdocker exec -ti photoprism photoprism show config
    Show Migration Statusdocker exec -ti photoprism photoprism migrations ls
    Repeat Failed Migrationsdocker exec -ti photoprism photoprism migrations run -f
    Reset Databasedocker exec -ti photoprism photoprism reset --yes
    Backup Databasedocker exec -ti photoprism photoprism backup -a -i
    Restore Databasedocker exec -ti photoprism photoprism restore -a -i
    Change Passworddocker exec -ti photoprism photoprism passwd [username]
    Show User Management Commandsdocker exec -ti photoprism photoprism users help
    Reset User Accountsdocker exec -ti photoprism photoprism users reset --yes
    Reset Sessions and Access Tokensdocker exec -ti photoprism photoprism auth reset --yes
    Show Face Recognition Commandsdocker exec -ti photoprism photoprism faces help
    Index Facesdocker exec -ti photoprism photoprism faces index
    Reset People & Facesdocker exec -ti photoprism photoprism faces reset -f
    Transcode Videos to AVCdocker exec -ti photoprism photoprism convert
    Regenerate Thumbnailsdocker exec -ti photoprism photoprism thumbs -f
    Update Indexdocker exec -ti photoprism photoprism index --cleanup
    Move to Originalsdocker exec -ti photoprism photoprism import [path]
    Copy to Originalsdocker exec -ti photoprism photoprism cp [path]
    +

    You can alternatively use podman as a drop-in replacement for docker on Red Hat-compatible distributions.

    +
    +

    Complete Rescan

    +

    docker exec -ti photoprism photoprism index -f rescans all originals, including already indexed and unchanged files. +This may be necessary after major upgrades and after migrations of the database schema, especially if search results are missing or incorrect. Note You can also start a rescan from the user interface by navigating to Library > Index, checking "Full Rescan" and then clicking "Start". Manually entered information such as labels, people, titles or descriptions will not be modified when indexing, even if you perform a "complete rescan".

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/faq/index.html b/getting-started/faq/index.html new file mode 100644 index 0000000000..5f2149f953 --- /dev/null +++ b/getting-started/faq/index.html @@ -0,0 +1,9128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FAQ - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Frequently Asked Questions

    +

    What media file types are supported?

    +

    PhotoPrism supports indexing, viewing, and converting most popular image, video and RAW formats, including JPEG, PNG, GIF, BMP, HEIF, HEIC, MP4, MOV, WebP, and WebM. TIFF is partially supported without extensions such as GeoTIFF.

    +

    When indexing, a JPEG or PNG sidecar file is automatically created for videos and images in other formats, such as RAW or vector graphics. It is needed for thumbnail generation, image classification, and face detection. JPEG XL support is planned as soon as it is generally available and enough compatible tools exist.

    +

    If installed, converting RAW files is possible with the following converters (our Docker image includes both):

    + +

    On a Mac, RAW files can also be converted with Sips (supported cameras). +Our goal is to provide top-notch support for all RAW formats, regardless of camera make and model. +Please let us know about any issues with a particular camera or file format.

    +

    For maximum browser compatibility, video codecs and containers supported by FFmpeg can be transcoded to MPEG-4 AVC on demand, just as still images can be extracted for thumbnail creation.

    +

    Make sure you have JSON sidecar files enabled if you have videos, live photos, and/or animated GIFs so that video-specific metadata such as codec, frames, and duration can be extracted, indexed, and searched.

    +

    For a complete list of file formats and extensions, see our downloadable Feature Overview.

    +
    +

    In case FFmpeg is disabled or not installed, videos cannot be indexed because still images cannot be created. +You should also have ExifTool enabled to extract metadata such as duration, resolution, and codec.

    +
    +

    What are sidecar files and where do I find them?

    +

    A sidecar is a file that sits next to your main photo or video files and usually has the same name +but a different extension:

    +
      +
    • IMG_0123.mov
    • +
    • IMG_0123.mov.jpg
    • +
    • IMG_0123.json
    • +
    +

    New sidecar files are saved in the storage folder by default, so the originals folder can be mounted read-only.

    +
    +

    Even if PHOTOPRISM_DISABLE_EXIFTOOL is set to “true” or PHOTOPRISM_SIDECAR_YAML is set to “false”, the indexer will look for existing sidecar files and use them.

    +
    +

    What metadata sidecar file types are supported?

    +

    Currently, three types of file formats are supported:

    +

    JSON

    +

    If not disabled via PHOTOPRISM_DISABLE_EXIFTOOL or --disable-exiftool, ExifTool is used +to automatically create a JSON sidecar for each media file. In this way, embedded XMP and video metadata can also be indexed. +Native metadata extraction is limited to common Exif headers. Note that this causes small amount of overhead when +indexing for the first time.

    +

    JSON files can also be useful for debugging, as they contain the full metadata and can be processed with common +development tools and text editors.

    +
    +

    JSON files exported from Google Photos can be read as well. Support for more schemas may be added over time.

    +
    +

    YAML

    +

    Unless disabled by setting the PHOTOPRISM_SIDECAR_YAML option to "false" in your configuration, PhotoPrism automatically creates/updates human-friendly YAML sidecar files during indexing and after manual editing of fields such as title, date, or location. They serve as a backup in case the database (index) is lost, or when folders are synchronized with a remote instance.

    +

    Like JSON, YAML files can be opened with common development tools and +text editors. However, changes are not synchronized with the original index, as this could overwrite existing data.

    +

    XMP

    +

    XMP (Extensible Metadata Platform) is an XML-based metadata container format developed by Adobe. +It provides many more fields (as part of embedded models like Dublin Core) than Exif. This also makes it difficult - if not +impossible - to provide full support. Reading title, copyright, artist, and description from XMP sidecar files is +implemented as a proof-of-concept, contributions are welcome. Indexing of +embedded XMP is only possible via ExifTool, see above.

    +

    Does your software depend on any external services?

    +

    As explained in our Privacy Policy, reverse geocoding and interactive world maps depend on retrieving the necessary information from us and MapTiler AG, headquartered in Switzerland. Both services are provided with a very high level of privacy and confidentiality.

    +

    Your use of these services is fully covered by us. Depending on your usage, this can save you much more than the cost of a PhotoPrism+ Membership, since other providers generally charge usage-based fees and often don't allow you to cache the data they provide, compromising performance and your privacy with unnecessary requests.

    +

    View Privacy Policy › View Compliance FAQ ›

    +

    In order to successfully set up your installation and view location details in PhotoPrism, you must allow incoming requests as well as those to our Geocoding API and Docker if you have a firewall installed, and make sure that your Internet connection is working:

    +

    +

    Why do I see connection errors when requesting API keys at startup?

    +

    Retrieving location data with reverse geocoding and loading the interactive world maps we provide requires a connection to external services. Please make sure that requests to these API endpoints are allowed if you have a firewall installed, and that your internet connection is working.

    +

    Learn more ›

    +

    Are the keys for using interactive world maps provided free of charge?

    +

    All users have access to a high-resolution vector map that we host on our own infrastructure, so no commercial API key is required. It is based on data published by OpenStreetMap (OSM).

    +

    In addition, we automatically provide our members and business customers with an API key for MapTiler's commercial service, which includes satellite, outdoor and 3D maps. You can test these on our public demo.

    +

    Learn more ›

    +
    +

    Although experienced users could alternatively register test accounts with a commercial provider to gain access to additional map styles instead of signing up for a membership, we believe this would not be fair. Keep in mind that we have a much larger user base than others who might encourage their users to do so, and that providers might then stop offering free test accounts, which is something we don't want to be responsible for.

    +
    +

    Why don't you use the free map tile service provided by OpenStreetMap?

    +

    Other free and open-source software sometimes uses the public maps that OpenStreetMap provides for development and testing. These are not intended for end-user applications like ours.

    +

    Using their service also means that their usage and privacy policies apply, as your request data is stored and used to generate publicly available reports. This differs from our services, which ensure a high level of privacy and provide a better user experience with faster loading times.

    +

    How can I activate my membership?

    +

    To connect a new instance to your membership account, you will need to log in with the super admin user that is automatically created during setup (see your compose.yaml or docker-compose.yml file or the app store documentation), and then follow the steps described in our activation guide.

    +

    View Activation Guide ›

    +

    What are the advantages of purchasing a commercial license?

    +

    A key difference between the public license and a commercial license agreement is that you get access to additional support and configuration options, as well as the right to customize functionality to your needs without having to publicly disclose your changes. Our Compliance FAQ gives answers to the most frequently asked questions about product compliance and scalability.

    +

    Compare Team Editions ›

    +

    Will the self-hosted version continue to be supported?

    +

    Absolutely! We are on a mission to protect your freedom and privacy. Self-hosting is the easiest way to stay in control and protect your privacy. It also provides the best experience for advanced users who often rely on a local toolchain to select, edit, and publish their pictures.

    +

    At the same time, we know there's a huge demand and many practical uses for a cloud-hosted app that is easy to set up. We like to give our users the choice and therefore offer a fully managed service as a deployment option. Selected hosting partners ensure that your privacy is protected as much as technically possible, even in the cloud.

    + +

    JPEGs are currently not regenerated when related RAW or XMP files change. RAW files are digital negatives by design. +PhotoPrism therefore assumes that their image information is immutable.

    +

    XMP files can affect the appearance, but most of the metadata they contain, such as title and description, does not. +Creating JPEGs from RAW files is a time-consuming task, and in most cases would cause a huge, unjustified amount of +overhead. In addition, the rendering information in XMP files is not well standardized. For example, changes you make +in Photoshop may not be compatible with Darktable.

    +

    We recommend manually updating existing JPEG sidecar files as needed or creating additional JPEGs, so you can choose +between different versions. New files and other metadata changes are detected and reflected in the index as usual when +your library is scanned.

    +

    Which folder will be indexed?

    +

    This depends on your environment and configuration. While sub folders can be selected for indexing +in the UI, changing the originals base folder requires a restart for security reasons.

    +

    If you skip configuration and don't use one of our Docker images, PhotoPrism will attempt to find a photo library +by searching a list of common folder names +such as /photoprism/originals and ~/Pictures. It also searches for other resources such as external applications, +classification models, and frontend assets.

    +

    If you use our Docker Compose example without modifications, pictures will be +mounted from ~/Pictures where ~ is a shortcut for your home directory:

    +
      +
    • \user\username on Windows
    • +
    • /Users/username on macOS
    • +
    • and /root or /home/username on Linux
    • +
    +

    Since the app is running inside a container, you have to explicitly mount the host folders you want to use. +PhotoPrism won't be able to see folders that have not been mounted. Multiple folders can be made accessible +by mounting them as sub folders of /photoprism/originals, for example:

    +
    volumes:
    +  - "/home/username/Pictures:/photoprism/originals"
    +  - "/example/friends:/photoprism/originals/friends"
    +  - "/mnt/photos:/photoprism/originals/media"
    +
    +

    Can I use FAT32 and ExFAT formatted drives?

    +

    Photos and videos can be mounted from FAT-formatted drives, such as an external SSD. Our tests have shown that PhotoPrism and MariaDB can also be started from there. However, at least on macOS, the logs may occasionally show directory access errors and you will be forced to restart if problems occur.

    + +

    PhotoPrism depends on a number of other open source tools and applications, such as Darktable, RawTherapee, and FFmpeg. While you can install them directly on Windows, it's a lot of work and we don't have the capacity to test the respective Windows versions before each release.

    +

    We therefore recommend to use Docker, so you can take advantage of our pre-built and QA-tested Docker image, which includes all the dependencies you need. +It is a well-tested standard tool that also lets you run many other self-hosted apps without having to worry about the details or Windows-specific issues. +To further simplify the setup for you, we offer a batch script that you can run in the directory where you want to install PhotoPrism:

    +
    curl.exe -o install.bat https://dl.photoprism.app/docker/windows/install.bat
    +install.bat
    +
    +

    This will automatically download all required config files and start the server for you. Before you run the script, make sure you have Docker Desktop installed on your Windows PC.

    +

    How can I install PhotoPrism without Docker?

    +

    Installation Packages

    +

    Experienced users can use the packages available at dl.photoprism.app/pkg/linux/ to install PhotoPrism on compatible Linux distributions, e.g. by running the following commands:

    +
    sudo mkdir -p /opt/photoprism
    +cd /opt/photoprism
    +wget -c https://dl.photoprism.app/pkg/linux/amd64.tar.gz -O - | sudo tar -xz
    +sudo ln -sf /opt/photoprism/bin/photoprism /usr/local/bin/photoprism
    +photoprism --version
    +
    +

    Note that these packages must be updated manually, do not come with a default configuration, and do not include the system dependencies required to make use of all the features. The minimum required glibc version is 2.35, so for example Ubuntu 22.04 and Debian Bookworm will work, but older Linux distributions may not be compatible.

    +

    Learn more ›

    +

    Arch Linux Packages

    +

    Thomas Eizinger maintains AUR packages for installation on Arch Linux. These are based on the pre-built installation packages we provide and have a systemd integration so that PhotoPrism can be started and restarted automatically.

    +

    Learn more ›

    +

    LXC Images

    +

    There are currently no official LXC images available from us. However, you can use our installation packages together with the documentation we provide to set them up in a base image of your choice.

    +

    Since Docker and LXC are pretty much the same technology, you can also convert our Docker image to the LXC format, e.g. with the following commands:

    +
    apt update
    +apt install lxc umoci skopeo
    +lxc-create photoprism -t oci -- --url docker://photoprism/photoprism:latest
    +
    +

    PhotoPrism can then be configured and started like any other LXC container:

    +
    lxc-start --name=photoprism -- /opt/photoprism/bin/photoprism start
    +
    +

    Please note, though, that the network, storage and database configuration requires detailed knowledge of LXC. We therefore only recommend this approach if you can complete the setup without help from our documentation or support from our team.

    +

    BSD Ports

    +

    For FreeBSD and TrueNAS CORE (formerly FreeNAS) users, an unofficial port is available that builds PhotoPrism from source. It will also compile and install the required TensorFlow libraries for you.

    +

    Building From Source

    +

    You can alternatively build and install PhotoPrism from the publicly available source code, which includes all the Community Edition features and most of the Essentials features (except additional user roles):

    +
    git clone https://github.com/photoprism/photoprism.git
    +cd photoprism
    +make all install DESTDIR=/opt/photoprism
    +
    +

    When choosing this installation method, missing build and system dependencies must be installed manually, as shown in our human-readable and versioned Dockerfiles. Since you often don't need to use the exact same versions, you can replace most packages with those available in your environment.

    +

    Please be aware, though, that we do not have the resources to provide support and special dependencies, such as TensorFlow libraries, to private users who choose to build from source. If possible, we recommend using Docker Compose or the installation packages we provide, as they can save a lot of time creating and troubleshooting custom builds.

    +
    +

    PhotoPrism Plus

    +

    If you are a Plus, Silver, Gold or Platinum member and would like to build from source, please let us know so we can give you access to our private extension repository and provide assistance.

    +
    +

    What are the benefits of using Docker?

    +

    (1) Docker uses standard features of the Linux kernel. Containers are nothing new; Solaris Zones were released about 20 years ago and the chroot system call was introduced during development of Version 7 Unix in 1979. It is used ever since for hosting applications exposed to the public Internet. Modern Linux containers are an incremental improvement of this, based on standard functionality that is part of the kernel.

    +

    (2) Docker saves time through simplified deployment and testing. A main advantage of Docker is that application images can be easily made available to users via Internet. It provides a common standard across most operating systems and devices, which saves our team a lot of time that we can then spend more effectively, for example, providing support and developing one of the many features that users are waiting for.

    +

    (3) Dockerfiles are part of the source code repository. Human-readable and versioned Dockerfiles that are part of our public source code help avoid "works for me" moments and other unwelcome surprises by enabling us to have the exact same environment everywhere in development, staging, and production.

    +

    (4) Running applications in containers is more secure. Last but not least, virtually all file format parsers have vulnerabilities that just haven't been discovered yet. This is a known risk that can affect you even if your computer is not directly connected to the Internet. Running apps in a container with limited host access is an easy way to improve security without compromising performance and usability.

    +
    +

    A virtual machine with a dedicated operating system environment provides even more security, but usually has side effects such as lower performance and more difficult handling. Using a VM, however, doesn't prevent you from running containerized apps to get the best of both worlds. This is essentially what happens when you install Docker on virtual cloud servers and operating systems other than Linux.

    +
    +

    Why does your Docker image use the Plus License instead of the AGPL?

    +

    Our Plus License is used for both the extensions we provide to our members and the standard Docker images available on Docker Hub. This allows us to bundle the extensions with the compiled application, while the Community Edition remains freely available under the terms of the GNU Affero General Public License (AGPL).

    +

    If you don't plan to use any additional features, you can alternatively use the "ce" tag instead of "latest" to get a slightly smaller Docker image distributed under the AGPL. Note that system dependencies and other third-party components included in this image are still subject to additional terms and conditions.

    +

    View Open Source FAQ › View Plus License ›

    +

    Should I use SQLite, MariaDB, or MySQL?

    +

    PhotoPrism is compatible with SQLite 3 and MariaDB 10.5.12+. Official support for MySQL 8 has been discontinued as Oracle seems to have stopped shipping new features and enhancements.

    +

    If you only have few pictures, concurrent users, and CPU cores, SQLite may seem faster compared to full-featured database servers like MariaDB. This changes as the index grows and the number of concurrent accesses increases. While MariaDB is optimized for high concurrency, SQLite frequently locks its index so that other operations have to wait. In the worst case, this can lead to locking errors and timeouts during indexing - especially in combination with a slow disk or network storage.

    +

    The main advantage of SQLite is that you don't need to run a separate database server. It is therefore well suited for testing and can also be sufficient for small libraries with a few thousand files. If you are looking for scalability and high performance, it is not a good choice.

    +

    Is database corruption a common problem with self-hosting?

    +

    The likelihood of database corruption is generally very low if you follow our documentation. Our team runs many instances/databases and has never had any issues over the years.

    +

    However, if you run MariaDB or SQLite on a network drive or an external drive/stick that e.g. has been accidentally removed, it can happen. This is why our documentation explicitly warns about the danger of using unreliable storage for database files.

    +

    Some users also configure a named or anonymous Docker volume for the database, or mount the wrong path so that their index is lost when they recreate the database container, e.g. after an update of the Docker image.

    +

    I've configured an external database, but can't connect?

    +

    Most often this happens when new users configure localhost or 127.0.0.1 as database server host, since these always point back to the current container or computer. So it is not possible to access an external service with such a hostname or an IP address starting with 127. It works only if it is used directly in the container or on the computer where the database server is running. Instead, you must use a hostname or IP address that is accessible from other machines and containers.

    +

    Resolve Connection Issues ›

    +

    How can I determine the public IP address of my home network?

    +

    You can use one of the following services to view your public IP address, i.e. the IP address that the computers in your home network use to communicate with the Internet:

    + +

    Why is my configured memory limit exceeded when indexing, even though PhotoPrism doesn't actually seem to use that much memory?

    +

    When indexing a media library, many files are opened and processed very quickly, which is not a typical workload compared to other containerized applications and services. Various libraries and external applications simultaneously interact with each other in complex ways, so a few spikes are inevitable. Some memory is also used by the kernel for buffered I/O to improve performance, although the extent to which caching counts towards a limit may vary.

    +

    We therefore recommend not to set a hard memory limit, unless you are familiar with memory management and understand the implications. Instead, you should reduce the number of indexing workers and limit file size and resolution if you are low on resources or want to limit memory usage for other reasons. Also make sure you have at least 4 GB of swap configured.

    +

    View System Requirements › Get Performance Tips ›

    +

    Why does PhotoPrism always consume 100% of CPU when the background worker is running?

    +

    Many users reporting poor performance and high CPU load have migrated from SQLite to MariaDB so that their database schema is not optimized for performance, for example, because indexes are missing or columns have the wrong data type. The instructions for these migrations were provided by a contributor and are not part of the original software distribution. As such, they have not been officially released, recommended, or extensively tested by us.

    +

    In some instances, users have manually changed the contents of the database. It is also possible that the database is in an inconsistent state for other reasons, e.g. due to bugs in previous versions that have been fixed in the meantime. However, we are not currently aware of any such cases.

    +

    Due to the amount of time required to review each report, we can only offer this to eligible members and business customers, and not to users who have chosen our free community edition.

    +

    Get Performance Tips › View Database Schema ›

    +

    Can you improve performance when using older or otherwise slow hardware?

    +

    It is a known issue that the user interface and backend operations, especially face recognition, can be slow or even crash on older hardware due to a lack of resources. Like most applications, PhotoPrism has certain requirements and our development process does not include testing on unsupported or unusual hardware.

    +

    In many cases, performance can be improved through optimizations. Since these can prove to be very time-consuming and cost-intensive in practice, users and developers must decide on a case-by-case basis whether this provides sufficient benefit in relation to the costs or whether the use of more powerful hardware is faster and cheaper overall.

    +

    We kindly ask you not to open a problem report on GitHub Issues for poor performance on older hardware until a full cause and feasibility analysis has been performed. GitHub Discussions or any of our other public forums and communities are great places to start a discussion.

    +

    That being said, one of the advantages of open-source software is that users can submit pull requests with performance and other enhancements they would like to see implemented. This will result in a much faster solution than waiting for a core team member to remotely analyze your problem and then provide a fix.

    +

    Is a Raspberry Pi fast enough?

    +

    This mainly depends on your expectations and the number of files you have. Most users report that PhotoPrism runs smoothly on a Raspberry Pi 4 with 4 GB of RAM.

    +

    Note, however, that initial indexing usually takes much longer than on a regular desktop computer and that the hardware has limited video transcoding capabilities, so video file format conversion is not well supported and software transcoding is generally slow. We take no responsibility for instability or performance problems if your device does not meet the requirements.

    +

    Should I use an SD card or a USB stick?

    +

    Due to their performance and because they can lose data over time, we do not recommend using conventional SD cards, USB sticks or external USB 2 hard disk drives to store your files, except for backups.

    +

    External Solid-State Drives (SSD) connected via USB 3 are generally reliable and fast enough to keep your originals, database, and storage folders. This way you can, for example, do the indexing on one computer, eject the drive, and then connect it to another computer to browse your pictures.

    +

    Note, though, that database files may not be binary compatible in some cases (e.g. if the version or computer architecture does not match) and could also get corrupted when you disconnect an external drive before all changes have been written to disk. We therefore recommend that you regularly create database backups, so you can easily restore your index if necessary.

    +

    View Backup Guide ›

    +

    Why don't you display animated GIFs natively?

    +

    Support for animated GIFs was added in April 2022.

    +

    Why is my storage folder so large? What is in it?

    +

    The storage folder contains sidecar, cache, and configuration files. It may also contain index database files if you are using SQLite. Most of the space there is taken up by your thumbnails: These are high-quality, scaled-down versions of your originals. Thumbnails are necessary because web browsers are bad at resizing large images to fit the screen. Using full-resolution originals for slideshows and in search results would also consume a lot of browser memory and significantly reduce indexing performance.

    +

    We are working to implement storage optimizations whenever there is an opportunity. It is also possible to increase the JPEG compression and/or limit the resolution if you are happy with lower quality thumbnails.

    +

    To free up as much space as possible, the most effective way is to delete all files in the /cache/thumbnails storage folder. It is located outside the originals folder by default, depending on your configuration. Then perform a full rescan of your library or run the command photoprism thumbs -f in a terminal if you have direct server access. This command can also be used to replace existing thumbnails, for example after changing the quality settings. Higher resolution thumbnails cannot be automatically removed at this time.

    +

    If you have a fast CPU and enough memory, you can choose to render certain thumbnails only on demand. However, storage is usually so cheap that most users opt for better quality and performance instead.

    +

    Actual storage requirements vary and depend, among other things, on file resolutions and formats (RAW, JPEG, video,...). For highly compressed, high-resolution videos in modern formats that cannot be displayed natively by browsers, the storage folder may even be larger than the originals, since videos transcoded to AVC are not as heavily compressed.

    +

    Change Settings ›

    +

    Can I skip creating thumbnails completely?

    +

    The smallest configurable size is 720px for use by the indexer to perform color detection, image classification, as well as face detection and recognition. Recreating them every time they are needed is too demanding even for the most powerful servers. Unless you have just a few small pictures, this would make the app unusable.

    +
    +

    Reducing the Static Size Limit of thumbnails has a significant impact on face recognition +and image classification results. Simply put, it means that the indexer can no longer see properly.

    +
    +

    When should I perform a complete rescan?

    +

    We recommend performing a complete rescan after major updates to take advantage of new search filters and sorting options. Be sure to read the notes for each release to see what changes have been made and if they might affect your library, for example, because of the file types you have or because new search features have been added. If you encounter problems that you cannot solve otherwise (i.e. before reporting a bug), please also try a rescan and see if it solves the problem.

    +

    You can start a rescan from the user interface by navigating to Library > Index, selecting "Complete Rescan", and then clicking "Start". Manually entered information such as labels, people, titles or descriptions will not be modified when indexing, even if you perform a "complete rescan". Be careful not to start multiple indexing processes at the same time, as this will lead to a high server load.

    +

    How can I shorten the startup time after a restart or update?

    +

    To reduce startup time, do not set PHOTOPRISM_INIT to avoid running additional setup scripts, and set PHOTOPRISM_DISABLE_CHOWN to "true" to disable automatic permission updates.

    +

    View Config Options ›

    +
    +

    If your instance doesn't start even after waiting for some time, our Troubleshooting Checklists help you quickly diagnose and solve the problem.

    +
    +

    Why are files uploaded via WebDAV not indexed/imported immediately?

    +

    PHOTOPRISM_AUTO_INDEX and PHOTOPRISM_AUTO_IMPORT let you specify how long PhotoPrism should wait before indexing or importing newly uploaded files. The default setting is 300 seconds, or 5 minutes. This is a safety mechanism for users with slow uploads to avoid incomplete file sets, for example when uploading pictures with sidecar files. You can therefore reduce the delay if you have a fast connection and usually do not upload stacks of related files such as RAW images with sidecar JPEG and XMP files.

    +

    In some cases, it is also possible that the index is already being updated, so you will have to wait until the process is complete before indexing new files.

    +

    I'm having issues understanding the difference between the import and originals folders?

    +

    You may optionally mount an import folder from which files can be transferred to the originals folder +in a structured way that avoids duplicates. Imported files receive a canonical filename and will be +organized by year and month.

    +

    Most users with existing photo libraries will want to index their originals folder directly +without importing files, leaving the existing file and folder names unchanged. On the other hand +importing is an efficient way to add files, since PhotoPrism doesn't have to search your originals +folder to find new files.

    +

    View First Steps 👣 ›

    +

    Can I use PhotoPrism to sort files into a configurable folder structure?

    +

    You have complete freedom in how you organize your originals. If you don't like the unique names and +folders used by the import function, you can resort to external batch renaming tools, for example +ExifTool, +PhockUp, +or Photo Organizer.

    +

    Configurable import folders may be available in a later version. This is because - depending on the specific +pattern - appropriate conflict resolution is required and the patterns must be well understood and validated +to avoid typos or other misconfigurations that lead to undesired results for which we do not want to be responsible.

    +

    Why is only the logo displayed when I open the app?

    +

    This may happen when the server cannot be reached, for example, because a proxy is misconfigured, +JavaScript is disabled in your browser, an ad blocker is blocking requests, or you are using an incompatible browser.

    +

    We recommend going through the checklist provided and to verify that +your browser meets the system requirements.

    +

    Why is PhotoPrism getting stuck in a restart loop?

    +

    This happens when Docker was configured to automatically restart services after failures.

    +

    We recommend going through the checklist for fatal server errors and to verify that +your computer meets the system requirements.

    +

    Can I install PhotoPrism in a sub-directory on a shared domain?

    +

    Setting up PhotoPrism behind a reverse proxy in a sub-directory on a shared domain is possible in principle. This method is experimental, however, and not generally recommended because a number of detailed issues remain to be addressed and technical expertise is required.

    +

    I could not find a documentation of config parameters?

    +

    We maintain a complete list of config options in Getting Started. +When you run photoprism help in a terminal, +all commands and parameters available in your currently installed version +are listed:

    +
    docker compose exec photoprism photoprism help
    +
    +

    Our Docker Compose examples are continuously +updated and inline documentation has been added to simplify installation.

    +

    What exactly does the read-only mode?

    +

    When you enable read-only mode, all features that require write permission to the originals folder +are disabled, for example import, upload, and delete. Set PHOTOPRISM_READONLY to "true" +in docker-compose.yml for this. You can mount a folder with the :ro flag to make Docker block +write operations as well.

    +

    In which cases could files in the originals folder get modified?

    +

    PhotoPrism generally does not write to the originals folder, with the following exceptions: (1) You rotate an image in the user interface, so its Exif header must be updated. (2) You unstack files that were stacked based on their name, so they must be renamed. (3) You add files using the import functionality or the web upload. (4) You manually delete files in the user interface. (5) You have configured the originals folder as your sidecar folder. (6) You access the originals folder with a WebDAV client to manage your files without having read-only mode enabled.

    +

    How can I uninstall PhotoPrism?

    +

    This depends on how you installed it. If you're running PhotoPrism with Docker Compose, +this command will stop and remove the Docker container:

    +
    docker compose rm -s -v
    +
    +

    Please refer to the official Docker documentation +for further details.

    +

    How can I mount network shares with Docker?

    +

    Shared folders that have already been mounted on your host under a drive letter or path can be used with Docker containers like any other directory. +In addition, certain types of network storage like NFS (Unix/Linux) and CIFS (Windows/Mac) can also be mounted directly with Docker Compose.

    +

    For more information, see the Network Storage section of our Docker Troubleshooting Guide.

    +

    Learn more ›

    +

    Why does changing permissions using chmod not work for my network shares?

    +

    This is a common issue with NFS shares. For security reasons, the permissions must be changed on the server for them to take effect, unless the server allows them to be changed remotely, which depends on the settings. Even then, in the worst case, the actual permissions on the server and the effective ones on the clients may be different.

    +

    Learn more ›

    +

    Do you support Podman?

    +

    Podman works just fine both in rootless and under root. Mind the SELinux which is enabled on +Red Hat compatible systems, you may hit permission error problems.

    +

    More details on how to run PhotoPrism with Podman on CentOS in +this blog post, +it includes all the details including root and rootless modes, user mapping and SELinux.

    +

    Learn more ›

    +

    Do you have plans to add support for LDAP or Active Directory?

    +

    PhotoPrism offers support for secure single sign-on via OpenID Connect (OIDC). With our Pro Edition, you can also configure an LDAP or Active Directory server to authenticate users.

    +

    Learn more ›

    +

    Is it possible to set a default role for new OpenID Connect users?

    +

    For security reasons, our Personal Editions currently default to the Guest role, which admins can then upgrade after checking the eligibility of newly registered accounts.

    +

    Learn more ›

    +

    Can I configure a custom claim as the preferred OIDC username?

    +

    It is not possible to use a non-standard claim name such as username, as this can lead to conflicts and potential security issues, e.g. if the value is not unique or not reliably set.

    +

    Learn more ›

    +

    Who can I contact if I have a complaint about your software?

    +

    Please read this documentation and determine the cause of your problem before opening invalid, duplicate and/or incomplete bug reports, starting a public "shitstorm" or insulting other community members in our forums and chat rooms. Not only is this annoying for everyone, but it also keeps our team from working on features and improvements that our users are waiting for.

    +

    Learn more ›

    +
    +

    Professional Users

    +

    The feature set and support options of our Community Edition are intended for personal use. Enterprise users are welcome to contact us for a commercial license and professional services.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/first-steps/index.html b/getting-started/first-steps/index.html new file mode 100644 index 0000000000..2335e05df4 --- /dev/null +++ b/getting-started/first-steps/index.html @@ -0,0 +1,15 @@ + + + + + + Redirecting... + + + + + + +Redirecting... + + diff --git a/getting-started/freebsd/index.html b/getting-started/freebsd/index.html new file mode 100644 index 0000000000..e4e2896695 --- /dev/null +++ b/getting-started/freebsd/index.html @@ -0,0 +1,15 @@ + + + + + + Redirecting... + + + + + + +Redirecting... + + diff --git a/getting-started/img/docker-disable-wsl2.jpg b/getting-started/img/docker-disable-wsl2.jpg new file mode 100644 index 0000000000..9613de36f7 Binary files /dev/null and b/getting-started/img/docker-disable-wsl2.jpg differ diff --git a/getting-started/img/docker-resources-advanced.jpg b/getting-started/img/docker-resources-advanced.jpg new file mode 100644 index 0000000000..93d8cc4d4f Binary files /dev/null and b/getting-started/img/docker-resources-advanced.jpg differ diff --git a/getting-started/img/docker-resources-filesharing.jpg b/getting-started/img/docker-resources-filesharing.jpg new file mode 100644 index 0000000000..9fc992d2c7 Binary files /dev/null and b/getting-started/img/docker-resources-filesharing.jpg differ diff --git a/getting-started/img/letsencrypt.svg b/getting-started/img/letsencrypt.svg new file mode 100644 index 0000000000..4a09441b96 --- /dev/null +++ b/getting-started/img/letsencrypt.svg @@ -0,0 +1,38 @@ + + + + + Layer 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/img/omv_photoprism_plugin_ui.png b/getting-started/img/omv_photoprism_plugin_ui.png new file mode 100644 index 0000000000..4802cfa276 Binary files /dev/null and b/getting-started/img/omv_photoprism_plugin_ui.png differ diff --git a/getting-started/img/openmediavault.png b/getting-started/img/openmediavault.png new file mode 100644 index 0000000000..54736cbb83 Binary files /dev/null and b/getting-started/img/openmediavault.png differ diff --git a/getting-started/img/zerossl.svg b/getting-started/img/zerossl.svg new file mode 100644 index 0000000000..4d9aa282e8 --- /dev/null +++ b/getting-started/img/zerossl.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + diff --git a/getting-started/index.html b/getting-started/index.html new file mode 100644 index 0000000000..2d99e17979 --- /dev/null +++ b/getting-started/index.html @@ -0,0 +1,7794 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Setup - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Setup

    +

    PhotoPrism can be installed on all operating systems supporting Docker, as well as FreeBSD, Raspberry Pi, and many NAS devices. It is also available in the cloud on PikaPods and DigitalOcean.

    +

    We recommend running PhotoPrism with Docker Compose when hosting it on a private server. It is available for Mac, Linux, and Windows.

    +

    Once the initial setup is complete, our First Steps 👣 tutorial guides you through the user interface and settings to ensure your library is indexed according to your individual preferences.

    +
    +

    Our stable releases and preview builds are available as multi-arch Docker images for 64-bit AMD, Intel, and ARM processors. Experienced users can alternatively use the packages at dl.photoprism.app/pkg/linux/ to manually install PhotoPrism on compatible Linux distributions. For more installation methods, see our Getting Started FAQ.

    +
    +

    System Requirements

    +

    You should host PhotoPrism on a server with at least 2 cores, 3 GB of physical memory,1 and a 64-bit operating system. Beyond these minimum requirements, the amount of RAM should match the number of CPU cores. Indexing large photo and video collections also benefits greatly from local SSD storage, especially for the database and cache files.

    +

    Also ensure that your server has at least 4 GB of swap configured and avoid setting a hard memory limit as this can cause unexpected restarts when the indexer temporarily needs more memory to process large files. Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    +
    +

    We take no responsibility for instability or performance problems if your device does not meet the requirements.

    +
    +

    Databases

    +

    PhotoPrism is compatible with SQLite 3 and MariaDB 10.5.12+.2 Note that SQLite is generally not a good choice for users who require scalability and high performance, and that support for MySQL 8 has been discontinued due to low demand and missing features.3

    +

    Browsers

    +

    Built as a Progressive Web App (PWA), the web interface works with most modern browsers, and runs best on Chrome, Chromium, Safari, Firefox, and Edge. +You can conveniently install it on the home screen of all major operating systems and mobile devices.

    +

    Not all video and audio formats can be played with every browser. For example, AAC - the default audio codec for MPEG-4 AVC / H.264 - is supported natively in Chrome, Safari, and Edge, while it is only optionally supported by the OS in Firefox and Opera.

    +

    HTTPS

    +

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure HTTPS reverse proxy such as Traefik or Caddy. +Your files and passwords will otherwise be transmitted in clear text and can be intercepted by anyone, including your provider, hackers, and governments. Backup tools and file sync apps like FolderSync may refuse to connect as well.

    +

    Firewall

    +

    In order to successfully set up your installation and view location details in PhotoPrism, you must allow incoming requests as well as those to our Geocoding API and Docker if you have a firewall installed, and make sure that your Internet connection is working.

    +

    Configure Firewall ›

    +

    Maps & Places

    +

    As explained in our Privacy Policy, reverse geocoding and interactive world maps depend on retrieving the necessary information from us and MapTiler AG, headquartered in Switzerland. Both services are provided with a very high level of privacy and confidentiality.4

    +

    Your use of these services is fully covered by us. Depending on your usage, this can save you much more than the cost of a PhotoPrism+ Membership, since other providers generally charge usage-based fees and often don't allow you to cache the data they provide, compromising performance and your privacy with unnecessary requests.

    +

    View Privacy Policy › View Compliance FAQ ›

    +

    Roadmap

    +

    Our vision is to provide the most user- and privacy-friendly solution to keep your pictures organized and accessible. The project roadmap shows what tasks are in progress, what needs testing, and which features are going to be implemented next.

    +

    Please note, however, that we have a zero-bug policy and do our best to help users when they need support or have questions. This comes at a price, as we can't give exact release dates for new features.

    +

    Getting Support

    +

    If you need help installing our software at home, you are welcome to post your question in GitHub Discussions or ask in our Community Chat. +Common problems can be quickly diagnosed and solved using our Troubleshooting Checklists. Silver, Gold, and Platinum members are also welcome to email us for technical support and advice.

    +

    View Support Options › Compare Memberships ›

    +
    +

    We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app. +Contact us or a community member +if you need help, it could be a configuration problem, or a misunderstanding in how the software works.

    +
    +
    +
    +
      +
    1. +

      RAW image conversion and TensorFlow are disabled on systems with 1 GB or less memory 

      +
    2. +
    3. +

      Our configuration examples are generally based on the current stable MariaDB version to take advantage of performance improvements. This does not mean that older versions are no longer supported and you must upgrade immediately. We recommend not using the :latest tag for the MariaDB Docker image and to upgrade manually by changing the tag once we had a chance to test a new major version. 

      +
    4. +
    5. +

      Oracle seems to have stopped shipping new features and enhancements. As a result, the testing effort required before each release is no longer feasible. 

      +
    6. +
    7. +

      Our Compliance FAQ gives answers to the most frequently asked questions about product compliance and scalability. 

      +
    8. +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/nas/asustor/index.html b/getting-started/nas/asustor/index.html new file mode 100644 index 0000000000..884e875b58 --- /dev/null +++ b/getting-started/nas/asustor/index.html @@ -0,0 +1,7706 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Asustor - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Running PhotoPrism on an Asustor NAS

    +

    Before setting up PhotoPrism on your NAS, we recommend that you check the Asustor product database for the CPU and memory configuration of your device.

    +

    For a good user experience, it should be a 64-bit system with at least 2 cores and 3 GB of RAM. Indexing large photo and video collections also benefits greatly from using SSD storage, especially for the database and cache files.

    +
    +

    Third-party integrations may not provide direct access to config files or the command line, so you might not be able to use all features and config options. +Also note that RAW image conversion and TensorFlow are disabled on devices with 1 GB or less memory, and that high-resolution panoramic images may require additional swap space and/or physical memory above the recommended minimum. We take no responsibility for instability or performance problems if your device does not meet the requirements.

    +
    +

    Setup

    +

    This step-by-step guide explains how to set up a new PhotoPrism instance through App Central, the built-in app store.

    +

    Step 1: Open App Central

    +

    Log in to the user interface of your NAS. You can do this by navigating to https://asustor:7001 if you replace asustor with the actual IP address or hostname of your device and change the port depending on your configuration.

    +

    Now open "App Central" on the home screen:

    +

    Screenshot

    +

    Step 2: Install PhotoPrism

    +

    Type PhotoPrism in the search box in the upper right corner and press Enter to start the search. PhotoPrism should then be displayed so you can click "Install" to start the installation:

    +

    Screenshot

    +

    Next, you will be informed about dependencies like Docker that need to be installed, and you can decide whether you want your instance to be accessible from the Internet (if you have set this up for your NAS and your Internet router is compatible):

    +

    Screenshot

    +

    If you want to uninstall PhotoPrism later, you can also do that in App Central.

    +

    Step 3: Open PhotoPrism

    +

    Once the installation is complete, you will find PhotoPrism on your home screen, where you can open it in a new tab with one click:

    +

    Screenshot

    +

    You can also navigate to port 32770 on your device if you want to open PhotoPrism directly. +When you see the login screen, enter the username "admin" and password "admin321" to sign in:

    +

    Screenshot

    +

    Remember to change your password after the first login. You can do this in Settings > Account.

    +

    Step 4: Add Your Files

    +

    Our First Steps 👣 tutorial guides you through the user interface and settings to ensure your library is indexed according to your individual preferences.

    +

    Depending on which strategy you choose, you can add your media files to the originals or import folder located in /volume1/Docker/PhotoPrism/data:

    +

    Screenshot

    +

    The storage folder, which contains configuration, cache and sidecar files, can also be found there.

    +
    +

    Note that the folders that PhotoPrism uses cannot be dynamically configured at the moment when using this app store version. However, we are working to make this possible.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/nas/img/asustor/asustor-folder.jpg b/getting-started/nas/img/asustor/asustor-folder.jpg new file mode 100644 index 0000000000..f7cbcfe5c6 Binary files /dev/null and b/getting-started/nas/img/asustor/asustor-folder.jpg differ diff --git a/getting-started/nas/img/asustor/asustor-home.jpg b/getting-started/nas/img/asustor/asustor-home.jpg new file mode 100644 index 0000000000..4a34ea2cff Binary files /dev/null and b/getting-started/nas/img/asustor/asustor-home.jpg differ diff --git a/getting-started/nas/img/asustor/asustor-login.jpg b/getting-started/nas/img/asustor/asustor-login.jpg new file mode 100644 index 0000000000..1d74d66f99 Binary files /dev/null and b/getting-started/nas/img/asustor/asustor-login.jpg differ diff --git a/getting-started/nas/img/asustor/asustor-notes.jpg b/getting-started/nas/img/asustor/asustor-notes.jpg new file mode 100644 index 0000000000..b569d56505 Binary files /dev/null and b/getting-started/nas/img/asustor/asustor-notes.jpg differ diff --git a/getting-started/nas/img/asustor/asustor-step-1.jpg b/getting-started/nas/img/asustor/asustor-step-1.jpg new file mode 100644 index 0000000000..bc0436d062 Binary files /dev/null and b/getting-started/nas/img/asustor/asustor-step-1.jpg differ diff --git a/getting-started/nas/img/asustor/asustor-step-2.jpg b/getting-started/nas/img/asustor/asustor-step-2.jpg new file mode 100644 index 0000000000..765fbfca41 Binary files /dev/null and b/getting-started/nas/img/asustor/asustor-step-2.jpg differ diff --git a/getting-started/nas/img/asustor/asustor-step-3.jpg b/getting-started/nas/img/asustor/asustor-step-3.jpg new file mode 100644 index 0000000000..90e81fb21b Binary files /dev/null and b/getting-started/nas/img/asustor/asustor-step-3.jpg differ diff --git a/getting-started/nas/img/asustor/asustor-step-4.jpg b/getting-started/nas/img/asustor/asustor-step-4.jpg new file mode 100644 index 0000000000..61c4fa1da5 Binary files /dev/null and b/getting-started/nas/img/asustor/asustor-step-4.jpg differ diff --git a/getting-started/nas/img/ibracorp.jpg b/getting-started/nas/img/ibracorp.jpg new file mode 100644 index 0000000000..a25e51a837 Binary files /dev/null and b/getting-started/nas/img/ibracorp.jpg differ diff --git a/getting-started/nas/img/synology/Photoprism_1.jpg b/getting-started/nas/img/synology/Photoprism_1.jpg new file mode 100644 index 0000000000..ab882b83ee Binary files /dev/null and b/getting-started/nas/img/synology/Photoprism_1.jpg differ diff --git a/getting-started/nas/img/synology/Photoprism_2_en.jpg b/getting-started/nas/img/synology/Photoprism_2_en.jpg new file mode 100644 index 0000000000..7f9bb3185d Binary files /dev/null and b/getting-started/nas/img/synology/Photoprism_2_en.jpg differ diff --git a/getting-started/nas/img/synology/Photoprism_3_en.jpg b/getting-started/nas/img/synology/Photoprism_3_en.jpg new file mode 100644 index 0000000000..39aac633d2 Binary files /dev/null and b/getting-started/nas/img/synology/Photoprism_3_en.jpg differ diff --git a/getting-started/nas/img/synology/Photoprism_4_1_en.jpg b/getting-started/nas/img/synology/Photoprism_4_1_en.jpg new file mode 100644 index 0000000000..12fa4cec08 Binary files /dev/null and b/getting-started/nas/img/synology/Photoprism_4_1_en.jpg differ diff --git a/getting-started/nas/img/synology/Photoprism_4_2_en.jpg b/getting-started/nas/img/synology/Photoprism_4_2_en.jpg new file mode 100644 index 0000000000..d1ff175c22 Binary files /dev/null and b/getting-started/nas/img/synology/Photoprism_4_2_en.jpg differ diff --git a/getting-started/nas/img/synology/Photoprism_5_en.jpg b/getting-started/nas/img/synology/Photoprism_5_en.jpg new file mode 100644 index 0000000000..eadc136de4 Binary files /dev/null and b/getting-started/nas/img/synology/Photoprism_5_en.jpg differ diff --git a/getting-started/nas/img/synology/Photoprism_6_en.jpg b/getting-started/nas/img/synology/Photoprism_6_en.jpg new file mode 100644 index 0000000000..7b54c1c6ad Binary files /dev/null and b/getting-started/nas/img/synology/Photoprism_6_en.jpg differ diff --git a/getting-started/nas/img/synology/Photoprism_7_en.jpg b/getting-started/nas/img/synology/Photoprism_7_en.jpg new file mode 100644 index 0000000000..8c72052bda Binary files /dev/null and b/getting-started/nas/img/synology/Photoprism_7_en.jpg differ diff --git a/getting-started/nas/img/synology/Photoprism_8.jpg b/getting-started/nas/img/synology/Photoprism_8.jpg new file mode 100644 index 0000000000..ebcb2c20e0 Binary files /dev/null and b/getting-started/nas/img/synology/Photoprism_8.jpg differ diff --git a/getting-started/nas/openmediavault/index.html b/getting-started/nas/openmediavault/index.html new file mode 100644 index 0000000000..cbbbe45e3a --- /dev/null +++ b/getting-started/nas/openmediavault/index.html @@ -0,0 +1,7586 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OpenMediaVault - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Running PhotoPrism on OpenMediaVault

    +
    +

    Should you experience problems with the installation, we recommend that you ask the OpenMediaVault community for advice, as we cannot provide support for third-party software and services. Also note that third-party integrations may not provide direct access to config files or the command line, so you might not be able to use all features and config options.

    +
    +

    PhotoPrism can be conveniently installed using the OpenMediaVault plugin.

    +

    Screenshot

    +

    Getting Updates

    +

    To upgrade your instance, open a terminal, download our newest image from Docker Hub, and then restart the service:

    +
    podman pull docker.io/photoprism/photoprism:latest
    +systemctl restart pod-photoprism.service
    +
    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/nas/qnap/index.html b/getting-started/nas/qnap/index.html new file mode 100644 index 0000000000..874190377e --- /dev/null +++ b/getting-started/nas/qnap/index.html @@ -0,0 +1,7585 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + QNAP - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Setting Up PhotoPrism on QNAP

    +

    Before setting up PhotoPrism on your NAS, we recommend that you check the QNAP product database for the CPU and memory configuration of your device.

    +

    For a good user experience, it should be a 64-bit system with at least 2 cores and 3 GB of RAM. Indexing large photo and video collections also benefits greatly from using SSD storage, especially for the database and cache files.

    +
    +

    Should you experience problems with the installation, we recommend that you ask the QNAP community for advice, as we cannot provide support for third-party software and services. +Also note that RAW image conversion and TensorFlow are disabled on devices with 1 GB or less memory, and that high-resolution panoramic images may require additional swap space and/or physical memory above the recommended minimum.

    +
    +

    Setup

    +

    You can follow this tutorial to install PhotoPrism on QNAP:

    +

    https://safjan.com/install-photoprism-on-qnap-nas-using-docker-compose/

    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/nas/synology/index.html b/getting-started/nas/synology/index.html new file mode 100644 index 0000000000..db44e417d8 --- /dev/null +++ b/getting-started/nas/synology/index.html @@ -0,0 +1,7873 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Synology - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Running PhotoPrism on a Synology NAS

    +

    Before setting up PhotoPrism on your NAS, we recommend that you check the Synology Knowledge Base for the CPU and memory configuration of your device.

    +

    For a good user experience, it should be a 64-bit system with at least 2 cores and 3 GB of RAM. Indexing large photo and video collections also benefits greatly from using SSD storage, especially for the database and cache files.

    +
    +

    Should you experience problems with the installation, we recommend that you ask the Synology community for advice, as we cannot provide support for third-party software and services. +Also note that RAW image conversion and TensorFlow are disabled on devices with 1 GB or less memory, and that high-resolution panoramic images may require additional swap space and/or physical memory above the recommended minimum.

    +
    +

    Will my device be fast enough?

    +

    This largely depends on your expectations and the number of files you have. Most users report that PhotoPrism runs +well on their Synology NAS. However, you should keep in mind:

    +
      +
    • initial indexing may take longer than on standard desktop computers
    • +
    • the hardware has no video transcoding support and software transcoding is generally slow
    • +
    +

    Setup

    +

    Setup using Portainer

    +

    A step-by-step guide to install PhotoPrism with Portainer can be found here.

    +

    Setup using Synology Container Manager

    +

    To install PhotoPrism with the Synology Container Manager, we recommend following this beginner-friendly tutorial on LinuxLinks.

    +

    First Steps

    +

    Our First Steps 👣 tutorial guides you through the user interface and settings to ensure your library is indexed according to your individual preferences.

    +

    Troubleshooting

    +

    If your device runs out of memory, the index is frequently locked, or other system resources are running low:

    + +

    Other issues? Our troubleshooting checklists help you quickly diagnose and solve them.

    + + + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/nas/unraid/index.html b/getting-started/nas/unraid/index.html new file mode 100644 index 0000000000..365e5cee1a --- /dev/null +++ b/getting-started/nas/unraid/index.html @@ -0,0 +1,7518 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Unraid - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Setting Up PhotoPrism on Unraid

    +
    +

    Should you experience problems with the installation, we recommend that you ask the Unraid community for advice, as we cannot provide support for third-party software and services. Also note that third-party integrations may not provide direct access to config files or the command line, so you might not be able to use all features and config options.

    +
    +
    +

    SQLite is not a good choice for users who require scalability and high performance. If it is used in a configuration template, we recommend changing it to use MariaDB instead.

    +
    +

    We recommend you follow this tutorial provided by IBRACORP to install PhotoPrism on Unraid:

    +

    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/portainer/console-settings.png b/getting-started/portainer/console-settings.png new file mode 100644 index 0000000000..4b49557804 Binary files /dev/null and b/getting-started/portainer/console-settings.png differ diff --git a/getting-started/portainer/console.png b/getting-started/portainer/console.png new file mode 100644 index 0000000000..02312e6306 Binary files /dev/null and b/getting-started/portainer/console.png differ diff --git a/getting-started/portainer/containers.png b/getting-started/portainer/containers.png new file mode 100644 index 0000000000..353ef1a3c9 Binary files /dev/null and b/getting-started/portainer/containers.png differ diff --git a/getting-started/portainer/index.html b/getting-started/portainer/index.html new file mode 100644 index 0000000000..854d88dff5 --- /dev/null +++ b/getting-started/portainer/index.html @@ -0,0 +1,8070 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Portainer - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Portainer Setup Guide

    +

    Portainer can be used to manage Docker containers through a web interface. On many NAS devices, it either comes pre-installed or you can simply install it from the vendor's app store. If you are installing PhotoPrism on a regular home or cloud server, you may instead want to follow our Docker Compose Setup Guide, which only uses standard Docker tools and commands.

    +

    Step 1: Create Stack

    +

    Navigate to "Stacks", click "Add stack" and paste the contents of our stack.yml config template (opens in a new tab) into the Web editor so that you can change the storage folder locations in the volumes sections as needed:

    +

    Screenshot

    +

    When using the Web editor, please make sure that related values remain on the same indentation level and that lists start with a dash as shown in our template.

    +

    Volumes

    +

    You need to explicitly specify the directories you want to use on your NAS device, since PhotoPrism can't see files in folders that have not been shared. This is an important security feature and allows for a flexible configuration without having to change any other variables.

    +
    +

    It is important that all folders specified in the "volumes" sections are located on a persistent volume on your device. We recommend changing the relative paths used in our example to absolute paths in order to avoid potential data loss, e.g. if the default application folder managed by Portainer changes or is reset after an update.

    +

    The volume mount paths to configure depend on your NAS device and its settings. As on most operating systems, a dot followed by a slash ./ can be used to specify a path relative to the current directory. If you keep the defaults, all files will be located in the internal application folder that Portainer automatically creates when you add a new stack.

    +
    +
    Database
    +

    Our stack template includes a pre-configured MariaDB database server that stores its data in the Portainer application folder by default:

    +
    services:
    +  mariadb:
    +    volumes:
    +      - "./database:/var/lib/mysql"
    +
    +

    If your NAS device has a mixed drive configuration with solid-state drives (SSDs) and traditional hard disks, we recommend that you change ./database to an absolute path located on an SSD as this significantly improves performance, for example:

    +
          - "/mnt/ssd/database:/var/lib/mysql"
    +
    +
    +

    Database files should never be located on an unreliable device such as a USB flash drive, SD card, or network folder.

    +
    +
    /photoprism/originals
    +

    The originals folder contains your original photo and video files:

    +
    services:
    +  photoprism:
    +    volumes:
    +      - "./originals:/photoprism/originals"
    +
    +

    We recommend that you change ./originals to the directory on your NAS where your existing media files are, for example:

    +
          - "/mnt/photos:/photoprism/originals"
    +
    +

    Additional directories can be mounted as sub folders of /photoprism/originals (depending on overlay filesystem support):

    +
        volumes:
    +      - "/mnt/photos:/photoprism/originals"
    +      - "/mnt/videos:/photoprism/originals/videos"
    +
    +
    +

    If you want to start with an empty library, you can mount any directory that has enough free space for your needs.

    +
    +
    /photoprism/storage
    +

    The storage folder is used to save config, cache, backup, thumbnail, and sidecar files. It must always be specified so that you do not lose these files after a restart or upgrade. +If available, we recommend that you put the storage folder on a local SSD drive for best performance. You can otherwise keep the default to store the files in the internal application folder:

    +
    services:
    +  photoprism:
    +    volumes:
    +      - "./storage:/photoprism/storage"
    +
    +
    +

    Never configure the storage folder to be inside the originals folder unless the name starts with a . to indicate that it is hidden. +Should you later want to move your instance to another NAS, the easiest and most time-saving way is to copy the entire storage folder along with your originals and database.

    +
    +
    /photoprism/import
    +

    You can optionally mount an import folder from which files can be transferred to the originals folder in a structured way that avoids duplicates, for example:

    +
    services:
    +  photoprism:
    +    volumes:
    +      - "/mnt/media/usb:/photoprism/import"
    +
    +

    Imported files receive a canonical filename and will be organized by year and month. You should never configure the import folder to be inside the originals folder, as this will cause a loop by importing already indexed files.

    +
    +

    Even if you don't specify an import folder, adding files via Web Upload and WebDAV remains possible unless read-only mode is enabled or the features have been disabled.

    +
    +

    Step 2: Finalize Setup

    +

    To complete the setup, download the stack.env file from our server (right click and Save Link As...), click "Load variables from .env file", upload it to Portainer, and then change the values according to your needs:

    +

    Screenshot

    +
    +

    Always change PHOTOPRISM_ADMIN_PASSWORD so that the app starts with a secure initial password. +Never use easy-to-guess passwords or default values like insecure on publicly accessible instances. +There is no default in case no password was provided. A minimum length of 8 characters is required.

    +
    +
    +

    You cannot change the database password with MARIADB_PASSWORD after MariaDB has been started for the first time. However, choosing a secure password is not essential if you do not share the database with other applications or expose it over a network. To enable automatic schema updates when upgrading to a new major version, please make sure that MARIADB_AUTO_UPGRADE is set to a non-empty value.

    +
    +

    When you're done, scroll down and click "Deploy the stack" without changing any of the other options:

    +

    Screenshot

    +

    After waiting a few moments, you should be able to log in as admin with the password specified in PHOTOPRISM_ADMIN_PASSWORD when you navigate to http://localhost:2342/.

    +
    +

    If you have modified the server hostname, port, or protocol in your configuration, the URL to use changes accordingly.

    +
    +

    Step 3: Index Your Library

    +

    Our First Steps 👣 tutorial guides you through the user interface and settings to ensure your library is indexed according to your individual preferences.

    +
    +

    The config options and container image you want to use can be changed at any time by navigating to "Stacks", selecting your existing PhotoPrism stack, clicking "Editor", updating the configuration to your needs, and then clicking "Update the stack" to apply the changes.

    +
    +

    PhotoPrism® Plus

    +

    Our members can activate additional features by logging in with the admin user created during setup and then following the steps described in our activation guide. Thank you for your support, which has been and continues to be essential to the success of the project!

    +

    Compare Memberships › View Membership FAQ ›

    +
    +

    We recommend that new users install our free Community Edition before signing up for a membership.

    +
    +

    Troubleshooting

    +

    If your device runs out of memory, the index is frequently locked, or other system resources are running low:

    + +

    Other issues? Our troubleshooting checklists help you quickly diagnose and solve them.

    +
    +

    You are welcome to ask for help in our community chat. +Sponsors receive direct technical support via email. +Before submitting a support request, try to determine the cause of your problem.

    +
    +

    Command-Line Interface

    +

    Opening a Terminal

    +

    Navigate to "Stacks", select the PhotoPrism stack and scroll down to the list of containers:

    +

    Screenshot

    +

    Now click the button belonging to the photoprism-photoprism-1 container and accept the default settings to open a terminal:

    +

    Screenshot

    +

    Running photoprism help lists all commands and options available in the current version:

    +
    photoprism help
    +
    +

    Use the --help flag to see a detailed command description, for example:

    +
    photoprism backup --help
    +
    +

    The command-line interface is also well suited for job automation using a scheduler.

    +

    Examples

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ActionCommand
    Display Config Valuesphotoprism show config
    Show Migration Statusphotoprism migrations ls
    Repeat Failed Migrationsphotoprism migrations run -f
    Reset Databasephotoprism reset --yes
    Backup Databasephotoprism backup -a -i
    Restore Databasephotoprism restore -a -i
    Change Passwordphotoprism passwd [username]
    Show User Management Commandsphotoprism users help
    Reset Usersphotoprism users reset --yes
    Show Face Recognition Commandsphotoprism faces help
    Index Facesphotoprism faces index
    Reset People & Facesphotoprism faces reset -f
    Transcode Videos to AVCphotoprism convert
    Regenerate Thumbnailsphotoprism thumbs -f
    Update Indexphotoprism index --cleanup
    Move to Originalsphotoprism import [path]
    Copy to Originalsphotoprism cp [path]
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/portainer/login.png b/getting-started/portainer/login.png new file mode 100644 index 0000000000..e70abf8ad0 Binary files /dev/null and b/getting-started/portainer/login.png differ diff --git a/getting-started/portainer/preview.png b/getting-started/portainer/preview.png new file mode 100644 index 0000000000..2b325b0aca Binary files /dev/null and b/getting-started/portainer/preview.png differ diff --git a/getting-started/portainer/step-1-add.png b/getting-started/portainer/step-1-add.png new file mode 100644 index 0000000000..40a1fed5c2 Binary files /dev/null and b/getting-started/portainer/step-1-add.png differ diff --git a/getting-started/portainer/step-2-config.png b/getting-started/portainer/step-2-config.png new file mode 100644 index 0000000000..e65b00fb44 Binary files /dev/null and b/getting-started/portainer/step-2-config.png differ diff --git a/getting-started/portainer/step-3-deploy.png b/getting-started/portainer/step-3-deploy.png new file mode 100644 index 0000000000..6c78957ad7 Binary files /dev/null and b/getting-started/portainer/step-3-deploy.png differ diff --git a/getting-started/portainer/update.png b/getting-started/portainer/update.png new file mode 100644 index 0000000000..4c2574e15f Binary files /dev/null and b/getting-started/portainer/update.png differ diff --git a/getting-started/ports/freebsd/index.html b/getting-started/ports/freebsd/index.html new file mode 100644 index 0000000000..22edc6fd9a --- /dev/null +++ b/getting-started/ports/freebsd/index.html @@ -0,0 +1,7606 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FreeBSD - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Running PhotoPrism On FreeBSD

    +
    +

    Please note that third-party apps may not provide access to the compose.yaml or docker-compose.yml file or the command line, and therefore you may not be able to use all of PhotoPrism's features and config options.

    +
    +
    +

    Should you experience problems with the installation, we recommend that you ask the FreeBSD community for advice, as we cannot provide support for third-party software and services. You can contribute by clicking to send a pull request with your changes.

    +
    +

    For FreeBSD and TrueNAS CORE (formerly FreeNAS) users, an unofficial port is available that builds PhotoPrism from source. It will also compile and install the required TensorFlow libraries for you.

    +

    1. Clone or download the port:

    +
    git clone https://github.com/huo-ju/photoprism-freebsd-port
    +
    +

    2. Build TensorFlow and PhotoPrism from source, then install:

    +
    cd photoprism-freebsd-port
    +make config
    +make && make install
    +
    +

    When running the make config command, a CPU feature options dialog will be presented, and the default option is NONE.

    +

    3. Add entries to rc.conf:

    +
    photoprism_enable="YES"
    +photoprism_assetspath="/var/photoprism/assets"
    +photoprism_storagepath="/var/photoprism/storage"
    +
    +

    You can add more command line parameters into photoprism_flags="" in the rc.conf

    +

    photoprism config shows all config parameters.

    +

    4. Start the service:

    +
    service photoprism start
    +
    +

    Done!

    +

    When should I perform a complete rescan?

    +

    We recommend performing a complete rescan after major updates to take advantage of new search filters and sorting options. Be sure to read the notes for each release to see what changes have been made and if they might affect your library, for example, because of the file types you have or because new search features have been added. If you encounter problems that you cannot solve otherwise (i.e. before reporting a bug), please also try a rescan and see if it solves the problem.

    +

    You can start a rescan from the user interface by navigating to Library > Index, selecting "Complete Rescan", and then clicking "Start".

    +
    +

    Manually entered information such as labels, people, titles or descriptions will not be modified when indexing, even if you perform a "complete rescan". Be careful not to start multiple indexing processes at the same time, as this will lead to a high server load.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/proxies/apache-2/index.html b/getting-started/proxies/apache-2/index.html new file mode 100644 index 0000000000..ce337b3e20 --- /dev/null +++ b/getting-started/proxies/apache-2/index.html @@ -0,0 +1,7595 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Apache 2.4 - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Using Apache 2.4 as Reverse Proxy

    +
    +

    Should you experience problems with Apache, we recommend that you ask the Apache community for advice, as we cannot provide support for third-party software and services.

    +
    +
    +

    Example

    +
    ProxyPass /api/v1/ws ws://photoprism.lxd:2342/api/v1/ws
    +ProxyPassReverse /api/v1/ws ws://photoprism.lxd:2342/api/v1/ws
    +ProxyPass / http://photoprism:2342/
    +ProxyPassReverse / http://photoprism:2342/
    +ProxyRequests off
    +
    +
    +

    The official documentation explains in detail, how to configure Apache Web Server 2.4 to reverse proxy WebSockets.

    +

    Why Use a Proxy?

    +

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure +HTTPS reverse proxy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted +by anyone, including your provider, hackers, and governments. Backup tools and file sync apps may refuse to +connect as well.

    +

    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/proxies/caddy-1/index.html b/getting-started/proxies/caddy-1/index.html new file mode 100644 index 0000000000..903df3e567 --- /dev/null +++ b/getting-started/proxies/caddy-1/index.html @@ -0,0 +1,7598 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Caddy 1 - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Using Caddy 1 as Reverse Proxy

    +
    +

    Should you experience problems with Caddy, we recommend that you ask the Caddy community for advice, as we cannot provide support for third-party software and services.

    +
    +

    For PhotoPrism to work properly, you need to enable websockets and transparent proxying:

    +
    +

    Example

    +
    example.com {
    +    proxy / photoprism:2342 {
    +        websocket
    +        transparent
    +    }
    +}
    +
    +
    +

    Please refer to the official documentation +for further details.

    +

    Why Use a Proxy?

    +

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure +HTTPS reverse proxy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted +by anyone, including your provider, hackers, and governments. Backup tools and file sync apps may refuse to +connect as well.

    +

    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/proxies/caddy-2/index.html b/getting-started/proxies/caddy-2/index.html new file mode 100644 index 0000000000..b32e685982 --- /dev/null +++ b/getting-started/proxies/caddy-2/index.html @@ -0,0 +1,7597 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Caddy 2 - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Using Caddy 2 as Reverse Proxy

    +
    +

    Should you experience problems with Caddy, we recommend that you ask the Caddy community for advice, as we cannot provide support for third-party software and services.

    +
    +

    WebSocket proxying automatically works in Caddy 2. There is no need to enable this as necessary for Caddy 1, Apache, +and NGINX. In addition, Caddy 2 may automatically create +and update Let's Encrypt HTTPS certificates.

    +
    +

    Example

    +
    example.com {
    +    reverse_proxy photoprism:2342
    +}
    +
    +
    +

    Please refer to the official documentation +for further details.

    +

    Why Use a Proxy?

    +

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure +HTTPS reverse proxy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted +by anyone, including your provider, hackers, and governments. Backup tools and file sync apps may refuse to +connect as well.

    +

    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/proxies/haproxy/index.html b/getting-started/proxies/haproxy/index.html new file mode 100644 index 0000000000..892068396c --- /dev/null +++ b/getting-started/proxies/haproxy/index.html @@ -0,0 +1,7627 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Haproxy - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Using HAPROXY as Reverse Proxy

    +
    +

    Should you experience problems with Haproxy, we recommend that you ask the Haproxy community for advice, as we cannot provide support for third-party software and services.

    +
    +
    defaults
    +    #Defaults used in frontend and backends
    +    #Defined here to avoid repitition
    +    #Can be overwritten in frontends and/or backends
    +    log global
    +    option logasap
    +    mode http
    +    timeout connect 30000ms
    +    timeout client 30000ms
    +    timeout server 30000ms
    +    timeout tunnel 120000ms
    +    timeout queue 5000ms
    +
    +##########################################################
    +
    +#Frontend config
    +frontend fe-photoprism
    +    #'photo' is the name of the subdomain
    +    #TLS certs should be referenced here, maybe created by dehydrated, certbot, ...
    +    bind *:443 ssl crt /etc/ssl/localcerts/wildcard.example.com.pem
    +
    +    #SNI-Detection
    +    #Can be removed, if not needed
    +    acl sni_photo hdr(host) -i photo.example.com
    +    #Use Backend if domain (acl is set) detected
    +    use_backend be-photoprism if sni_photo
    +
    +    #Every unflagged request goes here, may target to another backend as well
    +    default_backend be-photoprism
    +
    +##########################################################
    +
    +#Backend config
    +#be-photoprism is the name of the backend referenced in frontend
    +backend be-photoprism
    +    retries 3
    +    option forwardfor
    +    no option httpclose
    +
    +    #Local PhotoPrism-Instance
    +    server photo 127.0.0.1:2342
    +
    +

    Why Use a Proxy?

    +

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure +HTTPS reverse proxy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted +by anyone, including your provider, hackers, and governments. Backup tools and file sync apps may refuse to +connect as well.

    +

    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/proxies/nginx/index.html b/getting-started/proxies/nginx/index.html new file mode 100644 index 0000000000..5e19726858 --- /dev/null +++ b/getting-started/proxies/nginx/index.html @@ -0,0 +1,7621 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NGINX - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Using NGINX as Reverse Proxy

    +
    +

    Getting Support

    +

    Since NGINX is notoriously difficult to configure, we are unable to provide technical support for NGINX-related issues such as failed uploads, connection errors, broken thumbnails, and video playback problems. If you cannot resolve these on your own, we recommend that you ask their community for advice or use Traefik instead, which is easier to configure and more convenient to handle overall.

    +
    +

    This tutorial explains, how to configure NGINX WebSocket connections between your client and backend services.

    +
    +

    Example

    +
    http {
    +  server {
    +    listen 80 ssl;
    +    listen [::]:80 ssl;
    +    server_name example.com;
    +    client_max_body_size 500M;
    +
    +    # With SSL via Let's Encrypt
    +    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    +    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    +    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    +    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    +
    +    location / {
    +      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    +      proxy_set_header Host $host;
    +
    +      proxy_pass http://photoprism:2342;
    +
    +      proxy_buffering off;
    +      proxy_http_version 1.1;
    +      proxy_set_header Upgrade $http_upgrade;
    +      proxy_set_header Connection "upgrade";
    +
    +      client_max_body_size 500M;
    +    }
    +  }
    +}
    +
    +
    +

    At the very least you will need to adapt server_name and the ssl_certificate/ssl_certificate_key paths to match your setup. Please refer to their official documentation for further details.

    +

    View "Pitfalls and Common Mistakes" ›

    +

    Why Use a Proxy?

    +

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure +HTTPS reverse proxy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted +by anyone, including your provider, hackers, and governments. Backup tools and file sync apps may refuse to +connect as well.

    +

    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/proxies/swag/index.html b/getting-started/proxies/swag/index.html new file mode 100644 index 0000000000..4bd8ec0f63 --- /dev/null +++ b/getting-started/proxies/swag/index.html @@ -0,0 +1,7812 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SWAG - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Using SWAG as Reverse Proxy

    +
    +

    Should you experience problems with Swag, we recommend that you ask the Swag community for advice, as we cannot provide support for third-party software and services.

    +
    +

    To simplify the setup of a reverse HTTPS proxy, Linuxserver.io developed SWAG.

    +

    SWAG - Secure Web Application Gateway (formerly known as letsencrypt, no relation to Let's Encrypt™) sets up a Nginx +web server and reverse proxy with PHP support and a built-in certbot client that automates free SSL server certificate +generation and renewal processes (Let's Encrypt and ZeroSSL). It also contains fail2ban for intrusion prevention.

    +

    Setup

    +

    Step 1: Get a domain

    +

    The first step is to grab a dynamic DNS if you don't have your own subdomain already. You can get this from for example DuckDNS.

    +

    Step 2: Set-up SWAG

    +

    Then you will need to set up SWAG, the variables of the docker compose are explained on the Github page of SWAG. +This is an example of how to set it up using duckdns and docker-compose.

    +
    +

    compose.yaml

    +
    version: "2.1"
    +services:
    + swag:
    + image: ghcr.io/linuxserver/swag
    + container_name: swag
    +        cap_add:
    +        - NET_ADMIN
    +    environment:
    +        - PUID=1000
    +        - PGID=1000
    +        - TZ=Europe/Brussels
    +        - URL=<mydomain.duckdns>
    +        - SUBDOMAINS=wildcard
    +        - VALIDATION=duckdns
    +        - CERTPROVIDER= #optional
    +        - DNSPLUGIN= #optional
    +        - DUCKDNSTOKEN=<duckdnstoken> 
    +        - EMAIL=<e-mail> #optional
    +        - ONLY_SUBDOMAINS=false #optional
    +        - EXTRA_DOMAINS=<extradomains> #optional
    +        - STAGING=false #optional
    +        volumes:
    +        - /etc/config/swag:/config
    +        ports:
    +        - 443:443
    +        restart: unless-stopped
    +
    +
    +

    Don't forget to change the mydomain.duckdns into your personal domain and the duckdnstoken into your token and remove the brackets.

    +

    Step 3: Change the config files

    +

    Navigate to the config folder of SWAG and head to proxy-confs. If you used the example above, you should navigate to: /etc/config/swag/nginx/proxy-confs/ . +There are a lot of preconfigured files to use for different apps such as radarr,sonarr,overseerr,...

    +

    To use the bundled configuration file, simply rename photoprism.subdomain.conf.sample in the proxy-confs folder to photoprism.subdomain.conf. +Alternatively, you can create a new file photoprism.subdomain.conf in proxy-confs with the following configuration:

    +
    +

    photoprism.subdomain.conf

    +
    server {
    +    listen 443 ssl http2;
    +    listen [::]:443 ssl http2;
    +
    +    server_name photoprism.*;
    +
    +    include /config/nginx/ssl.conf;
    +
    +    client_max_body_size 0;
    +
    +    location / {
    +        include /config/nginx/proxy.conf;
    +        resolver 127.0.0.11 valid=30s;
    +        set $upstream_app photoprism;
    +        set $upstream_port 2342;
    +        set $upstream_proto http;
    +        proxy_pass $upstream_proto://$upstream_app:$upstream_port;
    +        }
    +
    +}   
    +
    +
    +

    Step 4: Port-forward port 443

    +

    Since SWAG allows you to set up a secure connection, you will need to open port 443 on your router for encrypted traffic. This is way more secure than port 80 for http.

    +

    Step 5: Restart SWAG

    +

    When you change anything in the config of Nginx, you will need to restart the container using docker restart swag. +If everything went well, you can now access photoprism on the subdomain you configured: photoprism.mydomain.duckdns.org

    +
    +

    Attention

    +

    The docker-container of photoprism won't be named "photoprism", it will be "name_photoprism". +To check this, execute docker ps and check wether it is named "photoprism". +If it's not, go to your docker-compose.yml file and add the following line to photoprism below 'image' container_name=photoprism. Restart swag afterwards. +Keep in mind to not have two photoprism containers with the same name! +You could also change the config file of Swag with the right name in the proxy-confs directory.

    +
    +

    Why Use a Proxy?

    +

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure +HTTPS reverse proxy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted +by anyone, including your provider, hackers, and governments. Backup tools and file sync apps may refuse to +connect as well.

    +

    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/proxies/traefik/index.html b/getting-started/proxies/traefik/index.html new file mode 100644 index 0000000000..5f09762029 --- /dev/null +++ b/getting-started/proxies/traefik/index.html @@ -0,0 +1,7661 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Traefik - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Using Traefik as Reverse Proxy

    +
    +

    Best Choice

    +
      +
    • No special settings required in combination with modern web applications
    • +
    • WebSocket proxying automatically works
    • +
    • Traefik can create and update Let's Encrypt HTTPS certificates for you
    • +
    +
    +

    To run PhotoPrism behind Traefik, create a traefik.yaml configuration and then add a traefik service to your compose.yaml or docker-compose.yml file, as shown in the following example:

    +
    +

    compose.yaml

    +
    services:
    +  traefik:
    +    image: traefik:v3.1
    +    restart: unless-stopped
    +    ports:
    +      - "80:80"
    +      - "443:443"
    +    volumes:
    +      - "./traefik.yaml:/etc/traefik/traefik.yaml"
    +      - "./traefik/data:/data"
    +      - "/var/run/docker.sock:/var/run/docker.sock"
    +
    +  photoprism:
    +    image: photoprism/photoprism:latest
    +    restart: unless-stopped
    +    labels:
    +      - "traefik.enable=true"
    +      - "traefik.http.routers.photoprism.rule=Host(`example.com`)"
    +      - "traefik.http.routers.photoprism.tls=true"
    +      - "traefik.http.routers.photoprism.tls.certresolver=myresolver" 
    +    volumes:
    +      - "./originals:/photoprism/originals"
    +      - "./storage:/photoprism/storage"
    +    environment:
    +        PHOTOPRISM_SITE_URL: "https://example.com/"
    +        PHOTOPRISM_DISABLE_TLS: "true"
    +
    +
    +
    +

    traefik.yaml

    +
    log:
    +  level: INFO
    +
    +global:
    +  sendAnonymousUsage: false
    +
    +entryPoints:
    +  web:
    +    address: ":80"
    +    http:
    +      redirections:
    +        entryPoint:
    +          to: websecure
    +          scheme: https
    +  websecure:
    +    address: ":443"
    +
    +providers:
    +  docker:
    +    exposedByDefault: false
    +    watch: true
    +
    +api:
    +  insecure: false
    +  dashboard: false
    +  debug: false
    +
    +certificatesResolvers:
    +  myresolver:
    +    acme:
    +      email: ssl-admin@example.com
    +      storage: /data/certs.json
    +      httpChallenge:
    +        entryPoint: web
    +
    +
    +

    Note that you must disable HTTPS/TLS in PhotoPrism by setting PHOTOPRISM_DISABLE_TLS to "true" as Traefik handles HTTPS connections, and that all settings and config options not related to Traefik have been omitted for brevity.

    +

    Further traefik.yaml examples and a detailed description of the Traefik configuration can be found in the corresponding documentation.

    +

    Why Use a Proxy?

    +

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure +HTTPS reverse proxy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted +by anyone, including your provider, hackers, and governments. Backup tools and file sync apps may refuse to +connect as well.

    +

    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/raspberry-pi/index.html b/getting-started/raspberry-pi/index.html new file mode 100644 index 0000000000..ae8c121276 --- /dev/null +++ b/getting-started/raspberry-pi/index.html @@ -0,0 +1,7959 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Requirements - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Running PhotoPrism on a Raspberry Pi

    +

    Our stable releases and preview builds are available as multi-arch Docker images for 64-bit AMD, Intel, and ARM processors.1 +As a Raspberry Pi owner, you therefore get the exact same functionality and can follow the same installation steps after going through a short list of system requirements and architecture specific notes.

    +
    +

    PhotoPrismPi

    +

    The easiest way to run PhotoPrism on a Raspberry Pi2 is with PhotoPrismPi. +Simply flash the image to an SD card, plug it into the Pi and boot it. After a few minutes, our latest release will be ready to use!

    +
    +

    System Requirements

    + +

    Architecture Specific Notes

    +

    Modern ARM64-based Devices

    + + + + + + + + + + + + + + + + + + + + + +
    ImageName
    Stable Releasephotoprism/photoprism:latest
    Development Previewphotoprism/photoprism:preview
    MariaDBarm64v8/mariadb:11
    +

    Running 64-bit Docker images under Raspbian Linux requires a minimum of technical experience to perform the necessary configuration changes. This is because it is a 32-bit operating system with merely a 64-bit kernel to ensure compatibility with legacy software. If you don't need compatibility with 32-bit apps, we recommend choosing a standard 64-bit Linux distribution instead as it will save you time and requires less experience:

    + +
    +

    Other distributions that target the same use case as Raspbian, such as CoreELEC, will have similar issues and should therefore also be avoided to run modern server applications.

    +
    +
    Raspberry Pi OS
    +

    To ensure compatibility with 64-bit Docker images, your Raspberry Pi must boot with the arm_64bit=1 flag in its config.txt file. +An "exec format" error will occur otherwise.

    +

    Try explicitly pulling the ARM64 version if you've booted your device with the arm_64bit=1 flag +and you see the "no matching manifest" error on Raspberry Pi OS (Raspbian):

    +
    docker pull --platform=arm64 photoprism/photoprism:latest
    +
    +

    It may also help to set the DOCKER_DEFAULT_PLATFORM environment variable to linux/arm64.

    +

    In case you see Docker errors related to "cgroups", try adding the following parameters to +/boot/firmware/cmdline.txt or /boot/cmdline.txt (file location depends on the OS in use):

    +
    cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1
    +
    +

    Older ARMv7-based Devices

    +

    You may use the following 32-bit Docker images +to run PhotoPrism and MariaDB on ARMv7-based devices (always use our ARM64 image if possible):

    + + + + + + + + + + + + + + + + + + + + + +
    ImageName
    Stable Releasephotoprism/photoprism:armv7
    Development Previewphotoprism/photoprism:preview-armv7
    MariaDByobasystems/alpine-mariadb:latest
    +

    If your device meets the requirements, mostly the same installation instructions as for regular Linux servers apply. +However, you should pay close attention to differences in path and environment variable names.

    +
    +

    Darktable is not included in the ARMv7 image because it is not 32-bit compatible. Always choose the regular +64-bit version if your device supports it.

    +
    +

    Is a Raspberry Pi fast enough?

    +

    This mainly depends on your expectations and the number of files you have. Most users report that PhotoPrism runs smoothly on a Raspberry Pi 4 with 4 GB of RAM.

    +

    Note, however, that initial indexing usually takes much longer than on a regular desktop computer and that the hardware has limited video transcoding capabilities, so video file format conversion is not well supported and software transcoding is generally slow. We take no responsibility for instability or performance problems if your device does not meet the requirements.

    +

    Getting Updates

    +

    Open a terminal and change to the folder where your compose.yaml or docker-compose.yml file is located.3 +Now run the following commands to download the newest image from Docker Hub and +restart your instance in the background:

    +
    docker compose pull
    +docker compose stop
    +docker compose up -d
    +
    +

    Pulling a new version can take several minutes, depending on your internet connection speed.

    +

    Advanced users can add this to a Makefile so that they only have to type a single +command like make update. See Command-Line Interface +to learn more about terminal commands.

    +
    +

    Even when you use an image with the :latest tag, Docker does not automatically download new images for you. You can either manually upgrade as shown above, or set up a service like Watchtower to get automatic updates.

    +
    +

    Config Examples

    +

    We recommend that you compare your own docker-compose.yml with our latest examples from time to time, as they may include new config options or other enhancements relevant to you.

    +

    MariaDB Server

    +

    Our config examples are generally based on the latest stable release to take advantage of performance enhancements. +This does not mean older versions are no longer supported and you have to upgrade immediately.

    +
    +

    If MariaDB fails to start after upgrading from an earlier version (or migrating from MySQL), the internal management schema may be outdated. See Troubleshooting MariaDB Problems for instructions on how to fix this.

    +
    +

    Troubleshooting

    +

    If your device runs out of memory, the index is frequently locked, or other system resources are running low:

    + +

    Other issues? Our troubleshooting checklists help you quickly diagnose and solve them.

    +
    +

    You are welcome to ask for help in our community chat. +Sponsors receive direct technical support via email. +Before submitting a support request, try to determine the cause of your problem.

    +
    +
    +
    +
      +
    1. +

      Experienced users can alternatively use the packages at dl.photoprism.app/pkg/linux/ to manually install PhotoPrism on compatible Linux distributions. For more installation methods, see our Getting Started FAQ

      +
    2. +
    3. +

      Since our current MicroSD image is based on Ubuntu 22.04 LTS, it is not yet compatible with the Raspberry Pi 5, which requires Ubuntu 23.10 or later. An updated image will be provided as soon as possible. 

      +
    4. +
    5. +

      The default Docker Compose config filename is docker-compose.yml. For simplicity, it doesn't need to be specified when running docker compose or docker-compose in the same directory. Config files for other apps or instances should be placed in separate folders. 

      +
    6. +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/raspberry-pi/microsd-image/card.jpg b/getting-started/raspberry-pi/microsd-image/card.jpg new file mode 100644 index 0000000000..6f08837957 Binary files /dev/null and b/getting-started/raspberry-pi/microsd-image/card.jpg differ diff --git a/getting-started/raspberry-pi/microsd-image/flash-url.png b/getting-started/raspberry-pi/microsd-image/flash-url.png new file mode 100644 index 0000000000..0b66121ab5 Binary files /dev/null and b/getting-started/raspberry-pi/microsd-image/flash-url.png differ diff --git a/getting-started/raspberry-pi/microsd-image/index.html b/getting-started/raspberry-pi/microsd-image/index.html new file mode 100644 index 0000000000..f147fef68a --- /dev/null +++ b/getting-started/raspberry-pi/microsd-image/index.html @@ -0,0 +1,7745 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SD Card Image - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    MicroSD Image for the Raspberry Pi

    +

    +The easiest way to run PhotoPrism on a Raspberry Pi is with PhotoPrismPi.1 +Simply flash the image to an SD card and boot your device with it.

    +

    We recommend using a fast MicroSD card with at least 64 GB so that you don't run out of storage space later on. These are usually sold with an adapter that fits into normal SD card slots.

    +
    +

    Raspberry Pi 5

    +

    Since our current MicroSD image is based on Ubuntu 22.04 LTS, it is not yet compatible with the Raspberry Pi 5, which requires Ubuntu 23.10 or later. As an alternative, you can download Ubuntu 24.04, install Docker, and then follow our regular Setup Guide.

    +
    +

    Step 1: Install balenaEtcher

    +

    Etcher is a powerful OS image flasher that makes flashing an SD card a pleasant and safe experience. You can download it from the official website or directly from GitHub.

    + +

    Step 2: Flash from URL

    +

    Open balenaEtcher and enter the following URL as image source:

    +
    https://dl.photoprism.app/dist/photoprismpi/latest.zip
    +
    +

    +

    When you have selected the image and inserted a suitable card into your computer, press Flash!.

    +

    Step 3: Boot Your Device

    +

    Insert the MicroSD card into the Pi, make sure your device is connected to a wired network, and turn it on. After a few minutes,2 our latest release should be ready to use when you navigate to http://photoprismpi.local/!3

    +

    User Accounts

    +

    When you first log in to PhotoPrism, the username for the initial super admin account is admin and the password is photoprismpi.

    +

    You can also connect to the server via SSH with the username ubuntu and password ubuntu.

    +
    +

    Danger

    +

    Since they can be easily guessed, both passwords should be changed immediately. This is especially important if your device is connected to the Internet or any other shared network.

    +
    +

    Storage Folders

    +

    Uploads, sidecar and cache files are stored in /opt/photoprism. External drives can be connected via USB and accessed as folders /mnt/a to /mnt/d without further configuration.

    +

    Should you want to make changes to the default settings, you can find your compose.yaml or docker-compose.yml file in /boot/firmware/docker-compose/photoprism. +After connecting via SSH with the credentials provided above, you can obtain root privileges by running sudo -i.

    +

    HTTPS Proxy

    +

    Caddy is installed as a reverse proxy that can be configured in /etc/caddy/Caddyfile. By default, the automatically generated certificates are not recognized as valid by browsers, so you will see a warning when connecting over HTTPS.

    +
    +
    +
      +
    1. +

      PhotoPrismPi is based on Ubuntu Server and CustomPiOS. Special thanks to Guy Sheffer who helped us build this! 

      +
    2. +
    3. +

      Download and installation time depends on the speed of your Internet connection. 

      +
    4. +
    5. +

      If you can't connect, try using the existing hostname or IP address instead. 

      +
    6. +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/raspberry-pi/microsd-image/mnt.png b/getting-started/raspberry-pi/microsd-image/mnt.png new file mode 100644 index 0000000000..bfbccb6d54 Binary files /dev/null and b/getting-started/raspberry-pi/microsd-image/mnt.png differ diff --git a/getting-started/troubleshooting/browsers/index.html b/getting-started/troubleshooting/browsers/index.html new file mode 100644 index 0000000000..e6ddd3c06f --- /dev/null +++ b/getting-started/troubleshooting/browsers/index.html @@ -0,0 +1,7672 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Browsers - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Diagnosing Frontend Issues

    +

    Problems with the user interface can be caused by a bug or an incompatible browser:

    +
      +
    • some features may not be supported by non-standard browsers, as well as nightly, unofficial, or outdated versions
    • +
    • not all video and audio formats can be played with every browser, device, and operating system
    • +
    • for example, AAC - the default audio codec for MPEG-4 AVC / H.264 - is supported natively in Chrome, Safari, and Edge, while it is only optionally supported by the OS in Firefox and Opera
    • +
    +
    +

    If the user interface doesn't load at all, our App Not Loading checklist helps you identify and resolve the cause.

    +
    +

    Try Another Browser

    +

    To test if you have a general problem that is not browser-specific, open the Web UI in other browsers:

    +
      +
    • if you are using Firefox Nightly, try the stable version and Chrome or Chromium
    • +
    • if you have browser plugins installed, try disabling them to see if this makes a difference
    • +
    • when the problem disappears, you know that the issue is browser-dependent or caused by a plugin
    • +
    • otherwise, the issue may not be specific to the browser version
    • +
    • make a note in which browsers the problem occurs, as this will be helpful when submitting a support request
    • +
    +
    +

    The user interface works with most modern browsers, and runs best on Chrome, Chromium, Safari, Firefox, and Edge. +Opera and Samsung Internet have been reported to be compatible as well. Due to limited resources, we can +not test every release with all browser types and versions.

    +
    +

    Getting Error Details

    +

    If possible, please also include the error type, error message, and URL of the affected resource when +submitting a support request. For this purpose, check the +browser console for warnings and errors as described below. It is perfectly fine to take screenshots +instead of writing down the details.

    +

    In case you don't see any log messages, try reloading the page, as the problem may occur while the page is loading.

    +
    +
    +
    +
      +
    • press ⌘+Option+J (Mac) or Ctrl+Shift+J (Windows, Linux, Chrome OS) to go directly to the Developer Tools
    • +
    • or, navigate to More tools > Developer tools in the browser menu and open the Console tab
    • +
    +
    +
    +
      +
    • press ⌘+Option+K (Mac) or Ctrl+Shift+K (Windows) to go directly to the Firefox Web Console panel
    • +
    • or, navigate to Web Development > Web Console in the menu and open the Console panel
    • +
    +
    +
    +

    Before you can access the console in Safari on MacOS, you first need to enable the Develop menu:

    +
      +
    1. Choose Safari Menu > Preferences and select the Advanced Tab
    2. +
    3. Select "Show Develop menu in menu bar"
    4. +
    +

    Once the Develop menu is enabled:

    +
      +
    • press Option+⌘+C to go directly to the Javascript Console
    • +
    • or, navigate to Develop > Show Javascript Console in the browser menu
    • +
    +
    +
    +

    Browser logs on Apple mobile devices running iOS or iPadOS can be viewed when you connect them to a Mac. +Before you can connect your device to a Mac, you must allow your device to be inspected:

    +
      +
    1. Open the Settings app
    2. +
    3. Go to Safari
    4. +
    5. Scroll down to Advanced
    6. +
    7. Enable the Web Inspector toggle
    8. +
    +

    If you now connect the device to your Mac with a cable, websites opened in Safari on iOS and iPadOS will appear in a submenu for the connected device in the Develop menu of the Safari desktop browser. +Note that when prompted, you may need to confirm that you trust the Mac you are connecting your device to.

    +

    Web pages (and other content) are separated by app, making it easier for you to find the web page you are looking for. +Once you have found and selected the site you want to inspect, a Web Inspector window will open. +See Apple's Developer Guide for additional help and information.

    +
    +
    +
    +
    +

    We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app. +Ask for technical support if you need help, it could be a local +configuration problem, or a misunderstanding in how the software works.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/troubleshooting/docker/index.html b/getting-started/troubleshooting/docker/index.html new file mode 100644 index 0000000000..1eb247abeb --- /dev/null +++ b/getting-started/troubleshooting/docker/index.html @@ -0,0 +1,8350 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Docker - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Getting Docker Up and Running

    +
    +

    You are welcome to ask for help in our community chat. +Sponsors receive direct technical support via email. +Before submitting a support request, try to determine the cause of your problem.

    +
    +

    Installation

    +

    If you cannot use the docker and docker compose (or docker-compose) commands, make sure Docker is running on the host you are connected to and your current user has permission to use it. +The following instructions explain how to install Docker:

    + +

    Alternatively, Podman is supported as a drop-in replacement for Docker on Red Hat-compatible Linux distributions like RHEL, CentOS, Fedora, AlmaLinux, and Rocky Linux.

    +

    Ubuntu Linux

    +

    If you are using Ubuntu Linux, you can run this script to install the latest Docker version, including the Compose Plugin, on your server in one step:

    +
    bash <(curl -s https://setup.photoprism.app/ubuntu/install-docker.sh)
    +
    +

    Docker Compose

    +

    The examples in our guides now use the new docker compose command by default. However, if your Docker version does not yet support the Compose Plugin, you can still use the standalone docker-compose command.

    +

    On some Linux distributions, you may need to install an additional package. To do so, you can use a graphical software package manager or run the following command in a terminal to install the Compose Plugin for Docker on Ubuntu and Debian:

    +
    sudo apt update
    +sudo apt install docker-compose-plugin
    +
    +

    If that does not work, this will install the legacy docker-compose command:

    +
    sudo apt update
    +sudo apt install docker-compose
    +
    +

    Running the following commands will add a docker-compose alias for the new Compose plugin so that older scripts don't break:

    +
    echo 'docker compose "$@"' | sudo tee /bin/docker-compose
    +sudo chmod +x /bin/docker-compose
    +
    +

    Podman Compose

    +

    On Red Hat-compatible Linux distributions like RHEL, CentOS, Fedora, AlmaLinux, and Rocky Linux, you can use Podman and Podman Compose as direct replacements for Docker and Docker Compose. The following installs the podman and podman-compose commands if they are not already installed:

    +
    sudo dnf update -y
    +sudo dnf install epel-release -y
    +sudo dnf install netavark aardvark-dns podman podman-docker podman-compose -y
    +sudo systemctl start podman
    +sudo systemctl enable podman
    +podman --version
    +
    +

    We also provide a setup script that conveniently installs Podman and downloads the default configuration to a directory of your choice:

    +
    mkdir -p /opt/photoprism
    +cd /opt/photoprism
    +curl -sSf https://dl.photoprism.app/podman/install.sh | bash
    +
    +
    +

    Please keep in mind to replace the docker and docker compose commands with podman and podman-compose when following the examples in our documentation.

    +
    +

    Using Docker

    +

    Cannot Connect

    +

    If you see the error message "Cannot connect to the Docker daemon", it means that Docker is not installed or +not running yet. Before you try anything else, it may help to simply restart your computer.

    +

    On many Linux distributions, this command will start the Docker daemon manually if needed:

    +
    sudo systemctl start docker.service
    +
    +

    On other operating systems, start Docker Desktop and enable the "Start Docker Desktop when you log in" +option in its settings.

    +

    Connection Aborted

    +

    If you see the error message "Connection aborted" or "Connection denied", it usually means that your +current user does not have permission to use Docker.

    +

    On Linux, this command grants permission by adding a user to the docker group (relogin for changes to take effect):

    +
    sudo usermod -aG docker [username]
    +
    +

    Alternatively, you can prefix the docker and docker-compose commands with sudo when not running as root, +for example:

    +
    sudo docker compose stop
    +sudo docker compose up -d
    +
    +

    Note that this will point the home directory shortcut ~ to /root in the volumes: section +of your compose.yaml or docker-compose.yml.

    +

    IPTables Firewall

    +

    On Linux, Docker manipulates the iptables rules to provide network isolation. This does have some implications for what you need to do if you want to have your own policies in addition to the rules Docker manages.

    +

    Learn more ›

    +

    Wrong MTU Size

    +

    If you use Docker on your server or on a virtual machine, technical limitations of the local network or your internet provider can sometimes make it impossible to reach external services such as the Reverse Geocoding API that we operate for our users. In particular, the network cards of virtual machines often do not have the standard Maximum Transmission Unit (MTU) of 1500, but a smaller size like 1492 or 1454.

    +

    In this case, you must configure the virtual network cards of your Docker containers so that they have an MTU size that is less than or equal to that of the outgoing network, for example by adding the following to your compose.yaml (or docker-compose.yml) config files:

    +
    networks:
    +  default:
    +    driver: bridge
    +    driver_opts:
    +      com.docker.network.driver.mtu: 1450
    +
    +

    Learn more ›

    +
    +

    All network configuration changes require a restart of the affected services and/or the Docker daemon to take effect.

    +
    +

    Viewing Logs

    +

    You can run this command to watch the Docker service logs, including the last 100 messages (omit --tail=100 to see them all, and -f to output only the last logs without watching them):

    +
    docker compose logs -f --tail=100 
    +
    +

    A good way to troubleshoot configuration issues is to increase the log level. To enable trace log mode, set PHOTOPRISM_LOG_LEVEL to "trace" in the environment: section of the photoprism service (or use the --trace flag when running the photoprism command directly):

    +
    services:
    +  photoprism:
    +    environment:
    +      PHOTOPRISM_LOG_LEVEL: "trace"
    +      ...
    +
    +

    Now restart all services for your changes to take effect:

    +
    docker compose stop
    +docker compose up -d
    +
    +

    It can also be helpful to keep Docker running in the foreground while debugging, so that log messages are displayed directly. To do this, omit the -d parameter when (re)starting:

    +
    docker compose stop
    +docker compose up
    +
    +
    +

    If you see no errors or no logs at all, you may have started the server on a different host +and/or port. There could also be an issue with your browser, browser plugins, firewall settings, +or other tools you may have installed.

    +
    +
    +

    The default Docker Compose config filename is docker-compose.yml. For simplicity, it doesn't need to be specified when running docker compose or docker-compose in the same directory. Config files for other apps or instances should be placed in separate folders.

    +
    +

    Adding Swap

    +

    Note that indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory above the recommended minimum. We recommend not to set a hard memory limit, unless you are familiar with memory management and understand the implications.

    +

    Linux

    +

    Open a terminal and run this command to check if your server has swap configured.

    +
    swapon --show
    +
    +

    Example output:

    +
    NAME      TYPE SIZE USED PRIO
    +/swapfile file  64G  88M   -2
    +
    +

    This means you have 64 GB of swap and don't need to add more. Learn how much you need.

    +

    Otherwise, run these commands to permanently add 4 GB of swap (or more depending on how much physical memory you have):

    +
    sudo -i
    +fallocate -l 4G /swapfile
    +chmod 600 /swapfile
    +mkswap /swapfile
    +swapon /swapfile
    +echo '/swapfile none swap sw 0 0' | tee -a /etc/fstab
    +
    +
    +

    You can skip sudo -i if you are already logged in as root.

    +
    +

    Raspbian

    +

    Open a terminal on your Raspberry Pi and run the following command to verify if it has swap configured:

    +
    swapon --show
    +
    +

    Example output:

    +
    NAME      TYPE SIZE USED PRIO
    +/swapfile file  100M  0B   -2
    +
    +

    If no swap has been configured or the command only shows 100 MB, open /etc/dphys-swapfile with a text editor, search for CONF_SWAPSIZE=100 and increase the value to 2048 if your device has 4 GB of physical memory, and 4096 otherwise:

    +
    sudo nano /etc/dphys-swapfile
    +
    +

    Then restart for the changes to take effect:

    +
    sudo reboot
    +
    +

    In addition, you can reduce memory usage and improve stability by setting PHOTOPRISM_WORKERS to 1 in your compose.yaml or docker-compose.yml file to limit the number of indexing workers.

    +

    Windows

    +

    It is important to increase the Docker memory limit to 4 GB or more when using Hyper-V. The default of 2 GB can reduce indexing performance and cause unexpected restarts. Also make sure you configure at least 4 GB of swap space. Docker Desktop uses dynamic memory allocation with WSL 2, meaning you do not need to change any memory-related settings (depending on which version of Windows and Docker you are using).

    +

    macOS

    +

    It is important to increase the Docker memory limit to 4 GB or more, as the +default of 2 GB can reduce indexing performance and cause unexpected restarts. Also, ensure that you configure +at least 4 GB of swap space.

    +

    Kernel Security

    +

    We recommend disabling Linux kernel security modules like SELinux (Red Hat/Fedora) on private servers, especially if you have no experience configuring them.

    +

    If you have working configuration rules for a particular Linux distribution, feel free to share the instructions with the community so that less experienced users can harden their installation without running into problems.

    +

    File Permissions

    +

    Errors such as "read-only file system", "error creating path", "failed to create folder", "permission denied", or "wrong permissions" indicate a filesystem permission problem:

    +
      +
    • Use a file manager, or the commands ls -alh, chmod, and chown on Unix-like operating systems, to check and change filesystem permissions so all files and folders are accessible
    • +
    • The app and database storage folders must be writable as well: Verify that the services have write permissions and that you have not mounted the folders read-only on your host or via Docker using the :ro flag
    • +
    • If you have configured specific user and group IDs for a service, make sure they match
    • +
    • If symbolic links are mounted or used within storage folders, replace them with actual paths
    • +
    • It may help to add the :z mount flag to volumes when using SELinux (Red Hat/Fedora)
    • +
    • When mounting folders that only root has access to, you may have to prefix the docker and docker-compose commands with sudo on Linux if you are not already logged in as root
    • +
    +

    An easy way to test for missing permissions is to (temporarily) remove restrictions and make the entire folder accessible to everyone:

    +

    sudo chmod -R a+rwX [folder]
    +
    +Start a full rescan once all issues have been resolved, especially if it looks like thumbnails or pictures are missing.

    +
    +

    Be very careful when changing permissions in shared hosting environments. If you are using PhotoPrism on corporate +or university servers, we recommend that you ask your IT help desk for advice.

    +
    +

    Overlay Volumes

    +

    Depending on overlay file system support, it is possible to mount additional host folders as sub folders of /photoprism/originals (or other storage folders), for example:

    +
    volumes:
    +  - "/home/username/Pictures:/photoprism/originals"
    +  - "/example/friends:/photoprism/originals/friends"
    +  - "/mnt/photos:/photoprism/originals/media"
    +
    +

    For this to work, you should have the cgroupfs-mount package installed, as shown in the installation script we provide. +You may otherwise find that files added to the mounted folders are not visible on the host, and data loss may occur.

    +
    +

    We recommend that you start with a simple configuration without overlay volume mounts or path placeholders like ~, and only move on to a more complex setup once this works.

    +
    +

    Disk Space

    +

    In case the logs show "disk full", "quota exceeded", or "no space left" errors, either the disk containing the +storage folder is full (get a new one or use a different disk) or a disk usage limit is configured, for example +in the Docker, Kubernetes, or Virtual Machine configuration (remove or increase it):

    +
      +
    • on Linux and other Unix-like operating systems, the available disk space can be viewed by running df -h in a terminal
    • +
    • if you are using Kubernetes, Docker Desktop, Hyper-V, or a Virtual Machine, they have their own settings to adjust the size of storage, RAM, and swap
    • +
    • for details, refer to the corresponding documentation
    • +
    +

    Start a full rescan if necessary, for example, if it looks like thumbnails or pictures are missing.

    +

    Network Storage

    +

    Shared folders that have already been mounted on your host under a drive letter or path can be used with Docker containers like any other directory. As shown below, certain types of network storage can alternatively be mounted directly with Docker Compose.

    +

    Please note that the required system dependencies must be installed on your computer in order to mount NFS (Unix/Linux) and/or CIFS shares (Windows/Mac), e.g. the nfs-client and cifs-utils packages on Ubuntu Linux. Also make sure that your Docker version and operating system are up-to-date, and that the latest Subsystem for Linux (WSL) is installed if you have a Windows PC.

    +
    +

    Never store database files, e.g. used by MariaDB or SQLite, on an unreliable device like a USB stick, SD card or network drive as this leads to poor performance and can also result in data loss.

    +
    +

    Unix / NFS

    +

    Follow this docker-compose.yml example to mount Network File System (NFS) shares e.g. from Unix servers or NAS devices:

    +
    services:
    +  photoprism:
    +    # ...
    +    volumes:
    +      # Map named volume "originals"
    +      # to "/photoprism/originals":
    +      - "originals:/photoprism/originals"     
    +  mariadb:
    +    # ...
    +
    +# Specify named volumes:
    +volumes:
    +  originals:
    +    driver_opts:
    +      type: nfs
    +      # Authentication and other mounting options:
    +      o: "addr=1.2.3.4,username=user,password=secret,soft,rw,nfsvers=4.1"
    +      # Mount this path:
    +      device: ":/mnt/example"
    +
    +

    device should contain the path to the share on the NFS server, note the : at the beginning. In the above example, the share can be mounted as the named volume originals. You can also choose another name as long as it is consistent.

    +

    Driver-specific options can be set after the server address in o, see the nfs manual page. Here are some examples of commonly used options:

    +
      +
    • nfsvers=3, nfsvers=4, or nfsvers=4.1 to specify the NFS version
    • +
    • nolock (optional): Remote applications on the NFS server are not affected by lock files inside the Docker container (only other processes inside the container are affected by locks)
    • +
    • timeo=n (optional, default 600): The NFS client waits n tenths of a second before retrying an NFS request
    • +
    • soft (optional): The NFS client aborts an NFS request after retrans=n unsuccessful retries, otherwise it retries indefinitely
    • +
    • retrans=n (optional, default 2): Sets the number of retries for NFS requests, only relevant when using soft
    • +
    +

    When you are done, please restart all services for the changes to take effect.

    +
    +

    Because some operating environments and file systems do not enforce character set encodings, NFS v4.1 supports the fs_charset_cap attribute, which indicates the UTF-8 capabilities to the client.

    +
    +

    SMB / CIFS

    +

    Follow this docker-compose.yml example to mount CIFS network shares, e.g. from Windows, NAS devices or Linux servers with Samba:

    +
    services:
    +  photoprism:
    +    # ...
    +    volumes:
    +      # Map named volume "originals"
    +      # to "/photoprism/originals":
    +      - "originals:/photoprism/originals"     
    +  mariadb:
    +    # ...
    +
    +# Specify named volumes:
    +volumes:
    +  originals:
    +    driver_opts:
    +      type: cifs
    +      o: "iocharset=utf8,username=user,password=secret,rw"
    +      device: "//host/folder"
    +
    +

    Then restart all services for the changes to take effect. Note that related values must start at the same indentation level in YAML and that tabs are not allowed for indentation. We recommend using 2 spaces, but any number will do as long as it is consistent.

    +
    +

    We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app. +Ask for technical support if you need help, it could be a local +configuration problem, or a misunderstanding in how the software works.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/troubleshooting/firewall/index.html b/getting-started/troubleshooting/firewall/index.html new file mode 100644 index 0000000000..9a12cb0d50 --- /dev/null +++ b/getting-started/troubleshooting/firewall/index.html @@ -0,0 +1,7683 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Firewall - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Configuring Your Firewall

    +
    +

    You are welcome to ask for help in our community chat. +Sponsors receive direct technical support via email. +Before submitting a support request, try to determine the cause of your problem.

    +
    +

    Incoming Requests

    +

    Unless you have changed the default configuration, PhotoPrism is reachable via port 2342 on all network devices. If you are using a firewall, please ensure that this port can be accessed from other computers on your network, or that your instance can be accessed through a reverse proxy:

    +

    +

    Outgoing Connections

    +

    As explained in our Privacy Policy, reverse geocoding and interactive world maps depend on retrieving the necessary information from us and MapTiler AG, headquartered in Switzerland. Both services are provided with a very high level of privacy and confidentiality.

    +

    View Privacy Policy › View Compliance FAQ ›

    +

    In order to successfully set up your installation and view location details in PhotoPrism, you must allow requests to the following hosts if you have a firewall installed, and make sure that your Internet connection is working:

    + +

    In addition, the following API endpoints should be allowed so that public Docker images can be pulled from Docker Hub:

    +
      +
    • auth.docker.io
    • +
    • registry-1.docker.io
    • +
    • index.docker.io
    • +
    • dseasb33srnrn.cloudfront.net
    • +
    • production.cloudflare.docker.com
    • +
    +

    IPTables and Docker

    +

    On Linux, Docker manipulates the iptables rules to provide network isolation. This does have some implications for what you need to do if you want to have your own policies in addition to the rules Docker manages.

    +

    Learn more ›

    +

    Docker MTU Size

    +

    If you use Docker on your server or on a virtual machine, technical limitations of the local network or your internet provider can also make it impossible to reach external services. In particular, the network cards of virtual machines often do not have the standard Maximum Transmission Unit (MTU) of 1500, but a smaller size like 1492 or 1454.

    +

    In this case, you must configure the virtual network cards of your Docker containers so that they have an MTU size that is less than or equal to that of the outgoing network, for example by adding the following to your compose.yaml (or docker-compose.yml) config files:

    +
    networks:
    +  default:
    +    driver: bridge
    +    driver_opts:
    +      com.docker.network.driver.mtu: 1450
    +
    +

    Learn more ›

    +
    +

    All network configuration changes require a restart of the affected services and/or the Docker daemon to take effect.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/troubleshooting/img/passmark-cpu.svg b/getting-started/troubleshooting/img/passmark-cpu.svg new file mode 100644 index 0000000000..4df58e423a --- /dev/null +++ b/getting-started/troubleshooting/img/passmark-cpu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/getting-started/troubleshooting/img/ui-error-logs.jpg b/getting-started/troubleshooting/img/ui-error-logs.jpg new file mode 100644 index 0000000000..ea02130d1e Binary files /dev/null and b/getting-started/troubleshooting/img/ui-error-logs.jpg differ diff --git a/getting-started/troubleshooting/index.html b/getting-started/troubleshooting/index.html new file mode 100644 index 0000000000..0cb50d5967 --- /dev/null +++ b/getting-started/troubleshooting/index.html @@ -0,0 +1,8199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Checklists - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Troubleshooting Checklists

    +
    +

    You are welcome to ask for help in our community chat. +Sponsors receive direct technical support via email. +Before submitting a support request, try to determine the cause of your problem.

    +
    +

    Connection Fails

    +

    If your browser cannot connect to the Web UI even after waiting a few minutes, run this command to watch the logs including the last 100 messages (omit --tail=100 to see them all, and -f to output only the last logs without watching them):

    +
    docker compose logs -f --tail=100
    +
    +

    Before reporting a bug:

    +
      +
    • Check the logs for messages like disk full, disk quota exceeded, no space left on device, read-only file system, error creating path, wrong permissions, no route to host, connection failed, and killed:
        +
      • If a service has been "killed" or otherwise automatically terminated, this points to a memory problem (add swap and/or memory; remove or increase usage limits)
      • +
      • In case the logs show "disk full", "quota exceeded", or "no space left" errors, either the disk containing the storage folder is full (add storage) or a disk usage limit is configured (remove or increase it)
      • +
      • Errors such as "read-only file system", "error creating path", "failed to create folder", "permission denied", or "wrong permissions" indicate a filesystem permission problem
      • +
      • It may help to add the :z mount flag to volumes when using SELinux (Red Hat/Fedora)
      • +
      • Log messages that contain "no route to host" indicate a problem with the database or Docker network configuration (follow our examples)
      • +
      +
    • +
    • Make sure you are using the correct protocol (default is http), port (default is 2342), and host (default is localhost): +
    • +
    • If you use a firewall, ensure that it is configured correctly and that outgoing connections to our geocoding API are allowed
    • +
    • Note that HTTP security headers will prevent the app from loading in a frame (override them)
    • +
    • Verify your computer meets the system requirements
    • +
    • Go through the checklist for fatal server errors
    • +
    +

    MariaDB

    +

    Should MariaDB get stuck in a restart loop and PhotoPrism cannot connect to it, this indicates a memory, +filesystem, or other permission issue:

    +
    mariadb: mysqld: ready for connections.
    +mariadb: mysqld (initiated by: unknown): Normal shutdown
    +photoprism: dial tcp 172.18.0.2:3306: connect: no route to host
    +mariadb: mysqld: Shutdown complete
    +
    +

    Learn more ›

    +

    Firewall

    +

    Maps & Places: As explained in our Privacy Policy, reverse geocoding and interactive world maps depend on retrieving the necessary information from us and MapTiler AG, headquartered in Switzerland. You therefore need allow requests to these API endpoints if you have a firewall installed and make sure your Internet connection is working.

    +

    Learn more ›

    +

    IPTables: On Linux, Docker manipulates the iptables rules to provide network isolation. This does have some implications for what you need to do if you want to have your own policies in addition to the rules Docker manages.

    +

    Learn more ›

    +

    Debug Mode

    +

    To enable debug mode, set PHOTOPRISM_LOG_LEVEL to "debug" in the environment: section of the photoprism service (or use the --debug flag when running the photoprism command directly):

    +
    services:
    +  photoprism:
    +    environment:
    +      PHOTOPRISM_LOG_LEVEL: "debug"
    +
    +

    If you need even more detailed logs for debugging, you can enable trace log mode by setting PHOTOPRISM_LOG_LEVEL to “trace” in the environment: section of the photoprism service (or use the --trace flag when running the photoprism command directly):

    +
    services:
    +  photoprism:
    +    environment:
    +      PHOTOPRISM_LOG_LEVEL: "trace"
    +
    +

    Then restart all services for the changes to take effect. It can be helpful to keep Docker running in the foreground while debugging so that log messages are displayed directly. To do this, omit the -d parameter when restarting:

    +
    docker compose stop
    +docker compose up 
    +
    +
    +

    If you see no errors or no logs at all, you may have started the server on a different host +and/or port. There could also be an issue with your browser, browser plugins, firewall settings, +or other tools you may have installed.

    +
    +
    +

    The default Docker Compose config filename is docker-compose.yml. For simplicity, it doesn't need to be specified when running docker compose or docker-compose in the same directory. Config files for other apps or instances should be placed in separate folders.

    +
    +

    Docker Doesn't Work

    +

    Make sure you have Docker or Docker Desktop installed, started, and properly configured on your system. It is available for Mac, Linux, and Windows. On Red Hat-compatible Linux distributions like RHEL, CentOS, Fedora, AlmaLinux, and Rocky Linux, you can use Podman and Podman Compose as direct replacements for Docker and Docker Compose.

    +

    Getting Docker Up and Running

    +

    Bad Performance

    +

    Performance Tips

    +

    Solving Windows-Specific Issues

    +

    Fatal Server Errors

    +

    Fatal errors are often caused by one of the following conditions:

    + +

    We recommend checking your Docker Logs for messages like disk full, disk quota exceeded, +no space left on device, read-only file system, error creating path, wrong permissions, no route to host, connection failed, and killed:

    +
      +
    • If a service has been "killed" or otherwise automatically terminated, this points to a memory problem (add swap and/or memory; remove or increase usage limits)
    • +
    • In case the logs show "disk full", "quota exceeded", or "no space left" errors, either the disk containing the storage folder is full (add storage) or a disk usage limit is configured (remove or increase it)
    • +
    • Errors such as "read-only file system", "error creating path", "failed to create folder", "permission denied", or "wrong permissions" indicate a filesystem permission problem
    • +
    • Log messages that contain "no route to host" indicate a problem with the database or network configuration (follow our examples)
    • +
    +

    Start a full rescan if necessary, for example, if it looks like thumbnails or pictures are missing.

    +

    App Not Loading

    +

    If the app doesn't load in your browser when you navigate to the server URL, you can check the browser console +for helpful errors and warnings. Sometimes you just need to wait a moment, for example, if you are using a slow wireless +connection or the server was started only a few seconds ago. In case this does not help:

    +
      +
    • You are using an incompatible browser (try another browser)
    • +
    • JavaScript is disabled in your browser settings, so you only see the splash screen (enable it)
    • +
    • JavaScript was disabled by a browser plugin (disable it or add an exception)
    • +
    • Your browser cannot communicate properly with the server, e.g. because a reverse proxy, VPN, or CDN is configured incorrectly (check its configuration and try without)
    • +
    • HTTP security headers prevent the app from loading in a frame (override them)
    • +
    • An ad blocker or other plugins block requests (disable them or add an exception)
    • +
    • There is a problem with your network connection (test if other sites work)
    • +
    • You are connected to the wrong server, VPN, CDN, or a DNS record has not been updated yet
    • +
    +

    Cannot Log In

    +

    If password authentication is enabled and the user interface loads, but you cannot log in with what you assume is the correct password:

    +
      +
    • There is a problem with the integrity, stability or connection of the database that you should be able to diagnose by watching the logs for errors and warnings
    • +
    • You had too many failed login attempts, therefore another attempt from your computer is temporarily not possible
    • +
    • Caps Lock is enabled on your keyboard, your computer has the wrong input locale set, or somebody else might have changed the password without telling you
    • +
    • PHOTOPRISM_ADMIN_PASSWORD does not have a minimum length of 8 characters, so PhotoPrism has been started without a password since there is no default
    • +
    • Your password contains one or more $ signs that were not properly escaped in your compose.yaml or docker-compose.yml file (escape them and reset your database or manually set a new password)
    • +
    • The password may be correct, but the username is wrong and does not match PHOTOPRISM_ADMIN_USER
    • +
    • There is a problem with the schema or data in the auth_sessions database table that can be resolved by running the photoprism auth reset --yes command in a terminal to reset it to a clean state and force a re-login of all users (this will also delete all client access tokens and app passwords users may have created)
    • +
    • You upgraded from an early test or preview build and might need to run the photoprism users reset --yes command in a terminal after the upgrade, see Known Issues for details (this resets the auth_users table to a clean state and requires accounts to be recreated)
    • +
    • Your browser cannot communicate properly with the server, e.g. because a reverse proxy, VPN, or CDN is configured incorrectly (check its configuration and try without)
    • +
    • You are connected to the wrong server, VPN, CDN, or a DNS record has not been updated yet
    • +
    • Remember that the initial admin username and password cannot be changed after PhotoPrism has been started for the first time
    • +
    +

    We also recommend checking your Docker Logs for messages like disk full, disk quota exceeded, no space left on device, read-only file system, error creating path, wrong permissions, no route to host, connection failed, and killed:

    +
      +
    • If a service has been "killed" or otherwise automatically terminated, this points to a memory problem (add swap and/or memory; remove or increase usage limits)
    • +
    • In case the logs show "disk full", "quota exceeded", or "no space left" errors, either the disk containing the storage folder is full (add storage) or a disk usage limit is configured (remove or increase it)
    • +
    • Errors such as "read-only file system", "error creating path", "failed to create folder", "permission denied", or "wrong permissions" indicate a filesystem permission problem
    • +
    • Log messages that contain "no route to host" indicate a problem with the database or network configuration (follow our examples)
    • +
    +

    To see which user accounts exist on your instance, open a terminal and run photoprism users ls. A new password can be set with photoprism passwd [username]. You can then try to log in again. Upgrade to the latest release, restart the server, and check the logs for errors and warnings if it still doesn't work.

    +

    No WebDAV Access

    +

    If you followed our step-by-step guide and still have trouble connecting via WebDAV:

    + +

    Missing Pictures

    +

    If you have indexed your library and some images or videos are missing, first check Library > Errors for errors and warnings. +In case the application logs do not contain anything helpful:

    + +

    Depending on the cause of the problem, you may need to perform a full rescan once the issue is resolved.

    +

    Zip Archives

    +

    When you have tried to download multiple pictures or albums and found that some files are missing in the resulting zip archive or you got the error message "No files available for download":

    + +

    Also make sure that there is enough free disk space available, since the server creates a temporary zip file when multiple pictures are selected for download. Complete albums are compressed while downloading without needing temporary storage.

    +

    File Downloads

    +

    Follow the steps to resolve zip download issues if you are having problems downloading selected pictures, individual files, or stacks of files that belong to a single photo.

    +

    If this didn't help, the problems might be caused by your browser settings, e.g. insufficient permissions to download multiple files, browser plugins, a firewall, VPN, CDN or proxy that you use together with PhotoPrism.

    +

    Wrong Search Results

    +

    If search results are incorrect, for example, in the wrong order or not filtered properly:

    + +

    It may be a bug if you cannot find any other reasons, such as a local configuration problem or a misunderstanding in how the software works. Please note that reports must be reproducible in order for us to provide a solution.

    +

    Broken Thumbnails

    +

    If some pictures have broken or missing thumbnails, first check Library > Errors for errors and warnings. +In case the application logs do not contain anything helpful:

    +
      +
    • The issue can be resolved by reloading the page or clearing the browser cache
    • +
    • You browse non-JPEG files under Library > Originals which have an icon but no preview
    • +
    • Preview Images are disabled under Settings > Library (enable them)
    • +
    • Dynamic Previews are disabled under Settings > Advanced or your server is not powerful enough
    • +
    • The sizes in Settings > Advanced have been changed so the request can't be fulfilled
    • +
    • FFmpeg and/or RAW converters are disabled under Settings > Advanced (enable them)
    • +
    • Your storage folder is full, or a quota/inode limit has been reached (increase it)
    • +
    • Your storage folder is not writable or mounted read-only (change permissions)
    • +
    • Your cache storage folder is not accessible, has been renamed, or was not mounted on a permanent volume, so the cached thumbnails have been lost after a restart (run the photoprism thumbs command in a terminal to regenerate them after fixing this)
    • +
    • Originals or thumbnail files were deleted manually, for example to free up disk space
    • +
    • Files cannot be opened, e.g. because the file system permissions have been changed
    • +
    • Files are stored on an unreliable device such as a USB flash drive or a shared network folder
    • +
    • Some thumbnails could not be created because you didn't configure at least 4 GB of swap
    • +
    • Your browser cannot communicate properly with the server, e.g. because a reverse proxy, VPN, or CDN is configured incorrectly (check its configuration and try without)
    • +
    • Your proxy, router, or firewall has a request rate limit, so some requests fail
    • +
    • There are other network problems caused by a firewall, router, or unstable connection
    • +
    • An ad blocker or other plugins block requests (disable them or add an exception)
    • +
    • You are connected to the wrong server, VPN, CDN, or a DNS record has not been updated yet
    • +
    +

    We also recommend checking your Docker Logs for messages like disk full, disk quota exceeded, +no space left on device, read-only file system, error creating path, wrong permissions, and killed:

    +
      +
    • If a service has been "killed" or otherwise automatically terminated, this points to a +memory problem
    • +
    • In case the logs show "disk full", "quota exceeded", or "no space left" errors, either the disk containing the storage folder is full (add storage) or a disk usage limit is configured (remove or increase it)
    • +
    • Errors such as "read-only file system", "error creating path", "failed to create folder", "permission denied", or "wrong permissions" indicate a filesystem permission problem
    • +
    +

    Depending on the cause of the problem, you may need to perform a full rescan once the issue is resolved.

    +

    Videos Don't Play

    +

    In case FFmpeg is disabled or not installed, videos cannot be indexed because still images cannot be created. You should also have ExifTool enabled to extract metadata such as duration, resolution, and codec.

    +

    If videos do not play and/or you only see a white/black area when you open a video:

    +
      +
    • You are using an incompatible browser, e.g. without AVC support (try another browser)
    • +
    • AVC support or related JavaScript features have been disabled in your browser (check the settings and try another browser)
    • +
    • It is a large non-AVC video that needs to be transcoded first (wait or run photoprism convert to pre-transcode videos)
    • +
    • An ad blocker or other plugins block requests (disable them or add an exception)
    • +
    • Your (virtual) server disk is full, or a quota/inode limit has been reached (increase it)
    • +
    • The storage folder is not writable or mounted read-only (change permissions)
    • +
    • Files are stored on an unreliable device such as a USB flash drive or a shared network folder (check if the files are accessible)
    • +
    • Your browser cannot communicate properly with the server, e.g. because a reverse proxy, VPN, or CDN is configured incorrectly (check its configuration and try without)
    • +
    • There are other network problems caused by a proxy, firewall, or unstable connection (try a direct connection)
    • +
    • You are connected to the wrong server, VPN, CDN, or a DNS record has not been updated yet
    • +
    +

    We recommend that you check your Docker Logs and the browser console +for messages related to HTTP requests, permissions, security, FFmpeg, videos, and file conversion.

    +

    Please note:

    +
      +
    1. Not all video and audio formats can be played with every browser. For example, AAC - the default audio codec for MPEG-4 AVC / H.264 - is supported natively in Chrome, Safari, and Edge, while it is only optionally supported by the OS in Firefox and Opera.
    2. +
    3. HEVC/H.265 video files can have a .mp4 file extension too, which is often associated with AVC only. This is because MP4 is a container format, meaning that the actual video content may be compressed with H.264, H.265, or something else. The file extension doesn't really tell you anything other than that it's probably a video file.
    4. +
    5. MPEG-4 AVC videos are not re-encoded if they exceed the configured bitrate limit. To reduce the size of AVC videos, you can manually replace the original files with a smaller version or wait for a future release that offers this functionality.
    6. +
    +
    +

    We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app. +Ask for technical support if you need help, it could be a local +configuration problem, or a misunderstanding in how the software works.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/troubleshooting/logs/index.html b/getting-started/troubleshooting/logs/index.html new file mode 100644 index 0000000000..ee26902294 --- /dev/null +++ b/getting-started/troubleshooting/logs/index.html @@ -0,0 +1,7609 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Logs - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Collecting Debug Information

    +
    +
    +
    +

    Make sure Logs is enabled under Settings > General so you can see log messages in the Web UI.

    +

    Live Logs

    +

    The continuously updated live logs in Library > Logs are especially useful for diagnosing indexing and import issues, +but also display other types of logs (depending on the log level):

    +
      +
    1. Navigate to Library
    2. +
    3. Open the Logs tab
    4. +
    +
    +

    Only a limited number of messages are visible in the Web App to reduce memory usage. You can see all messages +in the Docker Logs. This may be more convenient if you are looking for information +on a specific file or want to attach your full logs to a support request.

    +
    +

    Errors and Warnings

    +
      +
    1. Expand the main navigation
    2. +
    3. Open the Library sub navigation
    4. +
    5. Navigate to Library > Errors
    6. +
    +

    +
    +
    +

    If you have a frontend issue, it is often helpful to check the browser console for errors and warnings. +A console is available in all modern browsers and can be activated via keyboard shortcuts or the browser menu.

    +

    Problems with the user interface can be caused by a bug or an incompatible browser: +Some features may not be supported by non-standard browsers, as well as nightly, unofficial, +or outdated versions.

    +

    In case you don't see any log messages, try reloading the page, as the problem may occur while the page is loading.

    +

    Chrome, Chromium, and Edge

    +
      +
    • press ⌘+Option+J (Mac) or Ctrl+Shift+J (Windows, Linux, Chrome OS) to go directly to the Developer Tools
    • +
    • or, navigate to More tools > Developer tools in the browser menu and open the Console tab
    • +
    +

    Firefox

    +
      +
    • press ⌘+Option+K (Mac) or Ctrl+Shift+K (Windows) to go directly to the Firefox Web Console panel
    • +
    • or, navigate to Web Development > Web Console in the menu and open the Console panel
    • +
    +

    Safari

    +

    Before you can access the console in Safari on MacOS, you first need to enable the Develop menu:

    +
      +
    1. Choose Safari Menu > Preferences and select the Advanced Tab
    2. +
    3. Select "Show Develop menu in menu bar"
    4. +
    +

    Once the Develop menu is enabled:

    +
      +
    • press Option+⌘+C to go directly to the Javascript Console
    • +
    • or, navigate to Develop > Show Javascript Console in the browser menu
    • +
    +

    Mobile Safari

    +

    Browser logs on Apple mobile devices running iOS or iPadOS can be viewed when you connect them to a Mac. +Before you can connect your device to a Mac, you must allow your device to be inspected:

    +
      +
    1. Open the Settings app
    2. +
    3. Go to Safari
    4. +
    5. Scroll down to Advanced
    6. +
    7. Enable the Web Inspector toggle
    8. +
    +

    If you now connect the device to your Mac with a cable, websites opened in Safari on iOS and iPadOS will appear in a submenu for the connected device in the Develop menu of the Safari desktop browser. +Note that when prompted, you may need to confirm that you trust the Mac you are connecting your device to.

    +

    Web pages (and other content) are separated by app, making it easier for you to find the web page you are looking for. +Once you have found and selected the site you want to inspect, a Web Inspector window will open. +See Apple's Developer Guide for additional help and information.

    +
    +
    +

    You can run this command to watch the Docker service logs, including the last 100 messages (omit --tail=100 to see them all, and -f to output only the last logs without watching them):

    +
    docker compose logs -f --tail=100 
    +
    +

    A good way to troubleshoot configuration issues is to increase the log level. To enable trace log mode, set PHOTOPRISM_LOG_LEVEL to "trace" in the environment: section of the photoprism service (or use the --trace flag when running the photoprism command directly):

    +
    services:
    +  photoprism:
    +    environment:
    +      PHOTOPRISM_LOG_LEVEL: "trace"
    +      ...
    +
    +

    Now restart all services for your changes to take effect:

    +
    docker compose stop
    +docker compose up -d
    +
    +

    It can also be helpful to keep Docker running in the foreground while debugging, so that log messages are displayed directly. To do this, omit the -d parameter when (re)starting:

    +
    docker compose stop
    +docker compose up
    +
    +
    +

    If you see no errors or no logs at all, you may have started the server on a different host +and/or port. There could also be an issue with your browser, browser plugins, firewall settings, +or other tools you may have installed.

    +
    +
    +

    The default Docker Compose config filename is docker-compose.yml. For simplicity, it doesn't need to be specified when running docker compose or docker-compose in the same directory. Config files for other apps or instances should be placed in separate folders.

    +
    +
    +
    +
    +
    +

    We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app. +Ask for technical support if you need help, it could be a local +configuration problem, or a misunderstanding in how the software works.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/troubleshooting/mariadb/index.html b/getting-started/troubleshooting/mariadb/index.html new file mode 100644 index 0000000000..2ea14fa1ed --- /dev/null +++ b/getting-started/troubleshooting/mariadb/index.html @@ -0,0 +1,8124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MariaDB - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Troubleshooting MariaDB Problems

    +
    +

    You are welcome to ask for help in our community chat. +Sponsors receive direct technical support via email. +Before submitting a support request, try to determine the cause of your problem.

    +
    +

    Compatibility

    +

    PhotoPrism is compatible with SQLite 3 and MariaDB 10.5.12+. +Official support for MySQL 8 is discontinued as Oracle seems to have stopped shipping new features and enhancements. +As a result, the testing effort required before each release is no longer feasible.

    +

    Our configuration examples are generally based on the current stable version to take advantage of performance improvements. This does not mean that older versions are no longer supported and you must upgrade immediately. We recommend not using the :latest tag for the MariaDB Docker image and to upgrade manually by changing the tag once we had a chance to test a new major version, e.g.:

    +
    services:
    +  mariadb:
    +    image: mariadb:11
    +    ...
    +
    +

    Cannot Connect

    +

    First, verify that you are using the correct port (default is 3306) and host:

    +
      +
    • in the internal Docker network, the default hostname is mariadb (same as the service)
    • +
    • avoid changing the default network configuration, unless you are experienced with this
    • +
    • avoid using IP addresses other than 127.0.0.1 (localhost) directly, as they can change
    • +
    • only use localhost or 127.0.0.1 if the database port has been exposed as described below and you are on the same computer (host)
    • +
    • we recommend configuring a local hostname to access other hosts on your network
    • +
    +

    To connect to MariaDB from your host or home network, you need to expose port 3306 in your compose.yaml or docker-compose.yml +and restart the service for changes to take effect:

    +
    services:
    +  mariadb:
    +    ports:
    +      - "3306:3306"
    +
    +
    +

    Set strong passwords if the database is exposed to an external network. Never expose your database to the public +Internet in this way, for example, if it is running on a cloud server.

    +
    +

    If this doesn't help, check the Docker Logs for messages like disk full, disk quota exceeded, no space left on device, read-only file system, error creating path, wrong permissions, no route to host, connection failed, exec format error, no matching manifest, and killed:

    +
      +
    • Make sure that the database storage folder is readable and writable: Errors such as "read-only file system", "error creating path", "failed to create folder", "permission denied", or "wrong permissions" indicate a filesystem permission problem
    • +
    • If symbolic links are mounted or used within the storage folder, replace them with the actual paths and verify that they are accessible
    • +
    • If the MariaDB service has been "killed" or otherwise automatically terminated, this can point to a memory problem (add swap and/or memory; remove or increase usage limits)
    • +
    • In case the logs also show "disk full", "quota exceeded", or "no space left" errors, either the disk containing the storage folder is full (add storage) or a disk usage limit is configured (remove or increase it)
    • +
    • Log messages that contain "no route to host" may also indicate a general network configuration problem (follow our examples)
    • +
    • You have to resort to alternative Docker images to run MariaDB on ARMv7-based devices and those with a 32-bit operating system
    • +
    • You may find a solution in the official MariaDB Docker Image FAQ
    • +
    +

    Wrong Password

    +

    If the password you are using was specified in a compose.yaml or docker-compose.yml file and contains one or more $ characters, these must be escaped with $$ (a double dollar sign) so that, for example, "compo$e" becomes "compo$$e":

    +
    services:
    +  mariadb:
    +    environment:
    +      # sets password to "compo$e"
    +      MARIADB_PASSWORD: "compo$$e"
    +
    +

    Also note that you cannot change the database password with MARIADB_PASSWORD after MariaDB has been started for the first time.

    +

    In this case, you can either delete the database storage folder and restart the database service or follow the instructions under Lost Root Password.

    +

    Bad Performance

    +

    Many users reporting poor performance and high CPU usage have migrated from SQLite to MariaDB, so their database schema is no longer optimized for performance. For example, MariaDB cannot handle rows with text columns in memory and always uses temporary tables on disk if there are any.

    +

    The instructions for these migrations were provided by a contributor and are not part of the original software distribution. As such, they have not been officially released, recommended, or extensively tested by us.

    +

    If this is the case, please make sure that your migrated database schema matches that of a fresh, non-migrated installation. It may help to run the migrations manually in a terminal using the migrations subcommands. However, this does not guarantee that all issues such as missing indexes are resolved.

    +

    Get Performance Tips › View Database Schema ›

    +

    Version Upgrade

    +

    Should MariaDB fail to start after upgrading from an earlier version (or migrating from MySQL), the internal management schema may be outdated. With older versions, it could only be updated manually. +However, newer MariaDB Docker images support automatic upgrades on startup, so you don't have to worry about that anymore.

    +
    +

    When upgrading from MariaDB 10.x to 11.0, you must replace command: mysqld with command: (followed by the command flags) in your compose.yaml or docker-compose.yml file, otherwise the database server may fail to start.

    +
    +

    Manual Update

    +

    To manually upgrade the internal database schema, run this command in a terminal:

    +
    docker compose exec mariadb mariadb-upgrade -uroot -p
    +
    +

    Enter the MariaDB "root" password specified in your compose.yaml or docker-compose.yml when prompted.

    +

    Alternatively, you can downgrade to the previous version, create a database backup using the photoprism backup +command, start a new database instance based on the latest version, and then restore your index with +the photoprism restore command.

    +

    Auto Upgrade

    +

    To enable automatic schema updates, set MARIADB_AUTO_UPGRADE to a non-empty value in your compose.yaml or docker-compose.yml as shown in our config example:

    +
    services:
    +  mariadb:
    +    image: mariadb:11
    +    ...
    +    environment:
    +      MARIADB_AUTO_UPGRADE: "1"
    +      MARIADB_INITDB_SKIP_TZINFO: "1"
    +      ...
    +
    +

    Before starting MariaDB in production mode, the database image entrypoint script now runs mariadb-upgrade to update the internal management schema as needed. For example, when you pull a new major release and restart the service.

    +
    +

    Since PhotoPrism does not require time zone support, you can also add MARIADB_INITDB_SKIP_TZINFO to your config as shown above. However, this is only a recommendation and optional.

    +
    +

    Incompatible Schema

    +

    If your database does not seem to be compatible with the currently installed version of PhotoPrism, for example because search results are missing or incorrect, first make sure you are using a supported database and that its internal management schema is up-to-date. How to do that is explained in the previous section.

    +

    Once you have verified that neither is a problem, you can run the following command in a terminal to check the status of previous database schema migrations:

    +
    docker compose exec photoprism photoprism migrations ls
    +
    +
    +

    Omit the docker compose exec photoprism prefix if you are using an interactive terminal session or are running PhotoPrism directly on your computer without Docker.

    +
    +

    Re-Run Migrations

    +

    Should the status of any migration not be OK, you can re-run failed migrations using this command in a terminal:

    +
    docker compose exec photoprism photoprism migrations run -f
    +
    +

    The -f flag instructs the photoprism migrations run subcommand to re-run previously failed migrations. Use --help to see the command help.

    +

    Additional migration command examples can be found in the Developer Guide.

    +

    Complete Rescan

    +

    We recommend that you re-index your pictures after a schema migration, especially if problems persist. You can either start a rescan from the user interface by navigating to Library > Index, checking "Complete Rescan", and then clicking "Start", or by running this command in a terminal:

    +
    docker compose exec photoprism photoprism index -f
    +
    +
    +

    Be careful not to start multiple indexing processes at the same time, as this will lead to a high server load.

    +
    +

    Server Crashes

    +

    If the server crashes unexpectedly or your database files get corrupted frequently, it is usually because they are stored on an unreliable device such as a USB flash drive, an SD card, or a shared network folder mounted via NFS or CIFS. These may also have unexpected file size limitations, which is especially problematic for databases that do not split data into smaller files.

    +
      +
    • Never use the same database files with more than one server instance
    • +
    • To share a database over a network, run the database server directly on the remote server instead of sharing database files
    • +
    • To repair your tables after you have moved the files to a local disk, you can start MariaDB with --innodb-force-recovery=1 (otherwise the same procedure as for recovering a lost password, see above)
    • +
    • Make sure you are using the latest Docker version and read the release notes for the database server version you are using
    • +
    +

    Invalid Table Errors

    +

    If you are using macOS and see errors like Invalid (old?) table or database name '._column_stats', it may be because you are running MariaDB on a file system like ExFAT that does not support extended attributes. In this case, macOS automatically creates these files and MariaDB then reports them as invalid tables (which is technically correct). To remove extended attribute files, you can run the following in a terminal:

    +
    find . -type f -name '._*' -delete
    +
    +

    Unless you open the storage folder again in macOS Finder, the errors should then be gone after restarting the database.

    +

    Corrupted Files

    +

    Server Crashes

    +

    Lost Root Password

    +

    In case you forgot the MariaDB "root" password and the one specified in your configuration does not work, +you can start the server with the --skip-grant-tables flag +added to the mysqld command in your compose.yaml or docker-compose.yml. This will temporarily give full access +to all users after a restart:

    +
    services:
    +  mariadb:
    +    command: --skip-grant-tables
    +
    +

    Restart the mariadb service for changes to take effect:

    +
    docker compose stop mariadb
    +docker compose up -d mariadb
    +
    +

    Now open a database console:

    +
    docker compose exec mariadb mysql -uroot
    +
    +

    Enter the following commands to change the password for "root":

    +
    FLUSH PRIVILEGES;
    +ALTER USER 'root'@'%' IDENTIFIED BY 'new_password';
    +ALTER USER 'root'@'localhost' IDENTIFIED BY 'new_password';
    +exit
    +
    +

    When you are done, remove the --skip-grant-tables flag again to restore the original +command and restart the mariadb service as described above.

    +

    Server Relocation

    +

    When moving MariaDB to another computer, cloud server, or virtual machine:

    +
      +
    • Move the complete storage folder along with it and preserve the file permissions
    • +
    • or restore your index from an SQL dump (backup file)
    • +
    • Perform a version upgrade if necessary
    • +
    • Make sure that PhotoPrism can access the database on the new host
    • +
    • Set strong passwords if the database is exposed to an external network
    • +
    • Never expose your database to the public Internet
    • +
    +

    Unicode Support

    +

    If the logs show "incorrect string value" database errors and you are running a custom MariaDB or MySQL +server that is not based on our default configuration:

    +
      +
    • Full Unicode support must be enabled, e.g. using the mysqld command parameters --character-set-server=utf8mb4 and --collation-server=utf8mb4_unicode_ci
    • +
    • Note that an existing database may use a different character set if you imported it from another server
    • +
    • Before submitting a support request, verify the problem still occurs with a newly created database based on our example
    • +
    +

    Run this command in a terminal to see the current values of the collation and character set variables (change the root +password insecure and database name photoprism as specified in your compose.yaml or docker-compose.yml):

    +
    echo "SHOW VARIABLES WHERE Variable_name LIKE 'character\_set\_%' OR Variable_name LIKE 'collation%';" | \
    +docker compose exec -T mariadb mysql -uroot -pinsecure photoprism
    +
    +

    MySQL Errors

    +

    Official support for MySQL 8 is discontinued as Oracle seems to have stopped shipping new features and enhancements. +As a result, the testing effort required before each release is no longer feasible.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/troubleshooting/metadata/index.html b/getting-started/troubleshooting/metadata/index.html new file mode 100644 index 0000000000..910e305949 --- /dev/null +++ b/getting-started/troubleshooting/metadata/index.html @@ -0,0 +1,7666 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Metadata - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Checking Image and Video Metadata

    +

    We recommend checking the file metadata with Exiftool if some of your pictures are displayed incorrectly (stretched, distorted)1, information seems to be missing (e.g. title or description), or the wrong time and location are shown.

    +

    To do this, run the following command within a Docker terminal session or on your host if you have Exiftool installed (-n displays the raw values without changes, -j will format the output as JSON, and -g optionally groups the output by metadata source):

    +
    exiftool -n -j [filename]
    +
    +

    If the file specified with [filename] contains readable metadata, it will then be displayed to you as JSON-formatted values, for example:

    +
    [{
    +  "SourceFile": "example.jpg",
    +  "ExifToolVersion": 12.76,
    +  "FileSize": 200108,
    +  "FileType": "JPEG",
    +  "MIMEType": "image/jpeg",
    +  "Make": "HUAWEI",
    +  "Model": "ELE-L29",
    +  "Orientation": 1,
    +  "ExposureTime": 0.02,
    +  "FNumber": 1.8,
    +  "ISO": 100,
    +  "DateTimeOriginal": "2020:10:17 17:48:24",
    +  "CreateDate": "2020:10:17 17:48:24",
    +  "ImageWidth": 500,
    +  "ImageHeight": 375,
    +  "Aperture": 1.8,
    +  "ShutterSpeed": 0.02,
    +  "SubSecCreateDate": "2020:10:17 17:48:24.950488",
    +  "SubSecDateTimeOriginal": "2020:10:17 17:48:24.950488",
    +  "SubSecModifyDate": "2020:10:17 17:48:24.950488",
    +  "GPSAltitude": 84.47,
    +  "GPSDateTime": "2020:10:17 15:48:23Z",
    +  "GPSLatitude": 33.8120962,
    +  "GPSLongitude": -117.9215491
    +}]
    +
    +

    This allows you to check e.g. the values for Orientation and Rotation if you have problems with the image orientation.

    +

    When you post the output on GitHub or in our Community Chat, please format it as follows for better readability:

    +
    ```json
    +[{
    +  "SourceFile": "example.jpg",
    +  "ExifToolVersion": 12.76,
    +  ...
    +}]
    +```
    +
    + +

    Thank you very much!

    +

    Exif Orientation

    +

    The numbers used in Exif metadata to specify the image orientation are defined as follows:

    +
      +
    1. = 0 degrees: the correct orientation, no adjustment is required.
    2. +
    3. = 0 degrees, mirrored: image has been flipped back-to-front.
    4. +
    5. = 180 degrees: image is upside down.
    6. +
    7. = 180 degrees, mirrored: image has been flipped back-to-front and is upside down.
    8. +
    9. = 90 degrees: image has been flipped back-to-front and is on its side.
    10. +
    11. = 90 degrees, mirrored: image is on its side.
    12. +
    13. = 270 degrees: image has been flipped back-to-front and is on its far side.
    14. +
    15. = 270 degrees, mirrored: image is on its far side.
    16. +
    +

    Learn more ›

    +

    Installing Exiftool

    +

    Running the following commands will install Exiftool on Debian or Ubuntu Linux if needed:

    +
    sudo apt update
    +sudo apt install -y exiftool
    +
    +

    See the Exiftool documentation for how to install it on other operating systems.

    +
    +
    +
      +
    1. +

      If images are displayed in low resolution or slightly distorted, this may also be due to a problem with the thumbnail cache folder or your quality settings

      +
    2. +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/troubleshooting/performance/index.html b/getting-started/troubleshooting/performance/index.html new file mode 100644 index 0000000000..3062414dd7 --- /dev/null +++ b/getting-started/troubleshooting/performance/index.html @@ -0,0 +1,7813 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Performance - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Performance Tips

    +

    MariaDB

    +

    The InnoDB buffer pool serves as a cache for data and indexes. +It is a key component for optimizing MariaDB performance. Its size should be as large as possible to keep frequently +used data in memory and reduce disk I/O - typically the biggest bottleneck.

    +

    By default, the buffer pool size is between 128 MB and 512 MB, depending on which configuration example you use. You can change it with the --innodb-buffer-pool-size command parameter in the mariadb: section of your config file. M stands for Megabyte, G for Gigabyte. Do not use spaces.

    +

    If your server has plenty of physical memory, we recommend increasing the size to 1 or 2 GB:

    +
    services:
    +  mariadb:
    +    command: --innodb-buffer-pool-size=1G ...
    +
    +

    As a rule of thumb, Innodb_buffer_pool_pages_free should never be less than 5% of the total pages. +You can run the following SQL statement, for example using the mariadb command in a terminal, to display the number of free pages and other InnoDB-related status information:

    +
    SHOW GLOBAL STATUS LIKE 'Innodb_buffer%';
    +
    +

    Advanced users may adjust additional parameters to further improve performance. Tools such as the mysqltuner.pl script can provide helpful recommendations for this.

    +
    +

    Windows and macOS

    +

    If you are using Docker Desktop on Windows or macOS, remember to increase the total memory available for Docker services. Otherwise, they may run out of resources and cannot benefit from a larger cache size. In case PhotoPrism and MariaDB are running in a virtual machine, its memory size should be increased as well. Restart for changes to take effect.

    +
    +

    Migration from SQLite

    +

    After migrating from SQLite, it is possible that columns do not have exactly the data type they should have or that indexes are missing. This can lead to poor performance. For example, MariaDB cannot process rows with text columns in memory and always uses temporary tables on disk if there are any.

    +

    The instructions for these migrations were provided by a contributor and are not part of the original software distribution. As such, they have not been officially released, recommended, or extensively tested by us.

    +

    If this is the case, please make sure that your migrated database schema matches that of a fresh, non-migrated installation . It may help to run the migrations manually in a terminal using the migrations subcommands. However, this does not guarantee that all issues such as missing indexes are resolved.

    +

    View Database Schema ›

    +

    Windows

    +

    Solve Windows-Specific Issues ›

    +

    Storage

    +

    Local Solid-State Drives (SSDs) are best for databases +of any kind:

    +
      +
    • database performance extremely benefits from high throughput which HDDs can't provide
    • +
    • SSDs have more predictable performance and can handle more concurrent requests
    • +
    • due to the HDD seek time, HDDs only support 5% of the reads per second of SSDs
    • +
    • the cost savings from using slow hard disks are minimal
    • +
    +

    Switching to SSDs makes a big difference, especially for write operations and when the read cache is not +big enough or can't be used.

    +
    +

    Never store database files on an unreliable device such as a USB flash drive, SD card, or shared network folder. These may also have unexpected file size limitations, which is especially problematic for databases that do not split data into smaller files.

    +
    +

    Memory

    +

    Indexing large photo and video collections benefits from plenty of memory for caching and processing large media files. +Ideally, the amount of RAM should match the number of physical CPU cores. If not, reduce the number of workers as explained below.

    +

    Also ensure that your server has at least 4 GB of swap configured and avoid setting a hard memory limit as this can cause unexpected restarts when the indexer temporarily needs more memory to process large files. Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    +
    +

    RAW image conversion and TensorFlow are disabled on systems with 1 GB or less memory. We take no responsibility +for instability or performance problems if your device does not meet the requirements.

    +
    +

    Server CPU

    +

    Last but not least, performance can be limited by your server CPU. If you've tried everything else, then only moving +your instance to a more powerful device or cloud server may help.

    +

    Be aware that most NAS devices are +optimized for minimal power consumption and low production costs. Although their hardware gets faster with each generation, +benchmarks show that even 8-year-old standard desktop CPUs like the Intel Core i3-4130 are often many times faster:

    +

    CPU Benchmark

    +

    Legacy Hardware

    +

    It is a known issue that the user interface and backend operations, especially face recognition, can be slow or even crash on older hardware due to a lack of resources. Like most applications, PhotoPrism has certain requirements and our development process does not include testing on unsupported or unusual hardware.

    +

    In many cases, performance can be improved through optimizations. Since these can prove to be very time-consuming and cost-intensive in practice, users and developers must decide on a case-by-case basis whether this provides sufficient benefit in relation to the costs or whether the use of more powerful hardware is faster and cheaper overall.

    +

    We kindly ask you not to open a problem report on GitHub Issues for poor performance on older hardware until a full cause and feasibility analysis has been performed. GitHub Discussions or any of our other public forums and communities are great places to start a discussion.

    +

    That being said, one of the advantages of open-source software is that users can submit pull requests with performance and other enhancements they would like to see implemented. This will result in a much faster solution than waiting for a core team member to remotely analyze your problem and then provide a fix.

    +

    Troubleshooting

    +

    If your server runs out of memory, the index is frequently locked, or other system resources are running low:

    + +

    Other issues? Our troubleshooting checklists help you quickly diagnose and solve them.

    +
    +

    You are welcome to ask for help in our community chat. +Sponsors receive direct technical support via email. +Before submitting a support request, try to determine the cause of your problem.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/troubleshooting/raspberry-pi/index.html b/getting-started/troubleshooting/raspberry-pi/index.html new file mode 100644 index 0000000000..cc3123eea5 --- /dev/null +++ b/getting-started/troubleshooting/raspberry-pi/index.html @@ -0,0 +1,7603 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Raspberry Pi - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Troubleshooting Raspberry Pi Problems

    +
    +

    You are welcome to ask for help in our community chat. +Sponsors receive direct technical support via email. +Before submitting a support request, try to determine the cause of your problem.

    +
    +

    Hardware Watchdog Initiates Reboot

    +

    A watchdog timer is an electronic timer that is used to detect and correct computer malfunctions. When activated, it can trigger a reboot when the computer is under heavy load, e.g. when indexing pictures.

    +

    Should your Raspberry Pi fail to reset the timer before it expires, the WDT signal will reboot it. +It is disabled by default in the firmware.

    +

    Users can set up "dtparam" also known as Device Tree config files for Raspberry Pi's in /boot/config.txt, which will enable the kernel module.

    +

    It is the responsibility of the user to set the parameters of the watchdog daemon correctly.

    +

    A common parameter that users set is the average CPU load for 1, 5, or 15 minutes. The default value for the 1 minute span is 24

    +
    max-load-1 = 24
    +
    +

    The average load is the sum of the queue length and the number of jobs currently running on the CPUs. You can use the following commands to view load average statistics:

    +
    uptime, procinfo, w, top
    +
    +

    Raspberry Pi users who have the hardware watchdog enabled need to set a more appropriate value for the 1-minute span for the maximum load than the default value. +As a workaround, you can log the average workload to a file:

    +
    #!/bin/bash
    +
    +while true; do
    +echo $(cat /proc/loadavg) >> test_file.log
    +sleep 10
    +done
    +
    +

    This will write a log with a timestamp every 10 seconds. Run the above script while performing intensive tasks that would normally trigger a reboot, such as tagging faces. With this information, you can now set a new value that is greater than the recorded maximum.

    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/troubleshooting/sqlite/index.html b/getting-started/troubleshooting/sqlite/index.html new file mode 100644 index 0000000000..14402c808c --- /dev/null +++ b/getting-started/troubleshooting/sqlite/index.html @@ -0,0 +1,7686 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SQLite - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Troubleshooting SQLite Problems

    +
    +

    You are welcome to ask for help in our community chat. +Sponsors receive direct technical support via email. +Before submitting a support request, try to determine the cause of your problem.

    +
    +

    Bad Performance

    +

    If you only have few pictures, concurrent users, and CPU cores, SQLite may seem faster compared to full-featured database servers like MariaDB. This changes as the index grows and the number of concurrent accesses increases. While MariaDB is optimized for high concurrency, SQLite frequently locks its index so that other operations have to wait. In the worst case, this can lead to locking errors and timeouts during indexing - especially in combination with a slow disk or network storage.

    +

    The main advantage of SQLite is that you don't need to run a separate database server. It is therefore well suited for testing and can also be sufficient for small libraries with a few thousand files. If you are looking for scalability and high performance, it is not a good choice.

    +

    Get MariaDB Performance Tips ›

    +

    Locking Errors

    +

    If you use traditional hard drives instead of SSDs, you will find that PhotoPrism frequently runs into locking issues with SQLite because your CPU is many times faster than the mechanical heads of your disks. To some extent, this may also happen with solid-state drives, but it is much more likely with slow storage.

    +

    You may be able to optimize the behavior and reduce locking errors with SQLite parameters that you can set with the database DSN config option, but ultimately you should use an SSD if you want to keep SQLite or switch to MariaDB. Please note that our team cannot provide support otherwise.

    +

    Server Crashes

    +

    If the server crashes unexpectedly or your database files get corrupted frequently, it is usually because they are stored on an unreliable device such as a USB flash drive, an SD card, or a shared network folder mounted via NFS or CIFS. These may also have unexpected file size limitations, which is especially problematic for databases that do not split data into smaller files.

    +
      +
    • Never use the same database files with more than one server instance
    • +
    • Use SSDs instead of traditional hard drives, never use network storage
    • +
    • Consider using MariaDB instead of SQLite
    • +
    +

    Corrupted Files

    +

    Server Crashes

    +

    Migrating to MariaDB

    +

    When migrating from SQLite to MariaDB, e.g. using scripts and instructions from the community, you should note that your database schema may no longer be optimized for performance and indexes may be missing. Also, MariaDB cannot handle rows with "text" columns in memory and always uses temporary tables on disk if there are any.

    +

    If this is the case, please make sure that your migrated database schema matches that of a fresh, non-migrated installation . It may help to run the migrations manually in a terminal using the migrations subcommands. However, this does not guarantee that all issues such as missing indexes are resolved.

    +

    Troubleshoot MariaDB Problems ›

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/troubleshooting/windows/index.html b/getting-started/troubleshooting/windows/index.html new file mode 100644 index 0000000000..84135ab2e6 --- /dev/null +++ b/getting-started/troubleshooting/windows/index.html @@ -0,0 +1,7677 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Windows - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Solving Windows-Specific Issues

    +

    NTFS File System

    +

    If you experience poor performance when indexing large libraries stored on NTFS:

    +
      +
    • The I/O bandwidth used to update the Last Access Time can be a significant percentage of the total I/O bandwidth on NTFS volumes with a large number of files or folders (disable updates).1
    • +
    • In folders with many files, file names may start to conflict after NTFS uses all of the 8.3 short file names that are similar to the long names. Repeated conflicts between new and existing short names cause NTFS to regenerate the short file name from 6 to 8 times (disable short file names and reduce the number of files per folder).2 3
    • +
    • exFat can be faster than NTFS, especially on external SSD drives with a lot of small files.
    • +
    • Windows 10 and 11 allow physical disks formatted with the Linux ext4 file system to be mounted directly in WSL 2, which may be an option for some use cases.4
    • +
    +

    Connecting via WebDAV

    +

    If you followed our step-by-step guide and still have trouble connecting via WebDAV:

    +
      +
    • You need to change the basic authentication level (see below)
    • +
    • You do not have sufficient user rights (try as admin)
    • +
    • You are experiencing a general authentication problem
    • +
    • Your instance or reverse proxy uses an invalid HTTPS certificate
    • +
    • You are trying to connect to the wrong network or server
    • +
    +

    To change the basic authentication level in the Windows registry:

    +
      +
    1. Open the Windows Registry Editor.
    2. +
    3. Locate the following registry directory: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters
    4. +
    5. Locate the value BasicAuthLevel.
    6. +
    7. The value data box should be set to 2. If the value is not 2, right click it and then select Modify.
    8. +
    9. Change the value to 2.
    10. +
    +

    WebDAV File Size Limit

    +

    When uploading or downloading large files (more than 50 MB) on Windows, this error may occur:

    +
    Error 0x800700DF: The file size exceeds the limit allowed and cannot be saved
    +
    +

    To allow larger files, you must increase the size limit in the Windows registry:5

    +
      +
    1. Open the Windows Registry Editor.
    2. +
    3. Locate the following registry directory: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters
    4. +
    5. Locate the value FileSizeLimitInBytes.
    6. +
    7. Set the value to 4294967295 (in Decimal).
    8. +
    9. Restart your computer.
    10. +
    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/updates/index.html b/getting-started/updates/index.html new file mode 100644 index 0000000000..2bca1dde29 --- /dev/null +++ b/getting-started/updates/index.html @@ -0,0 +1,7955 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Getting Updates - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Getting Updates

    +

    Docker Compose

    +

    Open a terminal and change to the folder where your compose.yaml or docker-compose.yml file is located.1 +Now run the following commands to download the newest image from Docker Hub and +restart your instance in the background:

    +
    docker compose pull
    +docker compose stop
    +docker compose up -d
    +
    +

    Note that our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    +

    Pulling a new version can take several minutes, depending on your internet connection speed.

    +

    Advanced users can add this to a Makefile so that they only have to type a single +command like make update. See Command-Line Interface +to learn more about terminal commands.

    +
    +

    Even when you use an image with the :latest tag, Docker does not automatically download new images for you. You can either manually upgrade as shown above, or set up a service like Watchtower to get automatic updates.

    +
    +

    Config Examples

    +

    We recommend that you compare your own docker-compose.yml with our latest examples from time to time, as they may include new config options or other enhancements relevant to you.

    +

    Development Preview

    +

    You can test upcoming features and enhancements by changing the photoprism/photoprism image tag from :latest to :preview and then running the following commands to download the newest image from Docker Hub and restart your instance in the background:

    +
    docker compose pull
    +docker compose stop
    +docker compose up -d
    +
    +

    Note that our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    +

    Watchtower

    +

    Adding Watchtower as a service to your compose.yaml or docker-compose.yml will +automatically keep images up-to-date:

    +
    services:
    +  watchtower:
    +    image: containrrr/watchtower
    +    restart: unless-stopped
    +    volumes:
    +      - "/var/run/docker.sock:/var/run/docker.sock"
    +
    +

    Users of our DigitalOcean 1-Click App have Watchtower pre-installed.

    +
    +

    Danger

    +

    Keep in mind that automatic updates can interrupt indexing and import operations, and enable Watchtower only if this is acceptable to you.

    +
    +

    Portainer

    +

    You can change or update the image you are using by navigating to "Stacks", selecting your existing PhotoPrism stack, and clicking "Editor":

    +

    Screenshot

    +

    When you have changed the configuration to your needs or just want to get the latest image from Docker Hub, scroll down, click on "Update the stack", enable the "re-pull and redeploy" option, and then click on "Update":

    +

    Screenshot

    +

    Pure Docker

    +

    Open a terminal on your server, and run the following command to pull the most recent container image:

    +
    docker pull photoprism/photoprism:latest
    +
    +

    See Running PhotoPrism with Docker for a command reference.

    +

    OpenMediaVault

    +

    To upgrade your instance, open a terminal, download our newest image from Docker Hub, and then restart the service:

    +
    podman pull docker.io/photoprism/photoprism:latest
    +systemctl restart pod-photoprism.service
    +
    +

    Complete Rescan

    +

    We recommend performing a complete rescan after major updates to take advantage of new search filters and sorting options. Be sure to read the notes for each release to see what changes have been made and if they might affect your library, for example, because of the file types you have or because new search features have been added. If you encounter problems that you cannot solve otherwise (i.e. before reporting a bug), please also try a rescan and see if it solves the problem.

    +

    You can start a rescan from the user interface by navigating to Library > Index, selecting "Complete Rescan", and then clicking "Start". Manually entered information such as labels, people, titles or descriptions will not be modified when indexing, even if you perform a "complete rescan".

    +
    +

    Be careful not to start multiple indexing processes at the same time, as this will lead to a high server load.

    +
    +

    Face Recognition

    +

    Existing users may index faces without performing a complete rescan:

    +
    docker compose exec photoprism photoprism faces index
    +
    +

    Remove existing people and faces for a clean start e.g. after upgrading from our +development preview:

    +
    docker compose exec photoprism photoprism faces reset -f
    +
    +

    MariaDB Server

    +

    Our configuration examples are generally based on the current stable version to take advantage of performance improvements. This does not mean that older versions are no longer supported and you must upgrade immediately. We recommend not using the :latest tag for the MariaDB Docker image and to upgrade manually by changing the tag once we had a chance to test a new major version, e.g.:

    +
    services:
    +  mariadb:
    +    image: mariadb:11
    +    ...
    +
    +
    +

    If MariaDB fails to start after upgrading from an earlier version (or migrating from MySQL), the internal management schema may be outdated. See Troubleshooting MariaDB Problems for instructions on how to fix this.

    +
    +

    Raspberry Pi

    +

    Our stable releases and preview builds are available as multi-arch Docker images for 64-bit AMD, Intel, and ARM processors. +You therefore get the exact same functionality and can follow the same update instructions if your device meets the system requirements.

    +

    Try explicitly pulling the ARM64 version if you've booted your device with the arm_64bit=1 flag and you see the "no matching manifest" error on Raspberry Pi OS (Raspbian):

    +
    docker pull --platform=arm64 photoprism/photoprism:latest
    +
    +

    If you don't use legacy software, we recommend choosing a standard 64-bit Linux distribution as it requires less experience. For ARMv7-based devices, 32-bit images are provided separately.

    +
    +

    Darktable is not included in the ARMv7 version because it is not 32-bit compatible.

    +
    +

    PhotoPrism® Plus

    +

    Our members can activate additional features by logging in with the admin user created during setup and then following the steps described in our activation guide. Thank you for your support, which has been and continues to be essential to the success of the project!

    +

    Compare Memberships › View Membership FAQ ›

    +
    +

    We recommend that new users install our free Community Edition before signing up for a membership.

    +
    +

    How can I shorten the startup time after a restart or update?

    +

    To reduce startup time, do not set PHOTOPRISM_INIT to avoid running additional setup scripts, and set PHOTOPRISM_DISABLE_CHOWN to "true" to disable automatic permission updates.

    +
    +

    If your instance doesn't start even after waiting for some time, our Troubleshooting Checklists help you quickly diagnose and solve the problem.

    +
    +
    +
    +
      +
    1. +

      The default Docker Compose config filename is docker-compose.yml. For simplicity, it doesn't need to be specified when running docker compose or docker-compose in the same directory. Config files for other apps or instances should be placed in separate folders. 

      +
    2. +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/using-a-cdn/index.html b/getting-started/using-a-cdn/index.html new file mode 100644 index 0000000000..a0b8293306 --- /dev/null +++ b/getting-started/using-a-cdn/index.html @@ -0,0 +1,7744 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Using a CDN - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Using a Content Delivery Network (CDN)

    +

    A Content Delivery Network is a distributed network of servers that can deliver static content to users around the world.

    +

    When to use a CDN?

    +

    Large Media Files: PhotoPrism stores photos and videos that can be very large. A CDN can help speed up the delivery of these files to users.

    +

    Global Audience: If your PhotoPrism instance is accessed from different locations around the world, a CDN can help reduce latency and improve the overall user experience by delivering content from servers that are closer to your users.

    +

    Many Users: If your PhotoPrism instance is getting a lot of traffic, a CDN can improve application performance by reducing the load on your server.

    +

    Network Diagram

    +

    Config Options

    +

    You can use the following config options to specify the URL of an external CDN and change the cache expiration time for thumbnails and other static content:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentCLI FlagDefaultDescription
    PHOTOPRISM_CDN_URL--cdn-urlcontent delivery network URL
    PHOTOPRISM_CDN_VIDEO--cdn-videofalsestream videos over the specified CDN
    PHOTOPRISM_HTTP_CSP--http-cspHTTP Content-Security-Policy (CSP) HEADERplus
    PHOTOPRISM_HTTP_CACHE_PUBLIC--http-cache-publictrueallow static content to be cached by a CDN or caching proxy
    PHOTOPRISM_HTTP_CACHE_MAXAGE--http-cache-maxage2592000time in SECONDS until cached content expires
    PHOTOPRISM_HTTP_VIDEO_MAXAGE--http-video-maxage21600time in SECONDS until cached videos expire
    +

    CDN Providers

    +

    bunny.net

    +

    Bunny CDN +If you don't have a CDN provider yet, we can recommend bunny.net. This EU-based company has a cute name, but is a reputable provider with excellent performance, a wide range of features, and more than 20,000 customers including big names like Hyundai. We also chose bunny.net for our website and public demo as they are fully compliant with the GDPR.1

    +

    Pricing starts at $0.005/GB and there is no minimum usage or monthly fee, so you only pay for what you actually need.

    +

    Learn more ›

    +

    Cloudflare

    +

    Cloudflare works similarly to a reverse proxy and allows you to make a private server publicly accessible over the Internet. This means that users accessing your instance through their service will only see a single URL as if they were connecting directly.

    +

    You must therefore not configure a CDN URL for your site, as this may prevent the user interface from loading. Also note that their free tier does not include video streaming, so there may be problems with video playback if you are not a paying customer.

    +
    +
    +
      +
    1. +

      We receive a $20 credit when you sign up through our link, which helps us fund the project infrastructure. 

      +
    2. +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/using-https/index.html b/getting-started/using-https/index.html new file mode 100644 index 0000000000..608e564863 --- /dev/null +++ b/getting-started/using-https/index.html @@ -0,0 +1,7983 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Using HTTPS - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Using HTTPS

    +

    This guide shows you how to enable HTTPS/TLS, configure existing server certificates, and obtain new certificates as needed. If you have suggestions for improvement, please let us know by clicking to send a pull request.

    +

    Why Use Encryption?

    +

    If you install PhotoPrism on a shared server so that it is not only accessible to the local host, always secure the connection using HTTPS. Your files and passwords will otherwise be transmitted in clear text and can be intercepted by anyone, including your provider, hackers, and governments. Backup tools and file synchronization apps may also refuse to connect.

    +

    +
    +

    HTTPS connections use Transport Layer Security (TLS) for encryption. TLS is a network protocol that establishes an encrypted connection to an authenticated peer over an untrusted network.

    +
    +

    How To Enable HTTPS

    +

    You have the following options to enable HTTPS/TLS when using our latest stable release. +Note that after adding or updating certificates, it is required to restart PhotoPrism for the changes to take effect.

    +

    1. HTTPS Reverse Proxy

    +

    To run your instance behind an HTTPS reverse proxy like Traefik, we recommend that you explicitly disable TLS in PhotoPrism by setting PHOTOPRISM_DISABLE_TLS to "true" in your compose.yaml or docker-compose.yml configuration:

    +
    services:
    +  photoprism:
    +    # ...
    +    environment:
    +      PHOTOPRISM_SITE_URL: "https://www.example.com/"
    +      PHOTOPRISM_DISABLE_TLS: "true"
    +
    +
    +

    Especially if your server also has other web applications installed and/or a proxy with working HTTPS is already in place, this may be the best option.

    +
    +

    2. Self-Signed Certificate

    +
    services:
    +  photoprism:
    +    # ...
    +    environment:
    +      PHOTOPRISM_SITE_URL: "https://www.example.com/"
    +      PHOTOPRISM_DISABLE_TLS: "false"
    +      PHOTOPRISM_DEFAULT_TLS: "true"
    +      PHOTOPRISM_INIT: "https"
    +
    +

    3. Custom Certificate

    +

    To use your own certificates, you can add a custom TLS certificate and private key to the storage/config/certificates folder with the filenames www.example.com.crt and www.example.com.key, replacing www.example.com with the actual server domain. For this, you can set the same config options as when using a self-signed certificate (see above).

    +

    Alternatively, you can specify a custom TLS certificate (*.crt) and private key (*.key) filename within the storage/config/certificates folder using the PHOTOPRISM_TLS_CERT and PHOTOPRISM_TLS_KEY environment variables in your compose.yaml or docker-compose.yml, or use the corresponding command flags:

    +
    services:
    +  photoprism:
    +    # ...
    +    environment:
    +      PHOTOPRISM_SITE_URL: "https://www.example.com/"
    +      PHOTOPRISM_TLS_CERT: "site.crt"
    +      PHOTOPRISM_TLS_KEY: "site.key"
    +      PHOTOPRISM_DISABLE_TLS: "false"
    +      PHOTOPRISM_DEFAULT_TLS: "true"
    +      PHOTOPRISM_INIT: "https"
    +
    +
    +

    We recommend that you keep the PHOTOPRISM_DEFAULT_TLS option enabled so that you can always connect securely over HTTPS even if there is a problem with your custom certificates.

    +
    +

    Obtaining Certificates

    +

    Valid server certificates can be obtained either from a commercial Certificate Authority (CA) like ZeroSSL or free of charge from Let's Encrypt:

    +

    Let’s Encrypt

    +

    Let’s Encrypt +Let's Encrypt is an automatic certificate authority that provides you with free HTTPS/TLS certificates. Many web servers and reverse proxies such as Traefik and Caddy have integrated support for obtaining single-domain certificates if your server is accessible on port 80 over the public Internet.

    +

    The creation of certificates for servers that are not publicly reachable or that are valid for all subdomains (wildcard) is alternatively possible with the LEGO Let's Encrypt client. If you use Docker and DigitalOcean's free DNS service, the command to run will look as follows (replace the certificate path, access token, domain names, and email address with the appropriate values):

    +
    docker run --rm -v "/path/to/certificates:/data/" \
    +-e DO_AUTH_TOKEN=Your_Access_Token goacme/lego -a \
    +-d "example.com" -d "*.example.com" --email="you@example.com" \
    +--dns=digitalocean --dns-timeout=180 --path=/data run 
    +
    +

    Note that this verification method only works if you use a supported DNS provider that LEGO can access through an API. Please refer to its documentation for details, as each provider requires different authentication credentials. If you are using DigitalOcean, you can create the required access token in your customer dashboard and replace Your_Access_Token with it.

    +

    ZeroSSL

    +

    ZeroSSL +ZeroSSL is a widely trusted commercial certificate authority with more than 500,000 customers worldwide. Its headquarters are located in Vienna, Austria.

    +

    Compared to Let's Encrypt, you can also create and revoke certificates through a user-friendly web interface, obtain certificates with a validity of more than 90 days, and choose between additional domain verification methods.1

    +

    Learn more ›

    +

    Troubleshooting

    +

    Enabling Trace Log Mode

    +

    A good way to troubleshoot configuration issues is to increase the log level. To enable trace log mode, set PHOTOPRISM_LOG_LEVEL to "trace" in the environment: section of the photoprism service (or use the --trace flag when running the photoprism command directly):

    +
    services:
    +  photoprism:
    +    environment:
    +      PHOTOPRISM_LOG_LEVEL: "trace"
    +      ...
    +
    +

    Then restart all services for your changes to take effect:

    +
    docker compose stop
    +docker compose up -d
    +
    +

    Viewing Docker Service Logs

    +

    You can run this command to check the server logs for warnings and errors, including the last 100 messages (omit --tail=100 to see them all, and -f to output only the last logs without watching them):

    +
    docker compose logs -f --tail=100 
    +
    +

    Learn more ›

    +

    Determining Your Public IP Address

    +

    If you have a domain name and want to set up a public host entry for your home network, you can use one of the following services to see your public IP address, i.e. the IP address that others outside your home network can use to reach you:

    + +

    Failed to Find Any PEM Data in Key Input

    +

    This error can indicate that your key file starts with an unexpected Byte Order Mark (BOM):

    + +

    While BOMs are not strictly forbidden, there is only one way to encode UTF-8, and so they are not needed and extremely rare. As a result, a lot of software has problems with them.

    +

    You should be able to fix this by opening the file with a regular text or code editor (not Notepad) and then saving it again. Finally, restart all services for the changes to take effect:

    +
    docker compose stop
    +docker compose up -d
    +
    +
    +

    Our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    +
    +
    +
    +
      +
    1. +

      We may receive a credit when you sign up through our link, which helps us fund the project infrastructure. 

      +
    2. +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/getting-started/vpn/img/tailscale-1.png b/getting-started/vpn/img/tailscale-1.png new file mode 100644 index 0000000000..2018366623 Binary files /dev/null and b/getting-started/vpn/img/tailscale-1.png differ diff --git a/getting-started/vpn/img/tailscale-2.png b/getting-started/vpn/img/tailscale-2.png new file mode 100644 index 0000000000..5c3606d8c6 Binary files /dev/null and b/getting-started/vpn/img/tailscale-2.png differ diff --git a/getting-started/vpn/img/tailscale-3.png b/getting-started/vpn/img/tailscale-3.png new file mode 100644 index 0000000000..e17665526c Binary files /dev/null and b/getting-started/vpn/img/tailscale-3.png differ diff --git a/getting-started/vpn/img/tailscale-4.png b/getting-started/vpn/img/tailscale-4.png new file mode 100644 index 0000000000..a17b82bb44 Binary files /dev/null and b/getting-started/vpn/img/tailscale-4.png differ diff --git a/getting-started/vpn/img/tailscale-5.png b/getting-started/vpn/img/tailscale-5.png new file mode 100644 index 0000000000..0686741f49 Binary files /dev/null and b/getting-started/vpn/img/tailscale-5.png differ diff --git a/getting-started/vpn/img/tailscale-6.png b/getting-started/vpn/img/tailscale-6.png new file mode 100644 index 0000000000..0f3e965343 Binary files /dev/null and b/getting-started/vpn/img/tailscale-6.png differ diff --git a/getting-started/vpn/img/tailscale-7.png b/getting-started/vpn/img/tailscale-7.png new file mode 100644 index 0000000000..3c37c6be95 Binary files /dev/null and b/getting-started/vpn/img/tailscale-7.png differ diff --git a/getting-started/vpn/img/tailscale-8.png b/getting-started/vpn/img/tailscale-8.png new file mode 100644 index 0000000000..7a58ce4244 Binary files /dev/null and b/getting-started/vpn/img/tailscale-8.png differ diff --git a/getting-started/vpn/tailscale/index.html b/getting-started/vpn/tailscale/index.html new file mode 100644 index 0000000000..1a8c538952 --- /dev/null +++ b/getting-started/vpn/tailscale/index.html @@ -0,0 +1,7637 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tailscale VPN - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Tailscale VPN

    +
    +

    Should you experience problems with Tailscale, we recommend that you ask the Tailscale community for advice, as we cannot provide support for third-party software and services.

    +
    +
      +
    1. +

      Open the Tailscale website and Select Use Tailscale button.

      +
    2. +
    3. +

      Sign up with an email address or using any of your other accounts and install Tailscale on all the relevant devices. Detailed and clear instructions are available to guide your through the process depending on the operating system.

      +

      +

      For example below the instruction for Linux and Android

      +

      +
    4. +
    5. +

      When the devices have Tailscale installed (and logged in via the same account) they all appear in the overview as in the printscreen below.

      +

      +
    6. +
    7. +

      Each device gets an IP address allocated (100.xxx.xxx.xxx), which can be used to reach them via the VPN network. This IP address with addition of the port number where Photoprism is run on can be used to reach Photoprism outside the home network.

      +

      +
    8. +
    +

    Deny incoming vpn traffic from cloud server

    +

    If you have a Photoprism instance running on a cloud server, you might want to be able to connect from your desktop computer to the cloud server and deny connections from the cloud server. Denying connections from the cloud server is useful if it is compromised.

    +

    That can be achieved by setting up ACL's (Access Control Lists). Read about ACL's and tags in the tailscale documentation.

    +

    This example shows how to:

    +
      +
    • Create two tags "lan" and "cloud"
    • +
    • Create an ACL that allows machines tagged "lan" to connect to every machine in the tailnet.
    • +
    • Tags a desktop machine with "lan" and a cloud server with "cloud"
    • +
    +

    As the cloud server is not specifically allowed to connect anywhere on the tailnet, it cannot connect to machines tagged "lan".

    +

    Step 1

    +

    Go to your tailscale acl admin console.

    +

    Add the two tags and the acl as marked on the screenshot below:

    +

    +

    Step 2

    +

    Go to your tailscale machines admin console.

    +

    Add the "tag:lan" and "tag:cloud" to your desktop and cloud machines as demonstrated:

    +

    Open the ACL tags dialog window:

    +

    +

    Add/remove tags as needed and click on "Save":

    +

    +

    Step 3

    +

    Verify the changes work as intended.

    +
      +
    • +

      Connect to your cloud based PhotoPrism instance and make sure it works. You could try to do this:

      +
        +
      • Create an album
      • +
      • Tag some photos
      • +
      • Add the tagged photos to the new album
      • +
      +
    • +
    • +

      ssh into the cloud server from your desktop machine, that should work.

      +
    • +
    • +

      From the cloud server, ping your desktop, ssh into it or something else. All of it should fail.

      +
    • +
    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/icons/LICENSE b/icons/LICENSE new file mode 100644 index 0000000000..a4b2f9e83e --- /dev/null +++ b/icons/LICENSE @@ -0,0 +1,75 @@ + + PhotoPrism® Digital Assets + +TERMS OF USE + + (a) We hereby grant you a non-exclusive, non-transferable right to use the +Digital Assets accompanying the software, embedded in the documentation, or +provided for download — such as icons, fonts, illustrations, graphics, +wallpapers, videos, sounds, models, and sample files („Digital Assets“) — +as part of the Software distributed by us, unless otherwise noted. + + (b) Because some Digital Assets are licensed to us solely for direct +distribution, we cannot redistribute them under a more permissive license for +other purposes. If the author or copyright holder has not released them under a +permissive license, you must obtain a license before using them in your own +work, whether commercial or non-commercial in nature. + + (c) Related documentation is available under the terms of the CC BY-NC-SA 4.0 +License. Other terms may apply to Digital Assets — in particular +illustrations, graphics, and videos — embedded in the documentation if they +are licensed to us solely for direct distribution. When in doubt, please ask +before distributing or using them for other works. + +TRADEMARK AND BRAND ASSETS + + (a) PhotoPrism’s Brand Assets — including trademarks, logos, icons, fonts, +corporate design, product and service names, and any other brand features and +elements, whether registered or unregistered („Brand Assets“) — are +proprietary assets owned exclusively by PhotoPrism UG („PhotoPrism“). We +reserve the right to object to any use or misuse in any jurisdiction worldwide. +Visit photoprism.app/trademark to learn more. + + (b) Contributors, licensees, business partners, and other third parties may +never claim ownership of PhotoPrism's Brand Assets or brands confusingly +similar to PhotoPrism's Brand Assets in any way, including, without limitation, +as a trademark, service mark, company name or designation, domain name, social +media profile/handle, or in any other manner. + + (c) You may not include the PhotoPrism trademark in the name of your app, +product, or service, whether commercial or non-commercial in nature. This +includes online services such as e-commerce, community, blog, information, +advertising, and personal home pages, as well as apps, app stores, client apps, +or third-party apps that interact with PhotoPrism. + +DISCLAIMER OF WARRANTY + + OUR SOFTWARE, SERVICES, DOCUMENTATION, AND DIGITAL ASSETS ARE PROVIDED +“AS-IS” AND WITHOUT WARRANTY OF ANY KIND. WE DISCLAIM ALL WARRANTIES, +EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, TITLE, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE. + +LIMITATION OF LIABILITY + + WE WILL NOT BE LIABLE FOR ANY DAMAGES ASSOCIATED WITH OUR SOFTWARE AND +SERVICES, INCLUDING WITHOUT LIMITATION ORDINARY, INCIDENTAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OF ANY KIND, INCLUDING BUT NOT LIMITED TO DAMAGES +RELATING TO LOST DATA OR LOST PROFITS, EVEN IF WE HAVE BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +SEVERABILITY + + (a) If the Disclaimer of Warranty and Limitation of Liability set forth above +cannot be given local legal effect according to their terms, the reviewing +courts shall apply the local law that most closely approximates an absolute +waiver of any civil liability in connection with our software and services, +unless a written warranty or assumption of liability has been granted for a fee. + + (b) In the event that any provision set forth above is found by a court of +competent jurisdiction to be invalid, illegal, void or unenforceable, the +remaining provisions shall remain in full force and effect and shall not be +impaired, affected or invalidated in any way. + + (c) It is hereby agreed and declared that it is the intention of the parties to +enforce the remaining provisions without the inclusion of those provisions that +may subsequently be declared invalid, illegal, void or unenforceable. diff --git a/icons/favicon.ico b/icons/favicon.ico new file mode 100644 index 0000000000..b3980a2614 Binary files /dev/null and b/icons/favicon.ico differ diff --git a/icons/logo-black.svg b/icons/logo-black.svg new file mode 100644 index 0000000000..f9e04af4c0 --- /dev/null +++ b/icons/logo-black.svg @@ -0,0 +1 @@ + diff --git a/icons/logo-white.svg b/icons/logo-white.svg new file mode 100644 index 0000000000..1677785ebd --- /dev/null +++ b/icons/logo-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/logo.svg b/icons/logo.svg new file mode 100644 index 0000000000..5b48471e73 --- /dev/null +++ b/icons/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/logo/100.png b/icons/logo/100.png new file mode 100644 index 0000000000..48f2a7f214 Binary files /dev/null and b/icons/logo/100.png differ diff --git a/icons/logo/1024.png b/icons/logo/1024.png new file mode 100644 index 0000000000..3439926435 Binary files /dev/null and b/icons/logo/1024.png differ diff --git a/icons/logo/114.png b/icons/logo/114.png new file mode 100644 index 0000000000..ea9cbd5fe1 Binary files /dev/null and b/icons/logo/114.png differ diff --git a/icons/logo/120.png b/icons/logo/120.png new file mode 100644 index 0000000000..aa35fe8890 Binary files /dev/null and b/icons/logo/120.png differ diff --git a/icons/logo/128.png b/icons/logo/128.png new file mode 100644 index 0000000000..dbec90e340 Binary files /dev/null and b/icons/logo/128.png differ diff --git a/icons/logo/144.png b/icons/logo/144.png new file mode 100644 index 0000000000..506b13e3d2 Binary files /dev/null and b/icons/logo/144.png differ diff --git a/icons/logo/152.png b/icons/logo/152.png new file mode 100644 index 0000000000..182c87004f Binary files /dev/null and b/icons/logo/152.png differ diff --git a/icons/logo/16.png b/icons/logo/16.png new file mode 100644 index 0000000000..0c6aa19144 Binary files /dev/null and b/icons/logo/16.png differ diff --git a/icons/logo/160.png b/icons/logo/160.png new file mode 100644 index 0000000000..db6fcd572e Binary files /dev/null and b/icons/logo/160.png differ diff --git a/icons/logo/167.png b/icons/logo/167.png new file mode 100644 index 0000000000..dba5622de0 Binary files /dev/null and b/icons/logo/167.png differ diff --git a/icons/logo/172.png b/icons/logo/172.png new file mode 100644 index 0000000000..3743a9f0ab Binary files /dev/null and b/icons/logo/172.png differ diff --git a/icons/logo/175.png b/icons/logo/175.png new file mode 100644 index 0000000000..1b69491fd5 Binary files /dev/null and b/icons/logo/175.png differ diff --git a/icons/logo/180.png b/icons/logo/180.png new file mode 100644 index 0000000000..ded6fbafbc Binary files /dev/null and b/icons/logo/180.png differ diff --git a/icons/logo/192.png b/icons/logo/192.png new file mode 100644 index 0000000000..72ee048c11 Binary files /dev/null and b/icons/logo/192.png differ diff --git a/icons/logo/196.png b/icons/logo/196.png new file mode 100644 index 0000000000..cd9764ce8c Binary files /dev/null and b/icons/logo/196.png differ diff --git a/icons/logo/20.png b/icons/logo/20.png new file mode 100644 index 0000000000..e6ddfbb249 Binary files /dev/null and b/icons/logo/20.png differ diff --git a/icons/logo/216.png b/icons/logo/216.png new file mode 100644 index 0000000000..1f1ce82f69 Binary files /dev/null and b/icons/logo/216.png differ diff --git a/icons/logo/256.png b/icons/logo/256.png new file mode 100644 index 0000000000..63a5d01cb1 Binary files /dev/null and b/icons/logo/256.png differ diff --git a/icons/logo/267.png b/icons/logo/267.png new file mode 100644 index 0000000000..75fc58353b Binary files /dev/null and b/icons/logo/267.png differ diff --git a/icons/logo/29.png b/icons/logo/29.png new file mode 100644 index 0000000000..05093bb4bb Binary files /dev/null and b/icons/logo/29.png differ diff --git a/icons/logo/32.png b/icons/logo/32.png new file mode 100644 index 0000000000..c1e25f07c1 Binary files /dev/null and b/icons/logo/32.png differ diff --git a/icons/logo/40.png b/icons/logo/40.png new file mode 100644 index 0000000000..376942c544 Binary files /dev/null and b/icons/logo/40.png differ diff --git a/icons/logo/400.png b/icons/logo/400.png new file mode 100644 index 0000000000..cfbf42a096 Binary files /dev/null and b/icons/logo/400.png differ diff --git a/icons/logo/48.png b/icons/logo/48.png new file mode 100644 index 0000000000..307cb69332 Binary files /dev/null and b/icons/logo/48.png differ diff --git a/icons/logo/50.png b/icons/logo/50.png new file mode 100644 index 0000000000..a30d56a837 Binary files /dev/null and b/icons/logo/50.png differ diff --git a/icons/logo/512.png b/icons/logo/512.png new file mode 100644 index 0000000000..6ae653d80d Binary files /dev/null and b/icons/logo/512.png differ diff --git a/icons/logo/55.png b/icons/logo/55.png new file mode 100644 index 0000000000..7fc836c9cf Binary files /dev/null and b/icons/logo/55.png differ diff --git a/icons/logo/56.png b/icons/logo/56.png new file mode 100644 index 0000000000..15c202fce0 Binary files /dev/null and b/icons/logo/56.png differ diff --git a/icons/logo/60.png b/icons/logo/60.png new file mode 100644 index 0000000000..c6d991010c Binary files /dev/null and b/icons/logo/60.png differ diff --git a/icons/logo/64.png b/icons/logo/64.png new file mode 100644 index 0000000000..412d82608d Binary files /dev/null and b/icons/logo/64.png differ diff --git a/icons/logo/72.png b/icons/logo/72.png new file mode 100644 index 0000000000..88f9db49c5 Binary files /dev/null and b/icons/logo/72.png differ diff --git a/icons/logo/76.png b/icons/logo/76.png new file mode 100644 index 0000000000..eea791d1c3 Binary files /dev/null and b/icons/logo/76.png differ diff --git a/icons/logo/80.png b/icons/logo/80.png new file mode 100644 index 0000000000..a179b89b17 Binary files /dev/null and b/icons/logo/80.png differ diff --git a/img/LICENSE b/img/LICENSE new file mode 100644 index 0000000000..a4b2f9e83e --- /dev/null +++ b/img/LICENSE @@ -0,0 +1,75 @@ + + PhotoPrism® Digital Assets + +TERMS OF USE + + (a) We hereby grant you a non-exclusive, non-transferable right to use the +Digital Assets accompanying the software, embedded in the documentation, or +provided for download — such as icons, fonts, illustrations, graphics, +wallpapers, videos, sounds, models, and sample files („Digital Assets“) — +as part of the Software distributed by us, unless otherwise noted. + + (b) Because some Digital Assets are licensed to us solely for direct +distribution, we cannot redistribute them under a more permissive license for +other purposes. If the author or copyright holder has not released them under a +permissive license, you must obtain a license before using them in your own +work, whether commercial or non-commercial in nature. + + (c) Related documentation is available under the terms of the CC BY-NC-SA 4.0 +License. Other terms may apply to Digital Assets — in particular +illustrations, graphics, and videos — embedded in the documentation if they +are licensed to us solely for direct distribution. When in doubt, please ask +before distributing or using them for other works. + +TRADEMARK AND BRAND ASSETS + + (a) PhotoPrism’s Brand Assets — including trademarks, logos, icons, fonts, +corporate design, product and service names, and any other brand features and +elements, whether registered or unregistered („Brand Assets“) — are +proprietary assets owned exclusively by PhotoPrism UG („PhotoPrism“). We +reserve the right to object to any use or misuse in any jurisdiction worldwide. +Visit photoprism.app/trademark to learn more. + + (b) Contributors, licensees, business partners, and other third parties may +never claim ownership of PhotoPrism's Brand Assets or brands confusingly +similar to PhotoPrism's Brand Assets in any way, including, without limitation, +as a trademark, service mark, company name or designation, domain name, social +media profile/handle, or in any other manner. + + (c) You may not include the PhotoPrism trademark in the name of your app, +product, or service, whether commercial or non-commercial in nature. This +includes online services such as e-commerce, community, blog, information, +advertising, and personal home pages, as well as apps, app stores, client apps, +or third-party apps that interact with PhotoPrism. + +DISCLAIMER OF WARRANTY + + OUR SOFTWARE, SERVICES, DOCUMENTATION, AND DIGITAL ASSETS ARE PROVIDED +“AS-IS” AND WITHOUT WARRANTY OF ANY KIND. WE DISCLAIM ALL WARRANTIES, +EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, TITLE, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE. + +LIMITATION OF LIABILITY + + WE WILL NOT BE LIABLE FOR ANY DAMAGES ASSOCIATED WITH OUR SOFTWARE AND +SERVICES, INCLUDING WITHOUT LIMITATION ORDINARY, INCIDENTAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OF ANY KIND, INCLUDING BUT NOT LIMITED TO DAMAGES +RELATING TO LOST DATA OR LOST PROFITS, EVEN IF WE HAVE BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +SEVERABILITY + + (a) If the Disclaimer of Warranty and Limitation of Liability set forth above +cannot be given local legal effect according to their terms, the reviewing +courts shall apply the local law that most closely approximates an absolute +waiver of any civil liability in connection with our software and services, +unless a written warranty or assumption of liability has been granted for a fee. + + (b) In the event that any provision set forth above is found by a court of +competent jurisdiction to be invalid, illegal, void or unenforceable, the +remaining provisions shall remain in full force and effect and shall not be +impaired, affected or invalidated in any way. + + (c) It is hereby agreed and declared that it is the intention of the parties to +enforce the remaining provisions without the inclusion of those provisions that +may subsequently be declared invalid, illegal, void or unenforceable. diff --git a/img/Logs.jpg b/img/Logs.jpg new file mode 100644 index 0000000000..b43bc7cbff Binary files /dev/null and b/img/Logs.jpg differ diff --git a/img/Logs.png b/img/Logs.png new file mode 100644 index 0000000000..a49942d44a Binary files /dev/null and b/img/Logs.png differ diff --git a/img/badge-pixls-us.svg b/img/badge-pixls-us.svg new file mode 100644 index 0000000000..846b39ba1e --- /dev/null +++ b/img/badge-pixls-us.svg @@ -0,0 +1 @@ +discuss: pixls.us/photoprismdiscusspixls.us/photoprism \ No newline at end of file diff --git a/img/badge-reddit.svg b/img/badge-reddit.svg new file mode 100644 index 0000000000..262a639100 --- /dev/null +++ b/img/badge-reddit.svg @@ -0,0 +1 @@ +reddit: /r/photoprismreddit/r/photoprism \ No newline at end of file diff --git a/img/concept.jpg b/img/concept.jpg new file mode 100644 index 0000000000..3fd1c30df9 Binary files /dev/null and b/img/concept.jpg differ diff --git a/img/credits.jpg b/img/credits.jpg new file mode 100644 index 0000000000..2ad5f60d42 Binary files /dev/null and b/img/credits.jpg differ diff --git a/img/empty.jpg b/img/empty.jpg new file mode 100644 index 0000000000..74e34af20d Binary files /dev/null and b/img/empty.jpg differ diff --git a/img/empty.png b/img/empty.png new file mode 100644 index 0000000000..bc436d4a2d Binary files /dev/null and b/img/empty.png differ diff --git a/img/fullscreen.jpg b/img/fullscreen.jpg new file mode 100644 index 0000000000..3bbd7e9261 Binary files /dev/null and b/img/fullscreen.jpg differ diff --git a/img/fullscreen.png b/img/fullscreen.png new file mode 100644 index 0000000000..3306cd5a52 Binary files /dev/null and b/img/fullscreen.png differ diff --git a/img/gopher.jpg b/img/gopher.jpg new file mode 100644 index 0000000000..b15f79ad85 Binary files /dev/null and b/img/gopher.jpg differ diff --git a/img/gopher.png b/img/gopher.png new file mode 100644 index 0000000000..6033e62d53 Binary files /dev/null and b/img/gopher.png differ diff --git a/img/lens.ico b/img/lens.ico new file mode 100644 index 0000000000..15f39979a8 Binary files /dev/null and b/img/lens.ico differ diff --git a/img/lens.jpg b/img/lens.jpg new file mode 100644 index 0000000000..e3df5eb13b Binary files /dev/null and b/img/lens.jpg differ diff --git a/img/lens.png b/img/lens.png new file mode 100644 index 0000000000..3b18763536 Binary files /dev/null and b/img/lens.png differ diff --git a/img/live-photo.svg b/img/live-photo.svg new file mode 100644 index 0000000000..660d17a181 --- /dev/null +++ b/img/live-photo.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/img/metadataExport.jpg b/img/metadataExport.jpg new file mode 100644 index 0000000000..78c90ae448 Binary files /dev/null and b/img/metadataExport.jpg differ diff --git a/img/metadataExport.png b/img/metadataExport.png new file mode 100644 index 0000000000..923f63ca0c Binary files /dev/null and b/img/metadataExport.png differ diff --git a/img/patreon.svg b/img/patreon.svg new file mode 100644 index 0000000000..f57e00ac3e --- /dev/null +++ b/img/patreon.svg @@ -0,0 +1,72 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/img/preview.jpg b/img/preview.jpg new file mode 100644 index 0000000000..56cb6d2422 Binary files /dev/null and b/img/preview.jpg differ diff --git a/img/screenshot.jpg b/img/screenshot.jpg new file mode 100644 index 0000000000..8190d8b07d Binary files /dev/null and b/img/screenshot.jpg differ diff --git a/img/tensorgologo.jpg b/img/tensorgologo.jpg new file mode 100644 index 0000000000..bbb68fdaf7 Binary files /dev/null and b/img/tensorgologo.jpg differ diff --git a/img/tensorgologo.png b/img/tensorgologo.png new file mode 100644 index 0000000000..8a6a187354 Binary files /dev/null and b/img/tensorgologo.png differ diff --git a/img/thank-you-postit.jpg b/img/thank-you-postit.jpg new file mode 100644 index 0000000000..cc080dec36 Binary files /dev/null and b/img/thank-you-postit.jpg differ diff --git a/img/thank-you-postit.png b/img/thank-you-postit.png new file mode 100644 index 0000000000..3a8139e8dd Binary files /dev/null and b/img/thank-you-postit.png differ diff --git a/img/thank-you.jpg b/img/thank-you.jpg new file mode 100644 index 0000000000..43eca9d68a Binary files /dev/null and b/img/thank-you.jpg differ diff --git a/img/uploadStep1.jpg b/img/uploadStep1.jpg new file mode 100644 index 0000000000..31f82b8786 Binary files /dev/null and b/img/uploadStep1.jpg differ diff --git a/img/uploadStep1.png b/img/uploadStep1.png new file mode 100644 index 0000000000..ab912c4f9e Binary files /dev/null and b/img/uploadStep1.png differ diff --git a/img/uploadStep2.jpg b/img/uploadStep2.jpg new file mode 100644 index 0000000000..30ea167c2e Binary files /dev/null and b/img/uploadStep2.jpg differ diff --git a/img/uploadStep2.png b/img/uploadStep2.png new file mode 100644 index 0000000000..f1e0010a30 Binary files /dev/null and b/img/uploadStep2.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000000..be5d2b5fd5 --- /dev/null +++ b/index.html @@ -0,0 +1,7612 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Features - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    PhotoPrism: Browse Your Life in Pictures

    +

    PhotoPrism® is an AI-Powered Photos App for the Decentralized Web. It makes use of the latest technologies to tag and find pictures automatically without getting in your way. You can run it at home, on a private server, or in the cloud.

    +

    Screenshot

    +

    Feature Overview

    +

    Our mission is to provide the most user- and privacy-friendly solution to keep your pictures organized and accessible. That's why PhotoPrism was built from the ground up to run wherever you need it, without compromising freedom, privacy, or functionality:

    + +

    Compare Features ›

    +

    100% Privacy 🔒

    +

    Because PhotoPrism is 100% self-funded and independent, we can promise you that we will never sell your data and that we will always be transparent about our software and services. Your data will never be shared with Google, Amazon, Microsoft or Apple unless you intentionally upload files to one of their services.

    +

    + TRY OUR DEMO + GET STARTED +

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/known-issues/index.html b/known-issues/index.html new file mode 100644 index 0000000000..6da3fa5923 --- /dev/null +++ b/known-issues/index.html @@ -0,0 +1,8240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Known Issues - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Known Issues

    +

    PhotoPrism generally follows a zero-bug policy, which means that we do our best to provide a fix for every technical problem we learn about. +However, sometimes this is not possible right away, for example because it needs to be fixed in a third-party library that our software depends on, or because it is a specific use case that we do not support at this time.

    +
    +

    GitHub Issues

    +

    In order to improve readability and reduce maintenance effort, minor issues and recently reported bugs that we plan to fix in the short term are not listed here, but only in GitHub Issues. When browsing issues, please note that our team and all issue subscribers receive an email notification whenever a new comment is added, so these should only be used for sharing important information and not for discussions, questions, or expressing personal opinions. Thank you very much!

    +
    +

    Self-Hosted Setup

    +

    Shared Domain

    +

    Installation in a sub-directory on a shared domain is generally possible if you run your instance behind a reverse proxy. At the moment, this method is experimental. We do not recommend it because technical expertise is required and a number of specific issues still need to be addressed:

    + +

    Nested Import Folder

    +

    You must not configure the import folder to be inside the originals folder, as this will cause a loop by importing already indexed files.

    +

    Inexperienced users are advised to closely follow our documentation and to use the config examples we provide, as this issue can only occur with a custom setup.

    +

    Nested Storage Folder

    +

    We recommend not to configure the storage folder to be inside the originals folder unless the name starts with a . to indicate that it is hidden.

    +

    In older releases prior to 240420-ef5f14bc4, this could lead to an indexing loop by indexing thumbnails of already indexed files.

    + +

    Symbolic links to files and directories within the originals folder are supported if they are accessible from the environment in which your instance is running. However, you cannot mount a symbolic link as a storage folder or use links within the storage folder.

    +

    Authentication

    +

    Upgrading From Previous Releases

    +

    Should you experience problems after upgrading from a previous release or development preview, we recommend running the photoprism auth reset --yes command in a terminal to reset the auth_sessions table to a clean state and force a re-login of all users. Note that this will also delete all client access tokens and any app passwords that users may have created.

    +

    New User Management

    +

    The session and user management was reimplemented in November 2022. If you are upgrading from a Development Preview with a build number between 221102-905925b4d and 220901-f493607b0, you will need to run the photoprism users reset --yes command in a terminal after the upgrade to recreate the new database tables so that they are compatible with the stable version. This will not affect your pictures or albums.

    +

    Upgrading from the last stable version should work without any problems. However, if you have already created additional accounts with the previously offered unofficial multi-user support, you will notice that only the main admin account is migrated automatically. Run photoprism users legacy in a terminal to display the legacy accounts so you can migrate them manually if needed.

    +

    OpenID Connect (OIDC)

    +

    Changing the authentication of an existing user account to OIDC does not remove a previously set password, so that it can still be used to log in (optionally also in combination with 2FA).

    +

    If a local password has been set for an account, you can remove it by running the photoprism passwd --rm [username] command in a terminal. Alternatively, super admins can set the account password to a long random value through the UI or CLI to effectively prevent local authentication.

    +

    Please also note that you cannot change the authentication provider of your own account through the Admin UI, so you don't accidentally lock yourself out e.g. by setting it to "none".

    +

    Face Recognition

    +

    Legacy Hardware

    +

    Face recognition can be slow (or even crash) on old devices due to insufficient resources.

    +

    Like most applications, PhotoPrism has certain requirements and our development process does not include testing on unsupported or unusual hardware.

    +

    Asian Faces and Children

    +

    It is a known issue that children and Asian-looking faces cannot be recognized reliably. Detection without automatic recognition should not be affected by that.

    +

    This is because the model we use was trained with North American images, which unfortunately do not include many Asians. The absence of children in the training data comes from the fact that parents do not usually share such images under a public license (and may not have the right to do so).

    +

    We will continue to improve our models over time as our resources allow.

    +

    Background Worker

    +

    Face recognition was developed and tested under the assumption that the background worker runs every 15 minutes, unless the backend is busy with other tasks like indexing. It has not been tested with much longer intervals and is not designed for that.

    +

    PhotoPrism's background worker groups new faces by similarity, compares faces with clusters, and optimizes existing clusters as needed. Without these routine tasks, the number of faces to be processed becomes too large. The first and next time the worker runs, it can then cause a heavy server load until all the faces, face clusters, and related pictures have been updated. The longer you wait, the more CPU is required and the longer it takes.

    +

    An important reason for the worker to run independently of actual changes in the main instance is that some users change the database content directly or run additional instances, for example for indexing. It is a problem that can be solved, but it takes time. If we were to ignore this and don't run the worker at all times, it could lead to many additional support requests, further reducing the amount of time we can spend on development.

    +

    The handling of changes in multiple instances will be improved over time so that the worker can be run less frequently in future releases.

    +

    Photos With All Faces Assigned Appear Under “New Faces”

    +

    This can happen when multiple image files are grouped into a stack, for example, because they were taken at the same place and time, and stacking is enabled.

    +

    Secondary images are not searched for faces by default. So the problem is limited to specific cases where you have manually changed the primary image or the images are stacked after detection, which may indicate more fundamental problems, e.g. with the metadata, filenames, or settings.

    +

    One possible solution is to change the primary image of a stack to assign faces to the other images in the stack. You can also manually unstack these files and disable stacking in Settings > Library. Note that files that are already stacked are not automatically unstacked when you change the stacking settings, and that Live Photos do not appear in Stacks because they are a special type of media that is always "stacked".

    +

    Removing Merged Clusters Fails

    +

    Under certain conditions, inconsistent face assignments cannot be automatically resolved by the background worker, which can result in an unusually high CPU load when it is running:

    +
      +
    • if you use multiple browser tabs or windows for assigning faces and don't wait until saving the changes is complete, the likelihood of this problem increases, especially if you accidentally enter different names for the same face
    • +
    • another possible cause is running multiple instances (for example, parallel indexing workers started by a scheduler in the background) or modifying database content directly, as this may also lead to inconsistent faces, markers and subjects
    • +
    • see Faces: Error "Failed removing merged clusters for subject" seems to cause tagging of faces to become slow #2806
    • +
    +

    Running the following command in a terminal can resolve problems with inconsistent data:

    +
    docker compose exec photoprism photoprism faces audit --fix
    +
    +

    It can also be helpful to manually check for inconsistent assignments and fix them in the user interface. +Alternatively, you can use the photoprism faces reset command for a clean start if you haven't invested much time in assigning faces yet.

    +

    Advanced users affected by this are welcome to privately provide us with a SQL dump of their subjects, faces, and markers database tables for debugging. Thank you very much!

    +

    File Compatibility

    +

    JPEG: Bad RST Marker

    +

    This error can occur when decoding JPEG images that contain consecutive 0xFF bytes, e.g. images that have a "glich" (like a few lines of missing image information at the end) or were created with software that inserts them for padding, although this is based on an edge case of the specification and rather uncommon:

    + +

    RAW Converters

    +

    JPEG Size Limit

    +

    RawTherapee and "heif-convert" cannot limit the resolution of JPEG files when converting files from other formats such as RAW, DNG, HEIC or AVIF. In general, when converting images, the resolution of the generated JPEG files can be limited with the environment variable PHOTOPRISM_JPEG_SIZE or the CLI parameter --jpeg-size.

    +

    However, this does not work with certain converters because, unlike Darktable, they do not support CLI options to limit JPEG size:

    + +

    It would probably also hurt indexing performance and image quality if PhotoPrism reduced the size of the generated file after conversion, for example, by using a temporary file.

    +

    As a result, this option is ignored when generating JPEG files with these converters. Whether RawTherapee or "heif-convert" are used depends on additional settings such as PHOTOPRISM_DARKTABLE_BLACKLIST, PHOTOPRISM_DISABLE_DARKTABLE, PHOTOPRISM_RAWTHERAPEE_BLACKLIST, and PHOTOPRISM_DISABLE_RAWTHERAPEE.

    +

    Docker Compose

    +

    Dollar Signs

    +

    If a configuration value in a compose.yaml or docker-compose.yml file contains a literal $ character, for example in a password, you must use $$ (a double dollar sign) to escape it so that e.g. "compo$e" becomes "compo$$e":

    +
    services:
    +  mariadb:
    +    environment:
    +      # sets password to "compo$e"
    +      MARIADB_PASSWORD: "compo$$e"
    +
    +

    Values that contain a $ are otherwise interpreted as a variable. In this case, both the $VARIABLE and the ${VARIABLE} syntax are supported. Further details on the use of variables can be found in the file format reference.

    +

    True / False

    +

    Boolean variable values like "true", "false", "yes", "no", "on", or "off" must be enclosed in double or single quotes so that they are passed as intended:

    +
    services:
    +  photoprism:
    +    environment:
    +      PHOTOPRISM_DEFAULT_TLS: "true"
    +      PHOTOPRISM_READONLY: "false"
    +
    +

    If you otherwise specify true as a value without quotes, Docker Compose will pass the host variable of the same name to the container instead of setting the value to "true" (results in an empty string if no environment variable with the same name is set on the host):

    +
    services:
    +  photoprism:
    +    environment:
    +      # evaluated as "" (false)
    +      PHOTOPRISM_READONLY: true
    +
    +

    Reporting Bugs

    +

    Before reporting a bug, first use our Troubleshooting Checklists to determine the cause of your problem. If you have a general question, need help, it could be a configuration issue, or a misunderstanding in how the software works:

    + +

    In order for us to investigate new bug reports, they must include a complete list of steps to reproduce the problem, the software versions used and information about the environment in which the problem occurred, such as browser type, browser version, browser plug-ins, operating system, storage type, processor type, and memory size.

    +

    A template for creating bug reports can be found at photoprism.app/kb/reporting-bugs. We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app.

    +
    +

    When browsing issues, please note that our team and all issue subscribers receive an email notification from GitHub whenever a new comment is added, so these should only be used for sharing important information and not for discussions, questions, or expressing personal opinions. Thank you very much!

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/license/agpl/index.html b/license/agpl/index.html new file mode 100644 index 0000000000..68bb5ce983 --- /dev/null +++ b/license/agpl/index.html @@ -0,0 +1,8597 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Public License - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    GNU Affero General Public License

    +

    Version 3, 19 November 2007

    +

    Copyright © 2007 Free Software Foundation, Inc.

    +

    Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed.

    +

    Preamble

    +

    The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software.

    +

    The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users.

    +

    When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things.

    +

    Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software.

    +

    A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public.

    +

    The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version.

    +

    An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license.

    +

    The precise terms and conditions for copying, distribution and +modification follow.

    +

    TERMS AND CONDITIONS

    +

    0. Definitions

    +

    “This License” refers to version 3 of the GNU Affero General Public License.

    +

    “Copyright” also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks.

    +

    “The Program” refers to any copyrightable work licensed under this +License. Each licensee is addressed as “you”. “Licensees” and +“recipients” may be individuals or organizations.

    +

    To “modify” a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a “modified version” of the +earlier work or a work “based on” the earlier work.

    +

    A “covered work” means either the unmodified Program or a work based +on the Program.

    +

    To “propagate” a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well.

    +

    To “convey” a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying.

    +

    An interactive user interface displays “Appropriate Legal Notices” +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion.

    +

    1. Source Code

    +

    The “source code” for a work means the preferred form of the work +for making modifications to it. “Object code” means any non-source +form of a work.

    +

    A “Standard Interface” means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language.

    +

    The “System Libraries” of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +“Major Component”, in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it.

    +

    The “Corresponding Source” for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work.

    +

    The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source.

    +

    The Corresponding Source for a work in source code form is that +same work.

    +

    2. Basic Permissions

    +

    All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law.

    +

    You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you.

    +

    Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary.

    + +

    No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures.

    +

    When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures.

    +

    4. Conveying Verbatim Copies

    +

    You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program.

    +

    You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee.

    +

    5. Conveying Modified Source Versions

    +

    You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions:

    +
      +
    • a) The work must carry prominent notices stating that you modified +it, and giving a relevant date.
    • +
    • b) The work must carry prominent notices stating that it is +released under this License and any conditions added under section 7. +This requirement modifies the requirement in section 4 to +“keep intact all notices”.
    • +
    • c) You must license the entire work, as a whole, under this +License to anyone who comes into possession of a copy. This +License will therefore apply, along with any applicable section 7 +additional terms, to the whole of the work, and all its parts, +regardless of how they are packaged. This License gives no +permission to license the work in any other way, but it does not +invalidate such permission if you have separately received it.
    • +
    • d) If the work has interactive user interfaces, each must display +Appropriate Legal Notices; however, if the Program has interactive +interfaces that do not display Appropriate Legal Notices, your +work need not make them do so.
    • +
    +

    A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +“aggregate” if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate.

    +

    6. Conveying Non-Source Forms

    +

    You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways:

    +
      +
    • a) Convey the object code in, or embodied in, a physical product +(including a physical distribution medium), accompanied by the +Corresponding Source fixed on a durable physical medium +customarily used for software interchange.
    • +
    • b) Convey the object code in, or embodied in, a physical product +(including a physical distribution medium), accompanied by a +written offer, valid for at least three years and valid for as +long as you offer spare parts or customer support for that product +model, to give anyone who possesses the object code either (1) a +copy of the Corresponding Source for all the software in the +product that is covered by this License, on a durable physical +medium customarily used for software interchange, for a price no +more than your reasonable cost of physically performing this +conveying of source, or (2) access to copy the +Corresponding Source from a network server at no charge.
    • +
    • c) Convey individual copies of the object code with a copy of the +written offer to provide the Corresponding Source. This +alternative is allowed only occasionally and noncommercially, and +only if you received the object code with such an offer, in accord +with subsection 6b.
    • +
    • d) Convey the object code by offering access from a designated +place (gratis or for a charge), and offer equivalent access to the +Corresponding Source in the same way through the same place at no +further charge. You need not require recipients to copy the +Corresponding Source along with the object code. If the place to +copy the object code is a network server, the Corresponding Source +may be on a different server (operated by you or a third party) +that supports equivalent copying facilities, provided you maintain +clear directions next to the object code saying where to find the +Corresponding Source. Regardless of what server hosts the +Corresponding Source, you remain obligated to ensure that it is +available for as long as needed to satisfy these requirements.
    • +
    • e) Convey the object code using peer-to-peer transmission, provided +you inform other peers where the object code and Corresponding +Source of the work are being offered to the general public at no +charge under subsection 6d.
    • +
    +

    A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work.

    +

    A “User Product” is either (1) a “consumer product”, which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, “normally used” refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product.

    +

    “Installation Information” for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made.

    +

    If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM).

    +

    The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network.

    +

    Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying.

    +

    7. Additional Terms

    +

    “Additional permissions” are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions.

    +

    When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission.

    +

    Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms:

    +
      +
    • a) Disclaiming warranty or limiting liability differently from the +terms of sections 15 and 16 of this License; or
    • +
    • b) Requiring preservation of specified reasonable legal notices or +author attributions in that material or in the Appropriate Legal +Notices displayed by works containing it; or
    • +
    • c) Prohibiting misrepresentation of the origin of that material, or +requiring that modified versions of such material be marked in +reasonable ways as different from the original version; or
    • +
    • d) Limiting the use for publicity purposes of names of licensors or +authors of the material; or
    • +
    • e) Declining to grant rights under trademark law for use of some +trade names, trademarks, or service marks; or
    • +
    • f) Requiring indemnification of licensors and authors of that +material by anyone who conveys the material (or modified versions of +it) with contractual assumptions of liability to the recipient, for +any liability that these contractual assumptions directly impose on +those licensors and authors.
    • +
    +

    All other non-permissive additional terms are considered “further +restrictions” within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying.

    +

    If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms.

    +

    Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way.

    +

    8. Termination

    +

    You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11).

    +

    However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation.

    +

    Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice.

    +

    Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10.

    +

    9. Acceptance Not Required for Having Copies

    +

    You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so.

    +

    10. Automatic Licensing of Downstream Recipients

    +

    Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License.

    +

    An “entity transaction” is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts.

    +

    You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it.

    +

    11. Patents

    +

    A “contributor” is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's “contributor version”.

    +

    A contributor's “essential patent claims” are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, “control” includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License.

    +

    Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version.

    +

    In the following three paragraphs, a “patent license” is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To “grant” such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party.

    +

    If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. “Knowingly relying” means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid.

    +

    If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it.

    +

    A patent license is “discriminatory” if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007.

    +

    Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law.

    +

    12. No Surrender of Others' Freedom

    +

    If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program.

    +

    13. Remote Network Interaction; Use with the GNU General Public License

    +

    Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph.

    +

    Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License.

    +

    14. Revised Versions of this License

    +

    The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns.

    +

    Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License “or any later version” applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation.

    +

    If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program.

    +

    Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version.

    +

    15. Disclaimer of Warranty

    +

    THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

    +

    16. Limitation of Liability

    +

    IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES.

    +

    17. Interpretation of Sections 15 and 16

    +

    If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee.

    +

    ADDITIONAL TERMS

    +

    18. PhotoPrism® Trademark and Brand Assets

    +

    (a) PhotoPrism’s Brand Assets — including trademarks, logos, icons, fonts, +corporate design, product and service names, and any other brand features and +elements, whether registered or unregistered („Brand Assets“) — are +proprietary assets owned exclusively by PhotoPrism UG („PhotoPrism“). We +reserve the right to object to any use or misuse in any jurisdiction worldwide. +Visit photoprism.app/trademark to learn more.

    +

    (b) Contributors, licensees, business partners, and other third parties +may never claim ownership of PhotoPrism's Brand Assets or brands confusingly +similar to PhotoPrism's Brand Assets in any way, including, without +limitation, as a trademark, service mark, company name or designation, +domain name, social media profile/handle, or in any other manner.

    +

    (c) You may not include the PhotoPrism trademark in the name of your app, +product, or service, whether commercial or non-commercial in nature. This +includes online services such as e-commerce, community, blog, information, +advertising, and personal home pages, as well as apps, app stores, client +apps, or third-party apps that interact with PhotoPrism.

    +

    (d) In the event that any provision is found to be unenforceable by a court +or other competent jurisdiction, the remaining portions hereof shall remain +in full force and effect.

    +

    END OF TERMS AND CONDITIONS

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/license/apache/index.html b/license/apache/index.html new file mode 100644 index 0000000000..39e84e9905 --- /dev/null +++ b/license/apache/index.html @@ -0,0 +1,7966 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Apache License - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Apache License

    +

    Version 2.0, January 2004
    www.apache.org/licenses

    +

    Terms and Conditions for use, reproduction, and distribution

    +

    1. Definitions

    +

    “License” shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document.

    +

    “Licensor” shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License.

    +

    “Legal Entity” shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, “control” means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity.

    +

    “You” (or “Your”) shall mean an individual or Legal Entity exercising +permissions granted by this License.

    +

    “Source” form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files.

    +

    “Object” form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types.

    +

    “Work” shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below).

    +

    “Derivative Works” shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof.

    +

    “Contribution” shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +“submitted” means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as “Not a Contribution.”

    +

    “Contributor” shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work.

    + +

    Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form.

    +

    3. Grant of Patent License

    +

    Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed.

    +

    4. Redistribution

    +

    You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions:

    +
      +
    • (a) You must give any other recipients of the Work or Derivative Works a copy of +this License; and
    • +
    • (b) You must cause any modified files to carry prominent notices stating that You +changed the files; and
    • +
    • (c) You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and
    • +
    • (d) If the Work includes a “NOTICE” text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License.
    • +
    +

    You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License.

    +

    5. Submission of Contributions

    +

    Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions.

    +

    6. Trademarks

    +

    This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file.

    +

    7. Disclaimer of Warranty

    +

    Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License.

    +

    8. Limitation of Liability

    +

    In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages.

    +

    9. Accepting Warranty or Additional Liability

    +

    While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability.

    +

    END OF TERMS AND CONDITIONS

    +

    APPENDIX: How to apply the Apache License to your work

    +

    To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets [] replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same “printed page” as the copyright notice for easier identification within +third-party archives.

    +
    Copyright [yyyy] [name of copyright owner]
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +  https://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    +
    + +

    Learn more ›

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/license/docs/index.html b/license/docs/index.html new file mode 100644 index 0000000000..f57c1034e7 --- /dev/null +++ b/license/docs/index.html @@ -0,0 +1,7866 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Documentation - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    PhotoPrism® Documentation License

    +

    Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License

    +

    By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.

    +

    Section 1 – Definitions.

    +

    a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.

    +

    b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.

    +

    c. BY-NC-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License.

    +

    d. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.

    +

    e. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.

    +

    f. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.

    +

    g. License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution, NonCommercial, and ShareAlike.

    +

    h. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.

    +

    i. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.

    +

    h. Licensor means the individual(s) or entity(ies) granting rights under this Public License.

    +

    i. NonCommercial means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange.

    +

    j. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.

    +

    k. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.

    +

    l. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.

    +

    Section 2 – Scope.

    +

    a. License grant.

    +
      +
    1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
    2. +
    +

    A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and

    +

    B. produce, reproduce, and Share Adapted Material for NonCommercial purposes only.

    +
      +
    1. +

      Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.

      +
    2. +
    3. +

      Term. The term of this Public License is specified in Section 6(a).

      +
    4. +
    5. +

      Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.

      +
    6. +
    7. +

      Downstream recipients.

      +
    8. +
    +

    A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.

    +

    B. Additional offer from the Licensor – Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter’s License You apply.

    +

    C. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.

    +
      +
    1. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
    2. +
    +

    b. Other rights.

    +
      +
    1. +

      Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.

      +
    2. +
    3. +

      Patent and trademark rights are not licensed under this Public License.

      +
    4. +
    5. +

      To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes.

      +
    6. +
    +

    Section 3 – License Conditions.

    +

    Your exercise of the Licensed Rights is expressly made subject to the following conditions.

    +

    a. Attribution.

    +
      +
    1. If You Share the Licensed Material (including in modified form), You must:
    2. +
    +

    A. retain the following if it is supplied by the Licensor with the Licensed Material:

    +

    i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);

    +

    ii. a copyright notice;

    +

    iii. a notice that refers to this Public License;

    +

    iv. a notice that refers to the disclaimer of warranties;

    +

    v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;

    +

    B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and

    +

    C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.

    +
      +
    1. +

      You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.

      +
    2. +
    3. +

      If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.

      +
    4. +
    +

    b. ShareAlike.

    +

    In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply.

    +
      +
    1. +

      The Adapter’s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-NC-SA Compatible License.

      +
    2. +
    3. +

      You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material.

      +
    4. +
    5. +

      You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply.

      +
    6. +
    +

    Section 4 – Sui Generis Database Rights.

    +

    Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:

    +

    a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only;

    +

    b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and

    +

    c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.

    +

    For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.

    +

    Section 5 – Disclaimer of Warranties and Limitation of Liability.

    +

    a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.

    +

    b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.

    +

    c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.

    +

    Section 6 – Term and Termination.

    +

    a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.

    +

    b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:

    +
      +
    1. +

      automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or

      +
    2. +
    3. +

      automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or

      +
    4. +
    +

    For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.

    +

    c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.

    +

    d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.

    +

    Section 7 – Other Terms and Conditions.

    +

    a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.

    +

    b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.

    +

    Section 8 – Interpretation.

    +

    a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.

    +

    b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.

    +

    c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.

    +

    d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/privacy/index.html b/privacy/index.html new file mode 100644 index 0000000000..1683cc0a81 --- /dev/null +++ b/privacy/index.html @@ -0,0 +1,15 @@ + + + + + + Redirecting... + + + + + + +Redirecting... + + diff --git a/release-notes/index.html b/release-notes/index.html new file mode 100644 index 0000000000..838b02aff4 --- /dev/null +++ b/release-notes/index.html @@ -0,0 +1,10264 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Release Notes - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Release Notes

    +
    +

    You can test upcoming features and enhancements by changing the image tag from :latest to :preview and then following our update guide to download the newest image from Docker Hub and restart your instance.

    +
    +

    September 15, 2024

    +

    Build 240915-e1280b2fb

    +

    This update includes improved HEIC file support for iOS 18 compatibility, updated dependencies and translations, UX enhancements, and fixes for recently discovered issues. Please note that a complete re-scan of your library is required to increase the GPS location accuracy of pictures, e.g. under Places. A big thank you to everyone who contributed and helped with testing! 🛰🌎

    +

    What's new?

    + +

    July 11, 2024

    +

    Build 240711-2197af848

    +

    Our latest update adds support for single sign-on via OpenID Connect (OIDC). We would like to thank everyone who contributed to this, especially Timo Volkmann, who got things rolling and did much of the necessary work! 🌈

    +

    What's new?

    + +

    May 31, 2024

    +

    Build 240531-60b3a4628

    +

    With this update, you can choose to install FFmpeg 7 for faster software video transcoding. You also get the latest translations contributed by our community as well as improved backup commands and configuration defaults.

    +

    What's new?

    + +

    May 28, 2024

    +

    Build 240528-977d6c0de

    +

    This service release reduces the server load when downloading many files, expands the list of available config options, and gets you the latest translations contributed by our community.

    +

    What's new?

    + +

    May 23, 2024

    +

    Build 240523-923ee0cf7

    +

    This update adds a scheduler so you can easily create database backups and re-index your library at regular intervals. It also includes many updated dependencies and support for ICC color profiles, which especially benefits Apple iPhone and professional users working with color spaces other than sRGB. 🎨

    +

    Important Changes

    +
      +
    • If you keep the default settings, daily database backups will be automatically created, with up to 3 backup files being retained. This is to prevent the available storage space from filling up. We recommend setting the corresponding config options before installing the update if you want to disable scheduled backups, keep more backup files, or prefer a specific time for creating backups. The previously available --disable-backups flag has been deprecated in favor of these finer-grained options.
    • +
    • In order to preserve ICC color profiles and reduce memory usage, new thumbnails will be generated with the libvips image processing library. You can run the photoprism thumbs -f command in a terminal to regenerate your existing thumbs as needed, or delete the storage/cache/thumbnails folder and then re-index your library. To continue using the native image processing library, set PHOTOPRISM_THUMB_LIBRARY to "imaging" in your compose.yaml or docker-compose.yml configuration file. If you build from source or use one of our binary installation packages, the system on which you build and/or run PhotoPrism must have libvips >= 8.10 installed.
    • +
    +

    What's new?

    + +
    +

    Missing user interface translations have been generated with the help of DeepL and Google Translate. Native speakers are welcome to help us improve them, if necessary.

    +
    +

    April 20, 2024

    +

    Build 240420-ef5f14bc4

    +

    Our new stable release comes with a long list of indexing and security-related improvements. Most notably, we've added support for 2-Factor Authentication (2FA) to protect your account in case someone gains access to your password. As all security-related changes had to be thoroughly tested, this is one of the updates that were longer in the making. We appreciate your patience while we've been working on this and would like to thank everyone involved! 🔐

    +

    What's new?

    + +
    +

    Missing user interface translations have been generated with the help of DeepL and Google Translate. Native speakers are welcome to help us improve them, if necessary.

    +
    +

    November 28, 2023

    +

    Build 231128-f48ff16ef

    +

    Our latest service release provides updated dependencies and fixes for recently discovered issues. In addition, official installation packages with binaries for Linux are now available as an alternative to our Docker images. Please note that only experienced users should choose this installation method, since these do not include all dependencies and need to be set up manually.

    +

    What's new?

    + +

    October 21, 2023

    +

    Build 231021-9abea5b55

    +

    This update adds search filters for finding pictures by ISO number, focal length, aperture, and altitude. It also includes a number of user interface improvements, updated translations, as well as fixes for recently discovered issues. We would like to thank everyone who submitted pull requests, helped with testing, or contributed in other ways! ✨

    +

    What's new?

    + +

    October 11, 2023

    +

    Build 231011-63f708417

    +

    This service release includes an updated ARMv7 build, a number of usability improvements requested by our community, and fixes for recently discovered issues. We would like to thank everyone involved!

    +

    What's new?

    + +

    September 23, 2023

    +

    Build 230923-e59851350

    +

    Our latest release includes a redesigned Places view, with the search box moved to the top and a preview for selected clusters at the bottom. We've also added support for Samsung/Google Motion Photos, so you can view them like Apple Live Photos after re-indexing your library. Beyond those highlights, you'll get many usability improvements, new search filters, and fixes for recently discovered issues. A big thank you to everyone who contributed!

    +

    What's new?

    + +

    July 19, 2023

    +

    Build 230719-73fa7bbe8

    +

    Our latest release includes new features and enhancements contributed by our community, a number of security improvements, as well as fixes for recently discovered issues. Thank you to everyone who submitted pull requests, helped with testing, signed up as a member, or contributed in other ways! We appreciate it very much.

    +

    What's new?

    + +
    +

    We recommend that you explicitly disable TLS by adding PHOTOPRISM_DISABLE_TLS: "true" to your compose.yaml or docker-compose.yml file when running PhotoPrism behind a reverse proxy. HTTPS could otherwise be accidentally enabled if a certificate matching the site URL is found or PHOTOPRISM_DEFAULT_TLS is set to "true".

    +
    +

    June 25, 2023

    +

    Build 230625-17242fb07

    +

    This service release includes the latest translations contributed by our community, as well as fixes for recently discovered issues.

    +

    What's new?

    + +

    June 15, 2023

    +

    Build 230615-90a18f6e7

    +

    This update includes new features and enhancements contributed by our community, +as well as fixes for recently discovered issues. We would like to thank everyone involved!

    +

    What's new?

    + +

    June 7, 2023

    +

    Build 230607-9e086c7eb

    +

    With this much anticipated update, our new high-resolution vector world map becomes available to all users. It also features a special terrain mode for mountain lovers, so you can view the "Satellite", "Outdoor" and "Topography" maps in 3D!

    +

    What's new?

    + +

    PhotoPrism® Plus

    + +

    June 3, 2023

    +

    Build 230603-378d4746a

    +

    This service release fixes recently discovered issues and improves compatibility with the upcoming MariaDB v11.0. If you are upgrading from MariaDB 10.x to 11.0, please make sure that you replace command: mysqld with command: (followed by the command flags) in your compose.yaml or docker-compose.yml file, otherwise the database server might fail to start. Thank you to everyone who contributed with pull requests, reported bugs, and helped us test the changes!

    +

    What's new?

    + +

    PhotoPrism® Plus

    + +

    May 13, 2023

    +

    Build 230513-0b780defb

    +

    As promised, this update makes hardware transcoding and many other config options available to all users. A big thank you to all of our contributors, members, and sponsors, whose generous support has been and continues to be essential to the success of the project!

    +

    What's new?

    + +

    May 6, 2023

    +

    Build 230506-9de9a3540

    +

    This update resolves two recently reported issues and includes updated translations.

    +

    What's new?

    + +

    May 4, 2023

    +

    Build 230504-cbf48798c

    +

    This service release makes the Nordic theme and the Hide People feature available to all users. It also changes the theme order in Settings so that the freely available themes come first.

    +

    What's new?

    + +

    May 2, 2023

    +

    Build 230502-c405f6eff

    +

    With this major new release, you'll get a long list of new features and enhancements with a focus on performance, security, and file type support. In addition, our Plus Members can now register directly in the app to unlock additional features like vector graphics support and a new admin web UI for user and session management. Thank you to all contributors, members, and sponsors who made this possible!

    +

    What's new?

    + +

    PhotoPrism® Plus

    + +
    +

    Our new Plus License is used for both the extensions we provide to our members and the standard Docker images available on Docker Hub. This allows us to bundle the extensions with the compiled application, while the Community Edition remains freely available under the terms of the GNU Affero General Public License (AGPL).

    +

    If you don't plan to use any additional features, you can alternatively use the "ce" tag instead of "latest" to get a slightly smaller Docker image distributed under the AGPL. Note that system dependencies and other third-party components included in this image are still subject to additional terms and conditions.

    +

    View Membership FAQ › View Plus License ›

    +
    +

    November 18, 2022

    +

    Build 221118-e58fee0fb

    +

    This service release includes compatibility fixes for MariaDB 10.10, the latest translations, a new theme, and updated dependencies. +We recommend not using the :latest tag for the MariaDB Docker image and to upgrade manually by changing the tag once we had a chance to test a new major version.

    +

    What's new?

    + +

    November 17, 2022

    +

    Build 221117-3268c4de8

    +

    This update includes video transcoding improvements and the latest translations contributed by our community.

    +

    What's new?

    + +

    November 16, 2022

    +

    Build 221116-122ebfb70

    +

    With this update you get the latest translations, updated dependencies, and two metadata bug fixes. Thanks to all who contributed!

    +

    What's new?

    + +

    November 5, 2022

    +

    Build 221105-7a295cab4

    +

    This service release provides UX improvements for the photo editing dialog and includes the latest translations contributed by our community. Note that our guides now use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose to start and stop your instance.

    +

    What's new?

    + +

    November 4, 2022

    +

    Build 221104-20d180b21

    +

    A small update featuring improved NVIDIA GPU support, the latest translations contributed by our community, and updated dependencies.

    +

    What's new?

    + +

    November 3, 2022

    +

    Build 221103-211eb36ea

    +

    With this update you'll get the latest translations contributed by our community, updated dependencies as well as a few minor bug fixes and improvements.

    +

    What's new?

    + +

    November 2, 2022

    +

    Build 221102-905925b4d

    +

    Due to the many new features, enhancements and bug fixes, this is one of those updates that took longer to release. +Before upgrading, please read the full release notes and note that this release does not yet include support for user roles other than Admin, as we need to specify, create and test each new role before we can release it. Once this is done, we will also provide additional user management documentation.

    +
    +

    We've generated missing translations with the help of DeepL and Google Translate. Native speakers are +invited to help us improve those if needed. +A special thank you to everyone who contributed!

    +
    +

    Breaking Changes

    +
      +
    • In order to improve security and compatibility, the default Docker image is now based on Ubuntu 22.04 LTS (Jammy Jellyfish) instead of Debian 12 (Bookworm). The entrypoint script has been updated to preserve group permissions required for hardware transcoding.
    • +
    • Session and user management have been re-implemented. If you are upgrading from a preview build, you will need to run the photoprism users reset --yes command in a terminal after the upgrade to recreate the new database tables so that they are compatible with the stable version. This will not affect your pictures or albums.
    • +
    • Upgrading from the last stable version should work without any problems. However, if you have already created additional accounts with the previously offered unofficial multi-user support, you will notice that only the main admin account is migrated automatically. Run photoprism users legacy in a terminal to display the legacy accounts so you can migrate them manually if needed.
    • +
    • Sharing link visitors can now see the picture locations in the regular album view and optionally on a map after clicking the link. Based on user feedback, we may add settings to hide the locations for enhanced privacy.
    • +
    • We recommend performing a full rescan after the upgrade to take advantage of new search filters and sort options.
    • +
    • Indexing is also necessary to find and view HEIC, DNG, and AVIF images that were previously unsupported or had errors. In some cases with incorrectly converted images, it may be necessary to recreate the JPEG sidecar files by running the photoprism convert -f command in a terminal before starting the rescan. To regenerate your thumbnails, run photoprism thumbs -f.
    • +
    +

    What's new?

    + +

    September 1, 2022

    +

    Build 220901-f493607b0

    +

    With this update you get all the latest translations contributed by our community, mobile navigation enhancements, updated dependencies and, as usual, fixes for recently discovered issues. Thanks to everyone involved!

    +

    What's new?

    + +

    July 30, 2022

    +

    Build 220730-0e1222c83

    +

    Fixes the activation of public mode with PHOTOPRISM_AUTH_MODE instead of PHOTOPRISM_PUBLIC.

    +

    What's new?

    + +

    July 28, 2022

    +

    Build 220728-729ddd920

    +

    Includes indexing, metadata, and authentication enhancements, as well as updated translations.

    +

    What's new?

    + +

    Breaking Changes

    + +

    June 29, 2022

    +

    Build 220629-5d7448d2

    +

    With this update, you'll enjoy a much faster and smoother scrolling experience as well as direct streaming of OGV, VP8, VP9, AV1, WebM and HEVC videos if they do not exceed the configured bitrate limit. Special thanks to Heiko Mathes and Andre Carrera for their contributions!

    + +

    June 17, 2022

    +

    Build 220617-0402b8d3

    +

    This update features updated translations as well as fixes for recently discovered issues.

    + +

    June 14, 2022

    +

    Build 220614-dea9ff68

    +

    A small but important update that includes translations to Arabic, a migration fix for MariaDB, and many updated dependencies.

    + +

    May 28, 2022

    +

    Build 220528-efb5d710

    +

    This update includes translations that were recently contributed via translate.photoprism.app. Missing translations were added by us using DeepL and Google Translate. Native speakers are invited to help improve those if needed. Thank you very much!

    + +

    May 27, 2022

    +

    Build 220527-005770ca

    +

    This update improves navigation fonts and mobile submenu colors for light themes. We are also working to establish PhotoPrism+ as the name for our community membership and associated benefits. For this, membership information in the app, on our website, on GitHub Sponsors and Patreon is gradually being updated.

    + +

    May 24, 2022

    +

    Build 220524-c76de0df

    +

    This service release fixes potential issues with our new Debian 12-based Docker image that shipped with the last update. These may have prevented users from deploying it without making changes to their environment. In our ongoing effort to improve usability and performance, we have also implemented a number of UX/UI optimizations, such as using the default operating system font instead of Google's Roboto.

    + +

    Thank you to everyone who helped with testing, signed up as a member, or contributed in other ways! We appreciate it very much.

    +

    May 17, 2022

    +

    Build 220517-b9c68f8f

    +

    This update features search UX enhancements, a new Docker base image based on Debian 12 "Bookworm", +as well as fixes for recently discovered issues. Front- and backend translations in numerous +languages have been added and updated. Thanks to all involved!

    + +

    March 2, 2022

    +

    Build 220302-0059f429

    +

    The Docker images for this release are based on Debian 11 +"Bullseye" and include many updated dependencies such as Darktable 3.8. +Behind the scenes, the build process has also been improved so that it will be easier to provide standalone packages in the future.

    + +

    You can now join us on translate.photoprism.app to help translate the UI!

    +

    January 21, 2022

    +

    Build 220121-2b4c8e1f

    +

    We've generated missing translations with the help of DeepL and Google Translate. Native speakers are +invited to help us improve those if needed. Learn how to contribute.

    + +

    January 18, 2022

    +

    Build 220118-76c94a1f

    + +

    January 7, 2022

    +

    Build 220107-f5b7ef83

    +

    Based on our zero bug policy, this update focuses on bug fixes, security, and UX enhancements for search +filters, metadata, and the indexer. In addition, one of the merged pull requests may improve face recognition +performance on smaller devices and with large libraries.

    + +

    December 15, 2021

    +

    Build 211215-93b26f19

    +

    PhotoPrism is not directly affected by the Apache Log4j vulnerability. +Logs may still contain messages that can cause harm if consumed by an unpatched Java application. +As a precaution, this release includes additional rules and filters to validate user input.

    + +

    December 10, 2021

    +

    Build 211210-2cb90e7e

    +

    Starting with this release, the regular multi-arch Docker image is 64-bit only. +A 32-bit version of our stable release for older devices +is offered separately. This frees up development and infrastructure resources with minimal impact.

    + +

    December 3, 2021

    +

    Build 211203-fdb6b5e1

    +

    Since the funding goal required to make all features and maps generally +available has not been reached, early-access features have been renamed to sponsor features in this update. +Offline and high-resolution street maps remain free for everyone, while hybrid, topographic, and outdoor maps are +now a member feature. We believe this is fair. A big thank you to all our sponsors +and contributors!

    + +

    November 30, 2021

    +

    Build 211130-13cfcf6d

    + +

    For our sponsors and contributors:

    +
      +
    • UI: New Abyss and Gemstone dark themes 💎
    • +
    +

    November 28, 2021

    +

    Build 211128-7e8974fd

    +

    Official support for MySQL 8 is discontinued with this update as Oracle seems to have stopped shipping new features and enhancements. +As a result, the testing effort required before each release is no longer feasible. We recommend upgrading +to MariaDB 10.6 or later. +PostgreSQL support is planned for 2022 without a specific release date yet.

    + +

    November 27, 2021

    +

    Build 211127-86c43159

    +

    When possible, location estimates now include a latitude and longitude. Photos load faster when you open +them in Places, and the viewer sorts them by distance. Time zone handling has been completely reworked, +in particular for UTC. The Docker base image has been updated to Ubuntu 21.10, which ships with +Darktable 3.6 among other updated dependencies.

    + +

    For our sponsors and contributors:

    + +

    October 18, 2021

    +

    Build 211018-e200f322

    + +

    For our sponsors and contributors:

    +
      +
    • UI: New Vanta dark theme ✨
    • +
    +

    October 10, 2021

    +

    Build 211010-83b4f783

    + +

    We've generated missing translations with the help of DeepL and Google Translate. Native speakers are +invited to help us improve those if needed. Learn how to contribute.

    +

    October 9, 2021

    +

    Build 211009-d6cc8df5`

    + +

    October 7, 2021

    +

    Build 211007-8f55d6f8

    + +

    For our sponsors and contributors:

    + +

    October 2, 2021

    +

    Build 211002-bf015326

    + +

    September 25, 2021

    +

    Build 210925-96168e4b

    + +

    May 23, 2021

    +

    Build 210523-b1856b9d

    + +

    May 20, 2021

    +

    Build 210520-4b32bac7

    + +

    May 19, 2021

    +

    Build 210519-24b5c7e6

    + +

    May 18, 2021

    +

    Build 210518-80981c25

    + +

    May 5, 2021

    +

    Build 210505-d3e53a89

    + +

    April 26, 2021

    +

    Build 210426-da6e948f

    + +

    April 22, 2021

    +

    Build 210422-97e75b04

    + +

    February 22, 2021

    +

    Build 210222-ac5a9d5e

    + +

    February 17, 2021

    +

    Build 210217-49039368

    + +

    February 16, 2021

    +

    Build 210216-4939e36a

    + +

    February 11, 2021

    +

    Build 210211-b9595dd4

    + +

    February 8, 2021

    +

    Build 210208-9e10ba69

    + +

    January 28, 2021

    +

    Build 210128-a82061e0

    + +

    January 21, 2021

    +

    Build 210121-07e559df

    + +

    January 20, 2021

    +

    Build 210120-e7cd5e9a

    + +

    January 19, 2021

    +

    Build 210119-a5399f06

    + +

    For our sponsors and contributors:

    + +

    January 11, 2021

    +

    Build 210111-cc05c430

    + +

    January 4, 2021

    +

    Build 210104-7f9e806a

    + +

    January 2, 2021

    +

    Build 210102-af71e5f7

    +
      +
    • WebDAV: Uploads and other changes trigger auto indexing / importing
    • +
    • Config: Use random hash for improved preview token security
    • +
    • UX: Disabled page zoom so that app feels more native on mobile devices
    • +
    • UX: Reduced min password length to 4 characters
    • +
    • UX: Improved docker-compose.yml examples
    • +
    • UX: Reduced icon size in "add to album" dialog
    • +
    +

    December 31, 2020

    +

    Build 201231-8e22fbf8

    +
      +
    • Initial Stable Release
    • +
    +

    Getting Updates

    +

    Even when you use an image with the :latest tag, Docker does not automatically download new images for you. To update, you can either manually pull the newest image and restart, or set up a service like Watchtower to get automatic updates.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 0000000000..595ac6ab2b --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"PhotoPrism: Browse Your Life in Pictures","text":"

    PhotoPrism\u00ae is an AI-Powered Photos App for the Decentralized Web. It makes use of the latest technologies to tag and find pictures automatically without getting in your way. You can run it at home, on a private server, or in the cloud.

    "},{"location":"#feature-overview","title":"Feature Overview","text":"

    Our mission is to provide the most user- and privacy-friendly solution to keep your pictures organized and accessible. That's why PhotoPrism was built from the ground up to run wherever you need it, without compromising freedom, privacy, or functionality:

    • Browse all your photos and videos without worrying about RAW conversion, duplicates or video formats
    • Easily find specific pictures using powerful search filters
    • Recognizes the faces of your family and friends
    • Automatic classification of pictures based on their content and location
    • Play Live Photos by hovering over them in albums and search results
    • Since the User Interface is a Progressive Web App, it provides a native app-like experience, and you can conveniently install it on the home screen of all major operating systems and mobile devices
    • Includes 4 high-resolution World Maps to bring back the memories of your favorite trips
    • Metadata is extracted and merged from Exif, XMP, and other sources such as Google Photos
    • Many more image properties like Colors, Chroma, and Quality can be searched as well
    • Use PhotoSync to securely backup iOS and Android phones in the background
    • WebDAV clients such as Microsoft's Windows Explorer and Apple's Finder can connect directly to PhotoPrism, allowing you to open, edit, and delete files from your computer as if they were local

    Compare Features \u203a

    "},{"location":"#100-privacy","title":"100% Privacy","text":"

    Because PhotoPrism is 100% self-funded and independent, we can promise you that we will never sell your data and that we will always be transparent about our software and services. Your data will never be shared with Google, Amazon, Microsoft or Apple unless you intentionally upload files to one of their services.

    TRY OUR DEMO GET STARTED

    "},{"location":"credits/","title":"Credits","text":"

    A big thank you to all current and past sponsors, whose generous support has been and continues to be essential to the success of the project!

    "},{"location":"credits/#infrastructure","title":"Infrastructure","text":"

    Our project infrastructure is provided by the following companies:

    • GitHub hosts our code repositories and also provides many other important services
    • Docker approved us for their Open Source Program and hosts all of our app images
    • Element develops and operates the infrastructure that our community chat is based on
    • BrowserStack provides free access to their device and browser testing infrastructure
    "},{"location":"credits/#technologies","title":"Technologies","text":"
    • Go, Gin and Vuetify
    • Google TensorFlow and Material Design
    • MkDocs and Material for MkDocs
    • Complete list of 3rd-party software packages
    "},{"location":"credits/#artwork","title":"Artwork","text":"
    • Font Awesome Free by Fonticons, Inc.
    • AI-Generated Faces by generated.photos
    • NGC 1499 California Nebula by Remidone
    • Prism Light Separation by digitalvosem
    • Follow that light by Margeois
    • Go Gopher designed by Renee French
    • Gopher Stickers created by Takuya Ueda
    "},{"location":"known-issues/","title":"Known Issues","text":"

    PhotoPrism generally follows a zero-bug policy, which means that we do our best to provide a fix for every technical problem we learn about. However, sometimes this is not possible right away, for example because it needs to be fixed in a third-party library that our software depends on, or because it is a specific use case that we do not support at this time.

    GitHub Issues

    In order to improve readability and reduce maintenance effort, minor issues and recently reported bugs that we plan to fix in the short term are not listed here, but only in GitHub Issues. When browsing issues, please note that our team and all issue subscribers receive an email notification whenever a new comment is added, so these should only be used for sharing important information and not for discussions, questions, or expressing personal opinions. Thank you very much!

    "},{"location":"known-issues/#self-hosted-setup","title":"Self-Hosted Setup","text":""},{"location":"known-issues/#shared-domain","title":"Shared Domain","text":"

    Installation in a sub-directory on a shared domain is generally possible if you run your instance behind a reverse proxy. At the moment, this method is experimental. We do not recommend it because technical expertise is required and a number of specific issues still need to be addressed:

    • Hosting: Resolve known issues when installing in a sub-directory on a shared domain #2391
    "},{"location":"known-issues/#nested-import-folder","title":"Nested Import Folder","text":"

    You must not configure the import folder to be inside the originals folder, as this will cause a loop by importing already indexed files.

    Inexperienced users are advised to closely follow our documentation and to use the config examples we provide, as this issue can only occur with a custom setup.

    "},{"location":"known-issues/#nested-storage-folder","title":"Nested Storage Folder","text":"

    We recommend not to configure the storage folder to be inside the originals folder unless the name starts with a . to indicate that it is hidden.

    In older releases prior to 240420-ef5f14bc4, this could lead to an indexing loop by indexing thumbnails of already indexed files.

    "},{"location":"known-issues/#symbolic-links","title":"Symbolic Links","text":"

    Symbolic links to files and directories within the originals folder are supported if they are accessible from the environment in which your instance is running. However, you cannot mount a symbolic link as a storage folder or use links within the storage folder.

    "},{"location":"known-issues/#authentication","title":"Authentication","text":""},{"location":"known-issues/#upgrading-from-previous-releases","title":"Upgrading From Previous Releases","text":"

    Should you experience problems after upgrading from a previous release or development preview, we recommend running the photoprism auth reset --yes command in a terminal to reset the auth_sessions table to a clean state and force a re-login of all users. Note that this will also delete all client access tokens and any app passwords that users may have created.

    "},{"location":"known-issues/#new-user-management","title":"New User Management","text":"

    The session and user management was reimplemented in November 2022. If you are upgrading from a Development Preview with a build number between 221102-905925b4d and 220901-f493607b0, you will need to run the photoprism users reset --yes command in a terminal after the upgrade to recreate the new database tables so that they are compatible with the stable version. This will not affect your pictures or albums.

    Upgrading from the last stable version should work without any problems. However, if you have already created additional accounts with the previously offered unofficial multi-user support, you will notice that only the main admin account is migrated automatically. Run photoprism users legacy in a terminal to display the legacy accounts so you can migrate them manually if needed.

    "},{"location":"known-issues/#openid-connect-oidc","title":"OpenID Connect (OIDC)","text":"

    Changing the authentication of an existing user account to OIDC does not remove a previously set password, so that it can still be used to log in (optionally also in combination with 2FA).

    If a local password has been set for an account, you can remove it by running the photoprism passwd --rm [username] command in a terminal. Alternatively, super admins can set the account password to a long random value through the UI or CLI to effectively prevent local authentication.

    Please also note that you cannot change the authentication provider of your own account through the Admin UI, so you don't accidentally lock yourself out e.g. by setting it to \"none\".

    "},{"location":"known-issues/#face-recognition","title":"Face Recognition","text":""},{"location":"known-issues/#legacy-hardware","title":"Legacy Hardware","text":"

    Face recognition can be slow (or even crash) on old devices due to insufficient resources.

    Like most applications, PhotoPrism has certain requirements and our development process does not include testing on unsupported or unusual hardware.

    "},{"location":"known-issues/#asian-faces-and-children","title":"Asian Faces and Children","text":"

    It is a known issue that children and Asian-looking faces cannot be recognized reliably. Detection without automatic recognition should not be affected by that.

    This is because the model we use was trained with North American images, which unfortunately do not include many Asians. The absence of children in the training data comes from the fact that parents do not usually share such images under a public license (and may not have the right to do so).

    We will continue to improve our models over time as our resources allow.

    "},{"location":"known-issues/#background-worker","title":"Background Worker","text":"

    Face recognition was developed and tested under the assumption that the background worker runs every 15 minutes, unless the backend is busy with other tasks like indexing. It has not been tested with much longer intervals and is not designed for that.

    PhotoPrism's background worker groups new faces by similarity, compares faces with clusters, and optimizes existing clusters as needed. Without these routine tasks, the number of faces to be processed becomes too large. The first and next time the worker runs, it can then cause a heavy server load until all the faces, face clusters, and related pictures have been updated. The longer you wait, the more CPU is required and the longer it takes.

    An important reason for the worker to run independently of actual changes in the main instance is that some users change the database content directly or run additional instances, for example for indexing. It is a problem that can be solved, but it takes time. If we were to ignore this and don't run the worker at all times, it could lead to many additional support requests, further reducing the amount of time we can spend on development.

    The handling of changes in multiple instances will be improved over time so that the worker can be run less frequently in future releases.

    "},{"location":"known-issues/#photos-with-all-faces-assigned-appear-under-new-faces","title":"Photos With All Faces Assigned Appear Under \u201cNew Faces\u201d","text":"

    This can happen when multiple image files are grouped into a stack, for example, because they were taken at the same place and time, and stacking is enabled.

    Secondary images are not searched for faces by default. So the problem is limited to specific cases where you have manually changed the primary image or the images are stacked after detection, which may indicate more fundamental problems, e.g. with the metadata, filenames, or settings.

    One possible solution is to change the primary image of a stack to assign faces to the other images in the stack. You can also manually unstack these files and disable stacking in Settings > Library. Note that files that are already stacked are not automatically unstacked when you change the stacking settings, and that Live Photos do not appear in Stacks because they are a special type of media that is always \"stacked\".

    "},{"location":"known-issues/#removing-merged-clusters-fails","title":"Removing Merged Clusters Fails","text":"

    Under certain conditions, inconsistent face assignments cannot be automatically resolved by the background worker, which can result in an unusually high CPU load when it is running:

    • if you use multiple browser tabs or windows for assigning faces and don't wait until saving the changes is complete, the likelihood of this problem increases, especially if you accidentally enter different names for the same face
    • another possible cause is running multiple instances (for example, parallel indexing workers started by a scheduler in the background) or modifying database content directly, as this may also lead to inconsistent faces, markers and subjects
    • see Faces: Error \"Failed removing merged clusters for subject\" seems to cause tagging of faces to become slow #2806

    Running the following command in a terminal can resolve problems with inconsistent data:

    docker compose exec photoprism photoprism faces audit --fix\n

    It can also be helpful to manually check for inconsistent assignments and fix them in the user interface. Alternatively, you can use the photoprism faces reset command for a clean start if you haven't invested much time in assigning faces yet.

    Advanced users affected by this are welcome to privately provide us with a SQL dump of their subjects, faces, and markers database tables for debugging. Thank you very much!

    "},{"location":"known-issues/#file-compatibility","title":"File Compatibility","text":""},{"location":"known-issues/#jpeg-bad-rst-marker","title":"JPEG: Bad RST Marker","text":"

    This error can occur when decoding JPEG images that contain consecutive 0xFF bytes, e.g. images that have a \"glich\" (like a few lines of missing image information at the end) or were created with software that inserts them for padding, although this is based on an edge case of the specification and rather uncommon:

    • Bug: (invalid JPEG format: bad RST marker) #1673
    • image/jpeg: \"bad RST marker\" error when decoding #40130
    • JPEG: Automatically check and repair broken/invalid images #2463
    "},{"location":"known-issues/#raw-converters","title":"RAW Converters","text":""},{"location":"known-issues/#jpeg-size-limit","title":"JPEG Size Limit","text":"

    RawTherapee and \"heif-convert\" cannot limit the resolution of JPEG files when converting files from other formats such as RAW, DNG, HEIC or AVIF. In general, when converting images, the resolution of the generated JPEG files can be limited with the environment variable PHOTOPRISM_JPEG_SIZE or the CLI parameter --jpeg-size.

    However, this does not work with certain converters because, unlike Darktable, they do not support CLI options to limit JPEG size:

    • RAW: PHOTOPRISM_JPEG_SIZE is ignored when converting RAW with RawTherapee #2446

    It would probably also hurt indexing performance and image quality if PhotoPrism reduced the size of the generated file after conversion, for example, by using a temporary file.

    As a result, this option is ignored when generating JPEG files with these converters. Whether RawTherapee or \"heif-convert\" are used depends on additional settings such as PHOTOPRISM_DARKTABLE_BLACKLIST, PHOTOPRISM_DISABLE_DARKTABLE, PHOTOPRISM_RAWTHERAPEE_BLACKLIST, and PHOTOPRISM_DISABLE_RAWTHERAPEE.

    "},{"location":"known-issues/#docker-compose","title":"Docker Compose","text":""},{"location":"known-issues/#dollar-signs","title":"Dollar Signs","text":"

    If a configuration value in a compose.yaml or docker-compose.yml file contains a literal $ character, for example in a password, you must use $$ (a double dollar sign) to escape it so that e.g. \"compo$e\" becomes \"compo$$e\":

    services:\n  mariadb:\n    environment:\n      # sets password to \"compo$e\"\n      MARIADB_PASSWORD: \"compo$$e\"\n

    Values that contain a $ are otherwise interpreted as a variable. In this case, both the $VARIABLE and the ${VARIABLE} syntax are supported. Further details on the use of variables can be found in the file format reference.

    "},{"location":"known-issues/#true-false","title":"True / False","text":"

    Boolean variable values like \"true\", \"false\", \"yes\", \"no\", \"on\", or \"off\" must be enclosed in double or single quotes so that they are passed as intended:

    services:\n  photoprism:\n    environment:\n      PHOTOPRISM_DEFAULT_TLS: \"true\"\n      PHOTOPRISM_READONLY: \"false\"\n

    If you otherwise specify true as a value without quotes, Docker Compose will pass the host variable of the same name to the container instead of setting the value to \"true\" (results in an empty string if no environment variable with the same name is set on the host):

    services:\n  photoprism:\n    environment:\n      # evaluated as \"\" (false)\n      PHOTOPRISM_READONLY: true\n
    "},{"location":"known-issues/#reporting-bugs","title":"Reporting Bugs","text":"

    Before reporting a bug, first use our Troubleshooting Checklists to determine the cause of your problem. If you have a general question, need help, it could be a configuration issue, or a misunderstanding in how the software works:

    • you are welcome to ask in our Community Chat
    • or post your question in GitHub Discussions

    In order for us to investigate new bug reports, they must include a complete list of steps to reproduce the problem, the software versions used and information about the environment in which the problem occurred, such as browser type, browser version, browser plug-ins, operating system, storage type, processor type, and memory size.

    A template for creating bug reports can be found at photoprism.app/kb/reporting-bugs. We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app.

    When browsing issues, please note that our team and all issue subscribers receive an email notification from GitHub whenever a new comment is added, so these should only be used for sharing important information and not for discussions, questions, or expressing personal opinions. Thank you very much!

    "},{"location":"release-notes/","title":"Release Notes","text":"

    You can test upcoming features and enhancements by changing the image tag from :latest to :preview and then following our update guide to download the newest image from Docker Hub and restart your instance.

    "},{"location":"release-notes/#september-15-2024","title":"September 15, 2024","text":"

    Build 240915-e1280b2fb

    This update includes improved HEIC file support for iOS 18 compatibility, updated dependencies and translations, UX enhancements, and fixes for recently discovered issues. Please note that a complete re-scan of your library is required to increase the GPS location accuracy of pictures, e.g. under Places. A big thank you to everyone who contributed and helped with testing! \ud83d\udef0\ud83c\udf0e

    What's new?

    • HEIC: Improved .heic image file support for compatibility with iOS 18
    • Search: Sidecar files are no longer shown in the results when sorting by file size
    • Archive: Recently archived pictures are displayed first by default
    • Places: Fixed an issue where no pictures were found when clicking on clusters
    • Library: Removed the archive button from the action menu under Library > Hidden
    • API: Fixed an issue where update requests could fail silently in case of database errors
    • API: Added interactive Swagger developer documentation with examples
    • Security: Go has been updated to v1.22.7, which includes security and bug fixes
    • Translations: Added Irish (Gaeilge) and updated Basque, French and German
    "},{"location":"release-notes/#july-11-2024","title":"July 11, 2024","text":"

    Build 240711-2197af848

    Our latest update adds support for single sign-on via OpenID Connect (OIDC). We would like to thank everyone who contributed to this, especially Timo Volkmann, who got things rolling and did much of the necessary work! \ud83c\udf08

    What's new?

    • Auth: Added support for single sign-on via OpenID Connect (OIDC)
    • Index: Slashes and null bytes are trimmed from .ppignore patterns
    • Videos: Added support for MPEG-5 Essential Video Coding (EVC)
    • Videos: Added filter to transcode 10bit videos with Intel QSV
    • CLI: Local passwords can be removed with photoprism passwd --rm
    • Security: Go has been updated to the latest stable release v1.22.5
    • Translations: Updated French and Japanese
    "},{"location":"release-notes/#may-31-2024","title":"May 31, 2024","text":"

    Build 240531-60b3a4628

    With this update, you can choose to install FFmpeg 7 for faster software video transcoding. You also get the latest translations contributed by our community as well as improved backup commands and configuration defaults.

    What's new?

    • Videos: You can choose to install FFmpeg 7.0 for faster transcoding
    • MariaDB: Backup and restore commands support socket connections
    • Config: Increased auto-index delay and disabled auto-import by default
    • Translations: Updated Japanese
    "},{"location":"release-notes/#may-28-2024","title":"May 28, 2024","text":"

    Build 240528-977d6c0de

    This service release reduces the server load when downloading many files, expands the list of available config options, and gets you the latest translations contributed by our community.

    What's new?

    • Download: Zip archives are not compressed to reduce server load
    • Search: Added added, updated and edited search filters for app developers
    • Config: Replaced the terms whitelist and blacklist with alternatives
    • Config: New feature flag PHOTOPRISM_DISABLE_BACKUPS disables all backups
    • Config: New feature flag PHOTOPRISM_DISABLE_VIPS disables the use of libvips
    • Config: Due to compatibility issues, libvips is disabled on 32-bit operating systems
    • Setup: Improved .deb packages for installation on Ubuntu/Debian Linux
    • Setup: Improved AUR packages for installation on Arch Linux (Thomas Eizinger)
    • Translations: Updated French and German
    "},{"location":"release-notes/#may-23-2024","title":"May 23, 2024","text":"

    Build 240523-923ee0cf7

    This update adds a scheduler so you can easily create database backups and re-index your library at regular intervals. It also includes many updated dependencies and support for ICC color profiles, which especially benefits Apple iPhone and professional users working with color spaces other than sRGB. \ud83c\udfa8

    Important Changes

    • If you keep the default settings, daily database backups will be automatically created, with up to 3 backup files being retained. This is to prevent the available storage space from filling up. We recommend setting the corresponding config options before installing the update if you want to disable scheduled backups, keep more backup files, or prefer a specific time for creating backups. The previously available --disable-backups flag has been deprecated in favor of these finer-grained options.
    • In order to preserve ICC color profiles and reduce memory usage, new thumbnails will be generated with the libvips image processing library. You can run the photoprism thumbs -f command in a terminal to regenerate your existing thumbs as needed, or delete the storage/cache/thumbnails folder and then re-index your library. To continue using the native image processing library, set PHOTOPRISM_THUMB_LIBRARY to \"imaging\" in your compose.yaml or docker-compose.yml configuration file. If you build from source or use one of our binary installation packages, the system on which you build and/or run PhotoPrism must have libvips >= 8.10 installed.

    What's new?

    • Colors: Added libvips support to preserve ICC profiles in thumbnails
    • Search: Clicking on a timestamp finds pictures taken on the same day
    • Search: Added a sort option to order search results by picture title
    • Review: Photos are automatically approved when adding them to an album
    • People: Faces tagged on private or archived pictures will be ignored
    • Index: *.thm thumbnail files are not used as primary image anymore
    • Index: Added a config option for scheduling automatic library rescans
    • Index: Improved recovery of metadata from sidecar YAML files
    • Upload: Improved ETA display when using the web upload dialog
    • Backups: Added config options for creating backups at regular intervals
    • Moments: Background worker no longer creates backups to avoid disk activity
    • Docker: Upgraded base image from Ubuntu 23.10 to Ubuntu 24.04 LTS
    • Security: Go has been updated to the latest stable release v1.22.3
    • Translations: Updated Chinese (traditional), Danish, French, and German

    Missing user interface translations have been generated with the help of DeepL and Google Translate. Native speakers are welcome to help us improve them, if necessary.

    "},{"location":"release-notes/#april-20-2024","title":"April 20, 2024","text":"

    Build 240420-ef5f14bc4

    Our new stable release comes with a long list of indexing and security-related improvements. Most notably, we've added support for 2-Factor Authentication (2FA) to protect your account in case someone gains access to your password. As all security-related changes had to be thoroughly tested, this is one of the updates that were longer in the making. We appreciate your patience while we've been working on this and would like to thank everyone involved! \ud83d\udd10

    What's new?

    • Account: Added support for 2-Factor Authentication (2FA)
    • Account: Added dialog to manage App Passwords from the UI
    • Places: Updated reverse geocoding data and standard map tiles
    • Albums: Fixed links to albums in the settings tab of the edit dialog
    • Photos: Non-JPEG files like HEIC are no longer flagged as stacks in the UI
    • Videos: Improved Intel QSV hardware transcoding support and performance
    • Videos: Added support for Material Exchange Format (MXF) files
    • UI/UX: Improved visibility of buttons and toggles in search results
    • Index: A warning is shown for files with an invalid filename extension
    • Index: Nested storage folders within the originals path are ignored
    • Import: Modification times are preserved when moving or copying files
    • Metadata: Media files with a matching ContentIdentifier can be stacked
    • Metadata: File mod time instead of birth time is used as creation time fallback
    • Metadata: Improved validation for focal length, f-number, and exposure values
    • Metadata: Stop words are no longer ignored when generating titles from filenames
    • WebDAV: File modification date is preserved if client submits an X-OC-MTime header
    • API: Added support for OAuth2 Client Credentials and Access Tokens
    • API: Added Prometheus-compatible metrics and monitoring endpoint
    • CDN: Improved Cross-Origin Resource Sharing (CORS) and cache headers
    • MariaDB: Info log is shown when waiting for the database to become available
    • MariaDB: Changed image name in Docker Compose config example for ARMv7
    • Docker: Missing user accounts are automatically created by the entrypoint script
    • Setup: Added ARMv7 tar.gz packages for installation without Docker
    • Performance: Added index for files.file_error to reduce query time
    • Security: Go has been updated to the latest stable release v1.22.2

    Missing user interface translations have been generated with the help of DeepL and Google Translate. Native speakers are welcome to help us improve them, if necessary.

    "},{"location":"release-notes/#november-28-2023","title":"November 28, 2023","text":"

    Build 231128-f48ff16ef

    Our latest service release provides updated dependencies and fixes for recently discovered issues. In addition, official installation packages with binaries for Linux are now available as an alternative to our Docker images. Please note that only experienced users should choose this installation method, since these do not include all dependencies and need to be set up manually.

    What's new?

    • Search: Improved camera and lens information in the cards view details
    • Search: Fixed cards view rendering when a lens has no model description
    • Search: Added filter to find pictures by resolution range in Megapixels (MP)
    • PWA: Fixed list of available icon sizes in the app manifest file
    • JPEG: Fixed regression when handling image files with EOF error
    • JPEG: Fixed indexing of image files with invalid color metadata
    • JPEG/PNG: Added panic handler for unexpected thumbnail save errors
    • HEIC: Libheif has been upgraded from version 1.13.0 to 1.17.1
    • RAW: Darktable has been upgraded from version 4.2.1 to 4.4.2
    • Videos: Improved performance when extracting still images for creating thumbnails
    • Vectors: Improved SVG conversion using RSVG instead of ImageMagick
    • Docker: Base image has been upgraded from Ubuntu 23.04 to 23.10 (Mantic Minotaur)
    • Setup: Added tar.gz, deb and rpm packages for installation without Docker
    • Security: Go has been updated to the latest stable release v1.21.4
    "},{"location":"release-notes/#october-21-2023","title":"October 21, 2023","text":"

    Build 231021-9abea5b55

    This update adds search filters for finding pictures by ISO number, focal length, aperture, and altitude. It also includes a number of user interface improvements, updated translations, as well as fixes for recently discovered issues. We would like to thank everyone who submitted pull requests, helped with testing, or contributed in other ways! \u2728

    What's new?

    • Search: Added filters for ISO number, focal length, and aperture range
    • Search: Added alt:... filter to find pictures by altitude range
    • Search: Cards view shows ISO number, focal length, aperture, and exposure
    • Live Photos: Fixed Google HEVC motion photo playback and transcoding
    • Live Photos: Improved indexing of related files with vendor-specific naming schemes
    • Metadata: Updated offline map data for more accurate timezone lookups
    • Metadata: Creation time is calculated with UTC offset if timezone is unknown
    • Config: Creation of default certificate is skipped if HTTPS/TLS is disabled
    • Translations: Updated German, Greek, and Romanian
    "},{"location":"release-notes/#october-11-2023","title":"October 11, 2023","text":"

    Build 231011-63f708417

    This service release includes an updated ARMv7 build, a number of usability improvements requested by our community, and fixes for recently discovered issues. We would like to thank everyone involved!

    What's new?

    • PWA: Fixed automatic screen orientation in Google Chrome on Android
    • Upload: Current album is preselected when using the mobile nav menu
    • Videos: Creation of thumbnails can only be disabled in experimental mode
    • Settings: Ability to permanently delete files is now enabled by default
    • RAW/HEIC: Original media information is shown in the cards view details
    • Live Photos: Embedded video files can be streamed and transcoded
    • Metadata: Improved camera make and model name normalization
    • Docker: An updated ARMv7 image is available on Docker Hub
    • Security: Go has been updated to the latest stable release v1.21.3
    "},{"location":"release-notes/#september-23-2023","title":"September 23, 2023","text":"

    Build 230923-e59851350

    Our latest release includes a redesigned Places view, with the search box moved to the top and a preview for selected clusters at the bottom. We've also added support for Samsung/Google Motion Photos, so you can view them like Apple Live Photos after re-indexing your library. Beyond those highlights, you'll get many usability improvements, new search filters, and fixes for recently discovered issues. A big thank you to everyone who contributed!

    What's new?

    • UX: Added a preview image to the Labels tab in the photo edit dialog
    • UX: Reduced padding in mosaic view in favor of larger thumbnails
    • UX: Edit dialog allows pasting latitude and longitude in a single operation
    • UX: Reduced the number of info notifications in the user interface
    • UX: Improved user interface styles, added new \"Chrome\" and \"Mint\" themes
    • Search: Added scan:false filter to find photos that are not scans
    • Search: Added favorite:false filter to find pictures not marked as favorites
    • Albums: New share preview shows album contents as a stack of Polaroids
    • Albums: Fixed preview image URL when sharing album links
    • Albums: Current album is preselected when opening the upload dialog
    • Albums: Last edited timestamp is updated when pictures are added
    • People: Fixed an error when reusing the name of a previously deleted person
    • Places: Added cluster view to browse pictures close to each other in an overlay
    • Places: Added support sub-km distances when searching for locations
    • Places: Added support for the label and category search filters
    • Places: Added map style selector and a scale for comparing distances
    • Archive: Added \"Delete All\" button to permanently delete all archived files
    • Library: Added option for admins to perform index and cache cleanup from the UI
    • Library: Fixed escaping of hash characters in folder names
    • Live Photos: Added support for Samsung Motion Photos
    • Live Photos: Added support for Google Camera Motion Photos
    • Live Photos: Fixed indexing of sidecar video file properties
    • Videos: Added support for AMD GPUs in install-gpu.sh script
    • Videos: Removed deprecated FFmpeg -vsync vfr command flag
    • Metadata: Changed order of field names from which the title is extracted
    • Metadata: Added support for reading fstop favorite flag from XMP sidecar files
    • Metadata: Samsung/Google Motion Photos are flagged as Live Photos
    • Config: Added support for serving HTTP requests over Unix sockets
    • Config: A lower cache duration can be set for video content
    • SQLite: Updates are performed in batches to limit the number of variables
    • Docker: Added support for user ID ranges 1201-1250 and 2000-2100
    • Security: Reduced bcrypt cost for faster login on small devices
    • Security: Go has been updated to the latest stable release v1.21.1
    • Translations: Updated Chinese (Simplified and Traditional)
    "},{"location":"release-notes/#july-19-2023","title":"July 19, 2023","text":"

    Build 230719-73fa7bbe8

    Our latest release includes new features and enhancements contributed by our community, a number of security improvements, as well as fixes for recently discovered issues. Thank you to everyone who submitted pull requests, helped with testing, signed up as a member, or contributed in other ways! We appreciate it very much.

    What's new?

    • Setup: Added a batch script for simplified installation under Windows
    • Search: Added geo:false filter to find pictures without GPS coordinates
    • Photos: JPEG files with missing EOI marker are automatically repaired
    • Photos: Fixed an error when opening panoramas taken with a Samsung S21
    • Videos: Added a config option to limit the resolution of transcoded videos
    • Videos: Fixed container and codec checks in photoprism convert command
    • Metadata: Dates in WhatsApp generated file names can be parsed
    • Metadata: Year 0000 is mapped to 0001 when parsing dates from Exiftool
    • Security: Default to a self-signed HTTPS/TLS certificate if no other certificate is available
    • Security: Clipboard contents are cleared on logout and when user privileges change
    • Security: Go has been updated to v1.20.6, which includes bug fixes and enhancements
    • Translations: Updated Japanese

    We recommend that you explicitly disable TLS by adding PHOTOPRISM_DISABLE_TLS: \"true\" to your compose.yaml or docker-compose.yml file when running PhotoPrism behind a reverse proxy. HTTPS could otherwise be accidentally enabled if a certificate matching the site URL is found or PHOTOPRISM_DEFAULT_TLS is set to \"true\".

    "},{"location":"release-notes/#june-25-2023","title":"June 25, 2023","text":"

    Build 230625-17242fb07

    This service release includes the latest translations contributed by our community, as well as fixes for recently discovered issues.

    What's new?

    • Albums: Invalid entries are automatically hidden and flagged as missing
    • CLI: Fixed an issue where entering a very long password could disable the login
    • Security: Updated third-party dependencies in backend and frontend
    • Translations: Updated Chinese (Simplified), Italian, and Japanese
    "},{"location":"release-notes/#june-15-2023","title":"June 15, 2023","text":"

    Build 230615-90a18f6e7

    This update includes new features and enhancements contributed by our community, as well as fixes for recently discovered issues. We would like to thank everyone involved!

    What's new?

    • Photos: Related albums are displayed in the Info tab of the edit dialog
    • Photos: Added a link from the Files tab to the related folder in the file browser
    • Moments: Added labels to match Holidays as well as additional Pets
    • CLI: Added photoprism find command to search the index for specific files
    • CLI: Fixed the photoprism import command destination parameter type
    • PikaPods: Fixed an issue that caused newly deployed instances to require a restart
    • Security: Updated third-party dependencies in backend and frontend
    "},{"location":"release-notes/#june-7-2023","title":"June 7, 2023","text":"

    Build 230607-9e086c7eb

    With this much anticipated update, our new high-resolution vector world map becomes available to all users. It also features a special terrain mode for mountain lovers, so you can view the \"Satellite\", \"Outdoor\" and \"Topography\" maps in 3D!

    What's new?

    • Places: Improved the level of detail of the freely available default world map
    • Places: Added terrain mode to display the satellite, outdoor and topography maps in 3D
    • Security: Go has been updated to v1.20.5, which includes bug fixes and enhancements
    • Translations: Updated Chinese (Simplified), Italian, and Slovak

    PhotoPrism\u00ae Plus

    • Config: CSP header is updated automatically when a CDN is configured
    "},{"location":"release-notes/#june-3-2023","title":"June 3, 2023","text":"

    Build 230603-378d4746a

    This service release fixes recently discovered issues and improves compatibility with the upcoming MariaDB v11.0. If you are upgrading from MariaDB 10.x to 11.0, please make sure that you replace command: mysqld with command: (followed by the command flags) in your compose.yaml or docker-compose.yml file, otherwise the database server might fail to start. Thank you to everyone who contributed with pull requests, reported bugs, and helped us test the changes!

    What's new?

    • Folders: Searching for substrings now returns all matching albums
    • Search: Fixed an issue where the \"Unknown country\" filter has been ignored
    • Navigation: Fixed account feature check when clicking on the profile picture
    • Config: Fixed setting the title of the search page based on the site title
    • MariaDB: Improved compatibility with the upcoming release 11.0
    • Security: Updated third-party dependencies in backend and frontend
    • Security: Go has been updated to v1.20.4, which includes bug fixes and enhancements
    • Translations: Updated Chinese (Traditional), Dutch, German, and French

    PhotoPrism\u00ae Plus

    • Security: Malicious client requests can be automatically detected and blocked
    "},{"location":"release-notes/#may-13-2023","title":"May 13, 2023","text":"

    Build 230513-0b780defb

    As promised, this update makes hardware transcoding and many other config options available to all users. A big thank you to all of our contributors, members, and sponsors, whose generous support has been and continues to be essential to the success of the project!

    What's new?

    • Config: Additional options are now available to all users
    "},{"location":"release-notes/#may-6-2023","title":"May 6, 2023","text":"

    Build 230506-9de9a3540

    This update resolves two recently reported issues and includes updated translations.

    What's new?

    • Sharing: Upload checks if files have been deleted
    • CLI: Logging output is reduced in production mode
    • Translations: Updated French
    "},{"location":"release-notes/#may-4-2023","title":"May 4, 2023","text":"

    Build 230504-cbf48798c

    This service release makes the Nordic theme and the Hide People feature available to all users. It also changes the theme order in Settings so that the freely available themes come first.

    What's new?

    • Settings: Changed order in the theme dropdown so the freely available themes come first
    "},{"location":"release-notes/#may-2-2023","title":"May 2, 2023","text":"

    Build 230502-c405f6eff

    With this major new release, you'll get a long list of new features and enhancements with a focus on performance, security, and file type support. In addition, our Plus Members can now register directly in the app to unlock additional features like vector graphics support and a new admin web UI for user and session management. Thank you to all contributors, members, and sponsors who made this possible!

    What's new?

    • UX: Improved user interface layout for right-to-left languages
    • UX: Improved highlight and background colors in the cards view
    • UX: Improved theme styles and search field contrast in Places
    • UX: Navigation shows the total number of pictures without those under review
    • UX: Enabled long-touch menu in photo viewer on iOS Safari
    • PWA: Increased allowed length of app name on home screen
    • PWA: Improved manifest.json for more reliable installation prompts
    • Themes: Added \"Carbon\", \"Neon\", and \"Nordic\" based on colors from nordtheme.com
    • Themes: Removed \"Electra\", \"Moonlight\", \"Seaweed\", and \"Cyano\"
    • People: Ambiguous faces are skipped when matching to improve performance
    • People: Entering names is faster with many faces tagged
    • Search: Added id:... filter to find pictures by Exif UID, XMP Document ID or Instance ID
    • Search: Increased batch size for better performance when loading results
    • Search: Deleted albums are ignored when using the unsorted filter
    • Search: Sepia colored pictures are excluded when using the mono filter
    • Photos: Image orientation can be changed through the user interface
    • RAW: Upgraded RawTherapee from v5.8 to v.5.9 to fix ProRAW support
    • Videos: Improved player compatibility with browser plugins
    • Videos: Improved preview image generation depending on duration
    • Videos: Playback durations of less than one second can be indexed and displayed
    • Videos: Added .dv to the list of known video file types
    • Videos: Specific video and audio streams can be selected for transcoding
    • Videos: Improved detection of HEVC support for Google Chrome
    • Albums: Added extended search form with sorting options
    • Albums: Fixed form field styles in the share dialog
    • Albums: Double quotes in album names are replaced by Unicode characters
    • Albums: Improved error handling and validation of query parameters
    • Albums: \"Download as zip\" button is displayed on mobile screens
    • Moments: Changed default sort order in the overview to \"newest\"
    • Folders: Search is case-insensitive and uses wildcards for improved usability
    • Metadata: GPS coordinates are normalized to be within a common range
    • Metadata: Out-of-range altitude values are ignored to prevent indexing errors
    • Metadata: Date defaults caused by software or camera bugs are ignored
    • Metadata: Software name is displayed on the Files tab, if available
    • Metadata: Valid year range in Exif data and filenames has been extended from 1990 to 1970
    • Metadata: Scanned images are automatically recognized by device name
    • Metadata: Added TakenAtLocal to YAML backups to prevent incorrectly restored times
    • Metadata: Notes can be extracted from the Comment and UserComment fields
    • Index: Improved performance by skipping updates when there are no changes
    • Index: Improved performance when flagging hidden files
    • Index: Added file format support for Adobe Photoshop PSD images
    • Index: Added support for decoding JPEG XL and playing PNG animations
    • Index: Corrupted JPEG images are automatically repaired if necessary
    • Index: TIFF images with unsupported file format features can be converted
    • Upload: Estimated time remaining is displayed in minutes and seconds
    • Download: Added settings to choose which files to download by default
    • Backups: Improved backup and restore commands to better handle large index dumps
    • WebDAV: Enabled access to the originals and import folders in read-only mode
    • WebDAV: Replaced client library to prevent incomplete uploads to other servers
    • WebDAV: Download sync is prevented when read-only mode is enabled
    • API: Search results can be sorted randomly to get a random set of pictures
    • API: HEAD requests are now supported for frontend bootstrap paths
    • CLI: Added file extension flag to the photoprism convert command
    • CLI: Commands create thumbnails and convert files in deterministic order
    • Config: Migrations are skipped if the same version has already been initialized
    • Config: Use dynamic social preview image based on app name
    • Config: Custom template path is not searched for files if not specified
    • Config: Advanced settings include additional options for PNGs and vector graphics
    • Config: Added advanced HTTP cache control options
    • Config: Added option to stream videos over a Content Delivery Network (CDN)
    • Docker: Ubuntu base image has been upgraded from v22.04 to v23.04
    • Docker: MariaDB image and binaries have been upgraded from v10.9 to v10.11
    • Podman: Added config examples for users of Red Hat-based Linux distributions
    • Security: Improved bcrypt password support with explicit 72-character limit
    • Security: Go has been updated to v1.20.3, which includes bug fixes and other improvements
    • Translations: Added Afrikaans (South Africa) and Basque (Euskara)
    • Translations: Updated Arabic, Bulgarian, Chinese, Czech, Dutch, Estonian, French, German, Italian, Malay, Russian, Ukrainian and many others

    PhotoPrism\u00ae Plus

    • Auth: Admins can manage user accounts and active sessions through the web UI
    • Index: Added file format support for SVG, AI, PS and EPS vector graphics

    Our new Plus License is used for both the extensions we provide to our members and the standard Docker images available on Docker Hub. This allows us to bundle the extensions with the compiled application, while the Community Edition remains freely available under the terms of the GNU Affero General Public License (AGPL).

    If you don't plan to use any additional features, you can alternatively use the \"ce\" tag instead of \"latest\" to get a slightly smaller Docker image distributed under the AGPL. Note that system dependencies and other third-party components included in this image are still subject to additional terms and conditions.

    View Membership FAQ \u203a View Plus License \u203a

    "},{"location":"release-notes/#november-18-2022","title":"November 18, 2022","text":"

    Build 221118-e58fee0fb

    This service release includes compatibility fixes for MariaDB 10.10, the latest translations, a new theme, and updated dependencies. We recommend not using the :latest tag for the MariaDB Docker image and to upgrade manually by changing the tag once we had a chance to test a new major version.

    What's new?

    • UI: Added \"Electra\" theme
    • MariaDB: Compatibility fixes for version 10.10
    • Translations: Updated Czech and Estonian
    "},{"location":"release-notes/#november-17-2022","title":"November 17, 2022","text":"

    Build 221117-3268c4de8

    This update includes video transcoding improvements and the latest translations contributed by our community.

    What's new?

    • Videos: Fixed installation of Intel Quick Sync drivers for hardware transcoding
    • Videos: Added .m2ts to known file extensions
    • Translations: Updated Chinese (Traditional) and Estonian
    "},{"location":"release-notes/#november-16-2022","title":"November 16, 2022","text":"

    Build 221116-122ebfb70

    With this update you get the latest translations, updated dependencies, and two metadata bug fixes. Thanks to all who contributed!

    What's new?

    • Metadata: Bad Unicode strings are sanitized automatically
    • Metadata: UTC can be overridden by local time with unknown zone
    • MariaDB: Unsupported versions are allowed in \"unsafe\" mode
    • Translations: Added Estonian
    • Translations: Updated Polish, Italian, Korean, Romanian, and Chinese (Traditional)
    "},{"location":"release-notes/#november-5-2022","title":"November 5, 2022","text":"

    Build 221105-7a295cab4

    This service release provides UX improvements for the photo editing dialog and includes the latest translations contributed by our community. Note that our guides now use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose to start and stop your instance.

    What's new?

    • UX: Improved layout of form fields in photo edit dialog
    • Account: Disabled \"gender\" dropdown when busy or in demo mode
    • Docker: Changed \"docker-compose\" command to \"docker compose\"
    • Translations: Updated Estonian, Hungarian, and Russian
    "},{"location":"release-notes/#november-4-2022","title":"November 4, 2022","text":"

    Build 221104-20d180b21

    A small update featuring improved NVIDIA GPU support, the latest translations contributed by our community, and updated dependencies.

    What's new?

    • NVIDIA: Added a ready-to-use docker-compose.yml config example
    • NVIDIA: Updated FFmpeg parameters for hardware video transcoding
    • NVIDIA: Updated install-gpu.sh script and related documentation
    • Translations: Updated Chinese
    "},{"location":"release-notes/#november-3-2022","title":"November 3, 2022","text":"

    Build 221103-211eb36ea

    With this update you'll get the latest translations contributed by our community, updated dependencies as well as a few minor bug fixes and improvements.

    What's new?

    • Index: Paths starting with _. and __ like __MACOSX are ignored
    • Config: Updated new trusted proxy header options and command help
    • MariaDB: Improved server version check on startup
    • Security: Go has been updated to v1.19.3, which includes security fixes
    • Translations: Updated Chinese, French, Norwegian Bokm\u00e5l, and Romanian
    "},{"location":"release-notes/#november-2-2022","title":"November 2, 2022","text":"

    Build 221102-905925b4d

    Due to the many new features, enhancements and bug fixes, this is one of those updates that took longer to release. Before upgrading, please read the full release notes and note that this release does not yet include support for user roles other than Admin, as we need to specify, create and test each new role before we can release it. Once this is done, we will also provide additional user management documentation.

    We've generated missing translations with the help of DeepL and Google Translate. Native speakers are invited to help us improve those if needed. A special thank you to everyone who contributed!

    Breaking Changes

    • In order to improve security and compatibility, the default Docker image is now based on Ubuntu 22.04 LTS (Jammy Jellyfish) instead of Debian 12 (Bookworm). The entrypoint script has been updated to preserve group permissions required for hardware transcoding.
    • Session and user management have been re-implemented. If you are upgrading from a preview build, you will need to run the photoprism users reset --yes command in a terminal after the upgrade to recreate the new database tables so that they are compatible with the stable version. This will not affect your pictures or albums.
    • Upgrading from the last stable version should work without any problems. However, if you have already created additional accounts with the previously offered unofficial multi-user support, you will notice that only the main admin account is migrated automatically. Run photoprism users legacy in a terminal to display the legacy accounts so you can migrate them manually if needed.
    • Sharing link visitors can now see the picture locations in the regular album view and optionally on a map after clicking the link. Based on user feedback, we may add settings to hide the locations for enhanced privacy.
    • We recommend performing a full rescan after the upgrade to take advantage of new search filters and sort options.
    • Indexing is also necessary to find and view HEIC, DNG, and AVIF images that were previously unsupported or had errors. In some cases with incorrectly converted images, it may be necessary to recreate the JPEG sidecar files by running the photoprism convert -f command in a terminal before starting the rescan. To regenerate your thumbnails, run photoprism thumbs -f.

    What's new?

    • Auth: Session and user management have been re-implemented
    • Auth: API has been improved to prevent unnecessary re-logins
    • Auth: User interface routes have been prefixed with /library
    • UX: Scroll position is restored again when navigating back
    • UX: Loading screen and mobile toolbar menu have been redesigned
    • UX: Improved user interface styles for RTL languages
    • Search: Added city:... and state:... to filter by location details
    • Search: Results can now also be sorted by \"File Size\" and \"Video Duration\"
    • Albums: Added breadcrumbs to navigate back on large screens
    • Albums: Deselected labels are ignored when adding photos by label
    • HEIC: Added support for Sony's .HIF file extension
    • HEIC: Updated heif-convert tool to fix conversion problems
    • AVIF: Added support for the AV1 Image File Format
    • RAW: Updated Darktable from v3.8.1 to v4.0.1 (AMD64 only)
    • ProRAW: JPEGs embedded in .DNG files can be searched and viewed
    • Videos: Added VAAPI hardware AVC encoder support
    • Index: Delayed RAW file format check to improve indexing performance
    • Import: Selection of a source folder with dots in its name is now possible
    • Import: Related original names are indexed in addition to the main filename
    • Settings: Services cannot be managed in public mode to increase security
    • Backups: Worker no longer recreates all album YAML files on every run
    • Metadata: Added more place names with known countries
    • Metadata: Default to UTC when reading time from XMP file
    • Config: Increased default resolution limit from 100 to 150 MP
    • Config: imprint info text option has been renamed to legal-info
    • SQLite: Added busy timeout preset to reduce locking errors when indexing
    • MariaDB: Startup fails with an error message if an unsupported version is used
    • Docker: Default image is based on Ubuntu 22.04 LTS (Jammy Jellyfish)
    • Docker: Switched from gosu to setpriv in entrypoint.sh script
    • Security: New files are created without execution permission
    • Security: Go has been updated to v1.19.2, which includes security fixes
    • Translations: Added Persian
    • Translations: Updated Chinese, Dutch, Finnish, French, German, Spanish, and many more
    "},{"location":"release-notes/#september-1-2022","title":"September 1, 2022","text":"

    Build 220901-f493607b0

    With this update you get all the latest translations contributed by our community, mobile navigation enhancements, updated dependencies and, as usual, fixes for recently discovered issues. Thanks to everyone involved!

    What's new?

    • UX: Mobile toolbar menu has been redesigned and expanded
    • UX: Improved search result and Gemstone theme styles
    • Search: Known file extensions are stripped from name:... filter string
    • Library: Indexing will be aborted if the originals folder is empty
    • Videos: Local time is extracted from DateTimeOriginal if possible
    • Albums: All pictures are shown if \"Private\" has been disabled in Settings
    • Thumbs: photoprism thumbs command regenerates thumbnails of sidecar files
    • Docker: Permissions of original media files are no longer updated on startup
    • Build: Go has been updated to v1.19, which includes fixes and enhancements
    • Build: NodeJS has been updated from v16 to v18
    • Translations: Added Catalan, Finnish, Ukrainian, and Slovene
    "},{"location":"release-notes/#july-30-2022","title":"July 30, 2022","text":"

    Build 220730-0e1222c83

    Fixes the activation of public mode with PHOTOPRISM_AUTH_MODE instead of PHOTOPRISM_PUBLIC.

    What's new?

    • Auth: Activate public mode via PHOTOPRISM_AUTH_MODE=\"public\"
    "},{"location":"release-notes/#july-28-2022","title":"July 28, 2022","text":"

    Build 220728-729ddd920

    Includes indexing, metadata, and authentication enhancements, as well as updated translations.

    What's new?

    • Library: Added support for indexing and importing symbolically linked files
    • Thumbs: Creating redundant JPEG files is skipped to save disk space
    • Zip: Improved file system rights detection and temporary file handling
    • Metadata: Creation time is extracted from DateTimeCreated, if available
    • Metadata: Unknown values are ignored when parsing timestamps
    • Purge: Fixed SQL error when the photo ID of a file is missing
    • Cleanup: Improved logging when deleting related sidecar files
    • Config: Added PHOTOPRISM_AUTH_MODE option to select authentication mode
    • Config: Improved inline docs in docker-compose.yml examples
    • Build: Updated Go to v1.18.4, which includes a number of security and compiler fixes
    • Translations: Added Greek

    Breaking Changes

    • Config: PHOTOPRISM_AUTH has been removed in favor of PHOTOPRISM_AUTH_MODE
    • Config: PHOTOPRISM_PUBLIC has been deprecated in favor of PHOTOPRISM_AUTH_MODE
    "},{"location":"release-notes/#june-29-2022","title":"June 29, 2022","text":"

    Build 220629-5d7448d2

    With this update, you'll enjoy a much faster and smoother scrolling experience as well as direct streaming of OGV, VP8, VP9, AV1, WebM and HEVC videos if they do not exceed the configured bitrate limit. Special thanks to Heiko Mathes and Andre Carrera for their contributions!

    • UX: Much faster and smoother scrolling experience in albums and search results
    • Videos: Direct streaming of OGV, VP8, VP9, AV1, WebM, and HEVC where supported
    • Videos: Fixed incorrect frame rate when using NVIDIA hardware transcoding
    • Sharing: Fixed the spacing of the top navigation toolbar on small screens
    • RAW: Display actual dimensions as Exif metadata can be wrong, e.g. for .NEF files
    • WebDAV: Endpoints have been disabled in public mode as they cannot be used
    • API: Maximum number of search results has been increased to 100,000 files
    • CLI: Config command also lists disable-webdav and http-compression
    • Documentation: Added notes about manual session invalidation and other known issues
    • Translations: Updated Arabic, Dutch, Polish, Japanese, Chinese, French, German, and Italian
    "},{"location":"release-notes/#june-17-2022","title":"June 17, 2022","text":"

    Build 220617-0402b8d3

    This update features updated translations as well as fixes for recently discovered issues.

    • Albums: A deleted album is restored when trying to add a new album with the same name
    • WebDAV: Added support for auto indexing/importing in a sub-directory on a shared domain
    • Translations: Updated Arabic, Czech, Korean, and Norwegian Bokm\u00e5l
    "},{"location":"release-notes/#june-14-2022","title":"June 14, 2022","text":"

    Build 220614-dea9ff68

    A small but important update that includes translations to Arabic, a migration fix for MariaDB, and many updated dependencies.

    • Security: Updated Go to v1.18.3, which includes TLS and validation fixes
    • MariaDB: Removed migration that could corrupt photo descriptions in the index
    • Translations: Added Arabic, updated Danish and Polish
    "},{"location":"release-notes/#may-28-2022","title":"May 28, 2022","text":"

    Build 220528-efb5d710

    This update includes translations that were recently contributed via translate.photoprism.app. Missing translations were added by us using DeepL and Google Translate. Native speakers are invited to help improve those if needed. Thank you very much!

    • UX: Mobile toolbar menu has been redesigned and made accessible in public mode
    • Themes: Gemstone and Raspberry have been updated
    "},{"location":"release-notes/#may-27-2022","title":"May 27, 2022","text":"

    Build 220527-005770ca

    This update improves navigation fonts and mobile submenu colors for light themes. We are also working to establish PhotoPrism+ as the name for our community membership and associated benefits. For this, membership information in the app, on our website, on GitHub Sponsors and Patreon is gradually being updated.

    • UX: Fixed light theme colors of mobile navigation submenu
    • UX: Splash screen has been updated and no longer depends on admin theme
    "},{"location":"release-notes/#may-24-2022","title":"May 24, 2022","text":"

    Build 220524-c76de0df

    This service release fixes potential issues with our new Debian 12-based Docker image that shipped with the last update. These may have prevented users from deploying it without making changes to their environment. In our ongoing effort to improve usability and performance, we have also implemented a number of UX/UI optimizations, such as using the default operating system font instead of Google's Roboto.

    • UX: Added submenu to mobile navigation toolbar
    • UX: Updated splash screen information and animation
    • UX: Numerous translations have been added and updated
    • UX: Improved UI styles for right-to-left languages
    • Auth: Short initial passwords are permitted again to avoid login problems
    • Logs: Repeated log messages are omitted to prevent feedback loops
    • Logs: Trace and debug messages are no longer displayed in the UI to avoid information leaks and reduce websocket communication overhead
    • Search: mono:true filter now omits files with unknown chroma
    • Docker: Removed incorrect permission check for storage folder on startup
    • Docker: Supported User and Group ID ranges have been documented

    Thank you to everyone who helped with testing, signed up as a member, or contributed in other ways! We appreciate it very much.

    "},{"location":"release-notes/#may-17-2022","title":"May 17, 2022","text":"

    Build 220517-b9c68f8f

    This update features search UX enhancements, a new Docker base image based on Debian 12 \"Bookworm\", as well as fixes for recently discovered issues. Front- and backend translations in numerous languages have been added and updated. Thanks to all involved!

    • File Formats: Added native support for WebP images and animated GIFs
    • UX: RAW files are skipped by default when downloading photos and ZIP archives
    • Auth: Passwords must have at least 8 characters to mitigate brute-force attacks
    • Search: User interface and database performance optimizations
    • Search: Fixed occasional lag when entering queries in the search toolbar
    • Search: Improved album:... filter supports numeric names, albums:.. also AND/OR
    • Search: Improved camera:... and lens:... filters accept names in addition to IDs
    • Search: Added square:yes and landscape:yes filters
    • Albums: \"Add to album\" dialog preloads more names for auto-completion
    • Albums: Album names are shortened if necessary to avoid errors when saving
    • Albums: Fixed accidental creation of duplicates by pressing Enter multiple times
    • People: Improved logging and fixed potential issues with matching unrecognized faces
    • Places: Number of pictures rendered on the map has been limited to 500,000
    • Library: Added button to clear log history under Library > Errors
    • Library: RAW previews and the number of actual files are shown under Originals
    • Library: Disabled hidden files warning while indexing as it can be misleading
    • Index: Fixed errors when re-indexing libraries with archived photos
    • Index: RAW and video conversion commands run in a virtual home directory
    • Thumbnails: Reduced default JPEG quality from 92 to 85 to optimize storage and loading
    • Metadata: Manual local time changes are always preserved when reindexing
    • Metadata: Fixed Exif orientation flag when converting HEIF/HEIC images to JPEG
    • Metadata: Fault-tolerant parsing of timestamps from Exif and JSON sidecar files
    • Metadata: Improved parsing of two-digit years in original file paths
    • Metadata: Exif IFD1 tags with existing IFD0 values are ignored to improve standard compliance
    • Metadata: Brute-force search is skipped by default if no Exif headers were found in JPEG, PNG, TIFF, and HEIF files
    • Metadata: SubSecDateTimeOriginal and SubSecCreateDate timestamps are preferred
    • WebDAV: Up- and download sync can no longer be enabled at the same time to prevent unexpected behavior
    • WebDAV: Added timeout/retry settings and improved handling of sync errors
    • WebDAV: Fixed sharing videos and uploading automatically created albums
    • CLI: Renamed --config-file to --defaults-yaml and improved command help
    • CLI: Added short names for common config flags, e.g. -i for --wakeup-interval
    • CLI: Run photoprism show tags to display metadata tags and supported standards
    • CLI: Run photoprism show formats to display supported media and sidecar file formats
    • CLI: Run photoprism show filters to display a search filter overview with examples
    • Config: Improved FFmpeg parameters for Intel QSV hardware transcoding
    • Config: Added NVIDIA hardware video transcoding support for members
    • Config: Added --disable-raw flag to disable indexing and conversion of RAW files
    • Config: Added --resolution-limit option to skip high-resolution images when indexing
    • Docker: New Debian 12 \"Bookworm\" base image with FFmpeg 4.4.1 and Darktable 3.8.1
    • Docker: Added default users and groups for enhanced video transcoding compatibility
    • Translations: Added Swedish, Romanian, Turkish, Lithuanian, Bulgarian, Malay, and Croatian
    "},{"location":"release-notes/#march-2-2022","title":"March 2, 2022","text":"

    Build 220302-0059f429

    The Docker images for this release are based on Debian 11 \"Bullseye\" and include many updated dependencies such as Darktable 3.8. Behind the scenes, the build process has also been improved so that it will be easier to provide standalone packages in the future.

    • Auth: New login screen with more space for buttons, links, and legal information
    • Metadata: Redesigned file details tab in the edit dialog
    • Metadata: Support for Zulu formatted timestamps in Exiftool JSON and XMP
    • Sharing: Fixed upload of complete albums via WebDAV
    • Sharing: Manual WebDAV upload of video and RAW files
    • iOS: Fixed multi-select via long touch in Safari PWA mode
    • API: Added cache control header for faster thumbnail loading
    • Config: Simplified configuration of Unix domain socket database connections
    • Config: Added --imprint and --imprint-url to display legal information in the footer
    • Docker: Automatic installation of compatible CPU and GPU drivers
    • Translations: Updated all front- and backend locales

    You can now join us on translate.photoprism.app to help translate the UI!

    "},{"location":"release-notes/#january-21-2022","title":"January 21, 2022","text":"

    Build 220121-2b4c8e1f

    We've generated missing translations with the help of DeepL and Google Translate. Native speakers are invited to help us improve those if needed. Learn how to contribute.

    • Minimum memory requirements have been reduced to 3 GB
    • Photos: Fixed buttons in full screen view
    • People: Fixed typo that prevented face matching optimization
    • Moments: Improved update performance on MariaDB
    • Translations: Pre-translated missing UI messages
    "},{"location":"release-notes/#january-18-2022","title":"January 18, 2022","text":"

    Build 220118-76c94a1f

    • Auth: Logout redirects to base URI instead of site root
    • Videos: Excluded streaming from gzip compression
    • Videos: Fixed Content-Type header and streaming in Safari
    • Folders: Fixed search query string substitutions and sanitation
    • UI: Updated information and links in Settings > About
    • UI: Improved bootstrap template rendering performance
    "},{"location":"release-notes/#january-7-2022","title":"January 7, 2022","text":"

    Build 220107-f5b7ef83

    Based on our zero bug policy, this update focuses on bug fixes, security, and UX enhancements for search filters, metadata, and the indexer. In addition, one of the merged pull requests may improve face recognition performance on smaller devices and with large libraries.

    • People: Improved update performance on MariaDB
    • People: Improved contrast of person selection menu
    • Albums: Private pictures are excluded from download as zip
    • Search: Improved query parser for additional security
    • Search: Added uid:... search filter to find photos by uid
    • Search: keywords:... filter does not exclude stopwords anymore
    • Index: Original filenames can be extracted from Exiftool JSON
    • Index: More accurate and resilient handling of related files and photo stacks
    • Live Photos: HEIF is used to create primary JPEG instead of a MOV still image
    • Metadata: Reduced log level for missing Exif data from warn to info
    • Metadata: Increased size of projection and color profile fields to 40 characters
    • Backups: Improved help for config options and CLI commands
    • Help: Fixed reverse proxy documentation links
    • Config: Added Apple Video Toolbox hardware transcoding support for macOS
    • Config: Added /opt/photoprism to search path for asset and storage folders
    "},{"location":"release-notes/#december-15-2021","title":"December 15, 2021","text":"

    Build 211215-93b26f19

    PhotoPrism is not directly affected by the Apache Log4j vulnerability. Logs may still contain messages that can cause harm if consumed by an unpatched Java application. As a precaution, this release includes additional rules and filters to validate user input.

    • Sharing: Fixed album link redirect on shared domains
    • Import: More helpful warning when another import is already running
    • Docker: ARMv7 image for 32-bit processors and operating systems
    "},{"location":"release-notes/#december-10-2021","title":"December 10, 2021","text":"

    Build 211210-2cb90e7e

    Starting with this release, the regular multi-arch Docker image is 64-bit only. A 32-bit version of our stable release for older devices is offered separately. This frees up development and infrastructure resources with minimal impact.

    • Security: Updated Go to v1.17.5, which includes HTTP/2 and networking fixes
    • People: Concurrent updates are no longer possible to prevent inconsistencies
    • Places: Additional logs to detect invalid GPS coordinates in metadata
    • SQLite: Reduced routine maintenance log levels and fixed migration warnings
    • Thumbnails: Apple Display P3 profile support for more accurate colors
    • Translations: Updated French
    "},{"location":"release-notes/#december-3-2021","title":"December 3, 2021","text":"

    Build 211203-fdb6b5e1

    Since the funding goal required to make all features and maps generally available has not been reached, early-access features have been renamed to sponsor features in this update. Offline and high-resolution street maps remain free for everyone, while hybrid, topographic, and outdoor maps are now a member feature. We believe this is fair. A big thank you to all our sponsors and contributors!

    • CLI: Improved parameter and command descriptions
    • CLI: Reset command optionally also deletes files in the cache folder
    • Config: Improved docker-compose.yml examples
    "},{"location":"release-notes/#november-30-2021","title":"November 30, 2021","text":"

    Build 211130-13cfcf6d

    • Videos: Live photos page has been added to the sub-navigation
    • Albums: Manually created albums are sorted by name, with favorites first
    • Places: Improved location details in border regions and near Paris
    • PWA: Updated app icons, style is now also applied to the user interface

    For our sponsors and contributors:

    • UI: New Abyss and Gemstone dark themes \ud83d\udc8e
    "},{"location":"release-notes/#november-28-2021","title":"November 28, 2021","text":"

    Build 211128-7e8974fd

    Official support for MySQL 8 is discontinued with this update as Oracle seems to have stopped shipping new features and enhancements. As a result, the testing effort required before each release is no longer feasible. We recommend upgrading to MariaDB 10.6 or later. PostgreSQL support is planned for 2022 without a specific release date yet.

    • CLI: photoprism migrations run --failed will re-run previously failed migrations
    "},{"location":"release-notes/#november-27-2021","title":"November 27, 2021","text":"

    Build 211127-86c43159

    When possible, location estimates now include a latitude and longitude. Photos load faster when you open them in Places, and the viewer sorts them by distance. Time zone handling has been completely reworked, in particular for UTC. The Docker base image has been updated to Ubuntu 21.10, which ships with Darktable 3.6 among other updated dependencies.

    • UX: Redesigned splash screen based on theme colors
    • Places: Viewer loads faster and sorts photos by distance instead of date
    • Places: Less frequent estimates to reduce background activity
    • Places: Normalized names of states, oceans, and lakes
    • Places: Updated location data from OpenStreetMap
    • Places: State albums are grouped by country name
    • Folders: Path names are searched in addition to titles
    • People: Improved face detection performance
    • People: Fixed naming faces in non-primary files
    • People: Optimized matching of children's faces
    • RAW: Updated Darktable to 3.6.0
    • Metadata: Improved estimates and UTC time zone handling
    • Metadata: Altitude is indexed even if coordinates are missing
    • Auth: Usernames are not case-sensitive anymore
    • CLI: Added --force flag to photoprism optimize command
    • CLI: Improved parameter and command descriptions
    • Config: Improved docker-compose.yml examples
    • Translations: Added Bahasa Indonesia and Hungarian
    • Translations: Updated Polish and Italian

    For our sponsors and contributors:

    • CLI: Run photoprism places update to retrieve updated location details
    • Config: Set PHOTOPRISM_APP_ICON to choose an alternative PWA icon
    "},{"location":"release-notes/#october-18-2021","title":"October 18, 2021","text":"

    Build 211018-e200f322

    • UI: Updated Lavender theme
    • Places: Fixed maps initialization after reload in non-public mode
    • Search: Added live and raw:true filters as alternative to type:\u2026
    • Search: Added faces:new alias for face:new
    • Config: Maximum background worker interval has been increased to 7 days
    • Security: Added Content-Security-Policy header to prevent framing attacks
    • Translations: Updated Russian and Slovak

    For our sponsors and contributors:

    • UI: New Vanta dark theme \u2728
    "},{"location":"release-notes/#october-10-2021","title":"October 10, 2021","text":"

    Build 211010-83b4f783

    • Translations: Fixed German frontend typo
    • Translations: Updated all backend locales

    We've generated missing translations with the help of DeepL and Google Translate. Native speakers are invited to help us improve those if needed. Learn how to contribute.

    "},{"location":"release-notes/#october-9-2021","title":"October 9, 2021","text":"

    Build 211009-d6cc8df5`

    • UX: Improved wording of search result notifications
    • UX: Fixed sidebar navigation on small screens
    • Users: Show name and email in sidebar navigation
    • Folders: Directory names listed in .ppignore are ignored
    • Config: Allows bypassing low memory suggestion
    • Docs: Updated about page
    • Translations: Updated all frontend locales
    "},{"location":"release-notes/#october-7-2021","title":"October 7, 2021","text":"

    Build 211007-8f55d6f8

    • People: Improved stability and performance of new faces overview page
    • Index: Duplicate error logs caused by broken JPEG files have been removed
    • UX: Enhanced visibility of file errors in the edit dialog files tab
    • CLI: Revised descriptions of commands and configuration flags

    For our sponsors and contributors:

    • People: Recognized faces can be hidden on the overview page
    "},{"location":"release-notes/#october-2-2021","title":"October 2, 2021","text":"

    Build 211002-bf015326

    • People: Enhanced UI / UX for renaming and merging faces
    • People: Improved face detection accuracy
    • Labels: Improved photo count accuracy
    • Covers: Thumbnails load and update faster
    • Search: Finds titles when query is too short for full-text index
    • Search: name:\u2026 filter ignores path and extension
    • Videos: Optional Intel GPU hardware transcoding support
    • Index: Automatic cleanup of orphaned file entries
    • Logs: Updated log messages for improved readability
    • Translations: Updated German and French
    • Docker: Simplified installation of TensorFlow with AVX / AVX2 support
    • Docker: Entrypoint script uses prefixed environment variables, UID and GID are deprecated
    "},{"location":"release-notes/#september-25-2021","title":"September 25, 2021","text":"

    Build 210925-96168e4b

    • Recognizes faces so that specific people can be found
    • UX: Improved UI design, navigation, and wording
    • Search: Omit full-text index if query is too short
    • Search: Added keywords:\u2026, subjects:\u2026, and albums:\u2026 filters
    • Places: Internationalized maps incl RTL support
    • Labels: Added photo counts to overview page
    • Albums: Fixed share expiration date in form label
    • Calendar: Empty month albums are hidden
    • Viewer: Photos will be updated when search filters change
    • Index: Ignore Synology @eaDir folders
    • Import: Ignore dot files listed in .ppignore
    • Upload: Added more detailed error logs
    • Videos: Skip related images when downloading
    • Videos: Added .mp as known MP4 file extension
    • Videos: Default to UTC as metadata time zone
    • Exiftool: Enabled large file support
    • Metadata: Improved Exif parser with cycle detection
    • Metadata: Support for long projection type names like transverse-cylindrical
    • Config: Added RAW file extension blacklists for Darktable and RawTherapee
    • Config: Added disable options for image classification and facial recognition
    • Config: Added support for non-root site URLs
    • Config: Added content delivery network URL option
    • MariaDB: Set explicit table engine, charset, and collation
    • MariaDB: Added log message for old versions with broken table name resolution
    • Docker: Added HOME env for Darktable & RawTherapee
    • Docker: Single multi-arch image for AMD64, ARM64, and ARMv7
    "},{"location":"release-notes/#may-23-2021","title":"May 23, 2021","text":"

    Build 210523-b1856b9d

    • RAW: Added RawTherapee flag to use existing sidecar files
    • Import: Never remove ignored folders such as for Syncthing
    "},{"location":"release-notes/#may-20-2021","title":"May 20, 2021","text":"

    Build 210520-4b32bac7

    • Docker: Fixed home directory permissions in new base image
    • HEIF: Test if JPEG was already rotated based on video metadata
    "},{"location":"release-notes/#may-19-2021","title":"May 19, 2021","text":"

    Build 210519-24b5c7e6

    • Metadata: Updated Exiftool to fix security issue
    "},{"location":"release-notes/#may-18-2021","title":"May 18, 2021","text":"

    Build 210518-80981c25

    • Safari: Fixed PWA file download on iOS
    • Docker: Added config example for scheduled background tasks
    • Docker: Updated base image includes Darktable 3.4.1, RawTherapee 5.8, and FFmpeg 4.3.2
    • TensorFlow: Improved error handling
    • Translations: Updated French
    "},{"location":"release-notes/#may-5-2021","title":"May 5, 2021","text":"

    Build 210505-d3e53a89

    • UI: Improved RTL (right-to-left language) alignment
    • RAW: Added config options to disable specific converters
    • Metadata: Preserve stopwords in existing keywords
    • Metadata: Allow single quotes in keywords
    • WebDAV: Keep favorite flag when uploading via PhotoSync
    • Translations: Updated Dutch and German
    "},{"location":"release-notes/#april-26-2021","title":"April 26, 2021","text":"

    Build 210426-da6e948f

    • UI: Added Yellowstone theme for members, unlocked Grayscale theme for everyone
    • Metadata: Support for XMP sidecar CreateDate and Keywords
    • Metadata: Merge keywords from different sources
    • Translations: Updated Hebrew
    "},{"location":"release-notes/#april-22-2021","title":"April 22, 2021","text":"

    Build 210422-97e75b04

    • UX: Improved touch event accuracy
    • UX: Optimized rendering on small screens
    • UX: Fixed autocomplete in \"add to album\" dialog
    • HEIF: Prevent redundant sidecar JPEG files
    • Backup: Added command flags and usage docs
    • Translations: Added Danish and Kurdish
    "},{"location":"release-notes/#february-22-2021","title":"February 22, 2021","text":"

    Build 210222-ac5a9d5e

    • UX: Autofocus for input fields and confirm on enter
    • Restore: Find YAML album backups in originals folder
    • Metadata: Improved location labels and moments
    • Thumbnails: Fixed auto-rotation for HEIF, TIFF, and PNG images
    • Translations: Added Norwegian (Bokm\u00e5l)
    "},{"location":"release-notes/#february-17-2021","title":"February 17, 2021","text":"

    Build 210217-49039368

    • Videos: Optimized transcoding parameters
    • Videos: Use AAC audio for MP4 transcoding
    • Metadata: Default to landscape orientation if data is invalid
    • Translations: Updated Brazilian Portuguese
    "},{"location":"release-notes/#february-16-2021","title":"February 16, 2021","text":"

    Build 210216-4939e36a

    • UX: Automatically hide scrollbar in photo viewer and Places
    • Delete: Permanently remove all related sidecar files
    • Videos: Added transcoding config options
    • Videos: Added batch transcoding via convert command
    • Metadata: Remove estimate when setting a new country
    • Metadata: Workaround for Exif strings containing newlines
    "},{"location":"release-notes/#february-11-2021","title":"February 11, 2021","text":"

    Build 210211-b9595dd4

    • Videos: Native player featuring performance and UX enhancements
    • Index: Improved detection of missing photos, files, and folders
    "},{"location":"release-notes/#february-8-2021","title":"February 8, 2021","text":"

    Build 210208-9e10ba69

    • Upload: Adds duplicates to selected albums as well
    • Library: Show folder covers in Originals
    • Metadata: Automatically remove orphan countries, cameras, and lenses
    • Metadata: Improved Exif parser
    • Backup: Restore archive flag from YAML files
    • Docker: Improved entrypoint script
    "},{"location":"release-notes/#january-28-2021","title":"January 28, 2021","text":"

    Build 210128-a82061e0

    • UX: Improved theme colors and icons
    • UX: Download all related media files using their current name by default
    • UX: Redirect already authenticated users from /login to /browse
    • Mobile: Prevent like on touch swipe
    • Translations: Updated German and French
    • Config: Reduced auto index & import safety delay defaults
    • Metadata: Improved photo titles, removed small words from title endings
    • Metadata: Improved date extraction from current and original file names
    • Metadata: Fallback to earliest file mod time in case there is no other date
    • Import: Index keywords from non-primary filenames as well
    • WebDAV: Improved service discovery
    • Purge: Hide missing files in edit dialog and set new primary if needed
    • Archive: Physically delete files after confirmation
    • Moments: Added delete button to context menu
    • Settings: Added Estimates and Delete feature flags
    • CLI: Added cleanup command to remove orphaned index entries and thumbnails
    "},{"location":"release-notes/#january-21-2021","title":"January 21, 2021","text":"

    Build 210121-07e559df

    • UX: Improved video playback and icons
    • UX: Restructured main navigation
    • Mobile: Show search field in albums
    "},{"location":"release-notes/#january-20-2021","title":"January 20, 2021","text":"

    Build 210120-e7cd5e9a

    • API: Apply limit, offset and sort order when searching for IDs
    • ARM64: Reverted database image back to arm64v8/mariadb in config example
    "},{"location":"release-notes/#january-19-2021","title":"January 19, 2021","text":"

    Build 210119-a5399f06

    • UX: Optimized user interface for iOS and tablets
    • UX: Improved theme colors
    • UX: Scroll position is restored when navigating back
    • Translations: Added Czech
    • Metadata: Estimate timezone and allow overwriting estimated locations
    • Settings: Fixed disabling logs

    For our sponsors and contributors:

    • UX: Added two dark themes
    "},{"location":"release-notes/#january-11-2021","title":"January 11, 2021","text":"

    Build 210111-cc05c430

    • UX: Disabled preloading in live photo player to reduce memory footprint
    • UX: Updated main navigation, find all media types via /browse
    • UX: Removed lag when selecting pictures
    • UX: Tweaked tile size breakpoints in Albums, Labels, and Search
    • UX: Added tooltips to navigation expand and minimize buttons
    • UX: Preload additional search results
    • UX: Removed image loading spinners for faster rendering
    • Thumbnails: Added cache control headers for improved performance
    • Album Covers: Cache will be flushed after updating private flags
    • Search: Improved performance of photos query
    • PWA: Added service worker so that app can be installed more easily
    • PWA: Enabled auto-rotate so that photos may be viewed in landscape mode
    • Frontend: Removed unused dependencies and reduced build size
    • Translations: Updated Russian, French, Simplified Chinese, and German
    • Index: Automatically create JPEGs for related media files as well
    • Import: Improved error handling when the file system becomes unavailable
    • Config: Updated docker-compose.yml examples
    • Config: Added optional gzip compression for built-in web server
    • Config: Limit number of indexing workers to half the number of physical cores by default to avoid high load on hyper-threading capable CPUs
    "},{"location":"release-notes/#january-4-2021","title":"January 4, 2021","text":"

    Build 210104-7f9e806a

    • Config: Added auto index & import defaults to Dockerfiles
    • Import: Extract metadata with ExifTool before moving
    • Import: Automatically create folder albums
    • Help: Updated WebSocket page
    • UX: Added UI.Zoom setting to re-enable page zoom
    • UI: Updated default theme
    • Translations: Added Hebrew & Japanese, updated Brazilian Portuguese
    • Albums & Cards View: Reduced tile size on large screens
    • WebDAV: Less verbose logging
    "},{"location":"release-notes/#january-2-2021","title":"January 2, 2021","text":"

    Build 210102-af71e5f7

    • WebDAV: Uploads and other changes trigger auto indexing / importing
    • Config: Use random hash for improved preview token security
    • UX: Disabled page zoom so that app feels more native on mobile devices
    • UX: Reduced min password length to 4 characters
    • UX: Improved docker-compose.yml examples
    • UX: Reduced icon size in \"add to album\" dialog
    "},{"location":"release-notes/#december-31-2020","title":"December 31, 2020","text":"

    Build 201231-8e22fbf8

    • Initial Stable Release
    "},{"location":"release-notes/#getting-updates","title":"Getting Updates","text":"

    Even when you use an image with the :latest tag, Docker does not automatically download new images for you. To update, you can either manually pull the newest image and restart, or set up a service like Watchtower to get automatic updates.

    "},{"location":"developer-guide/","title":"Every Contribution Makes a Difference","text":"

    We welcome contributions of any kind, including blog posts, tutorials, testing, writing documentation, and pull requests.

    "},{"location":"developer-guide/#our-milestones","title":"Our Milestones","text":""},{"location":"developer-guide/#upcoming-features-and-enhancements","title":"Upcoming Features and Enhancements","text":"

    The public roadmap shows what tasks are in progress, what needs testing, and which features are going to be implemented next.

    You are welcome to submit specific feature requests after you have verified that no similar issue already exists. Give ideas you like a thumbs-up \ud83d\udc4d , so that we know what is most popular.

    "},{"location":"developer-guide/#join-the-community","title":"Join the Community","text":"

    Follow us on Mastodon, Bluesky, or join the Community Chat to get regular updates, connect with other users, and discuss your ideas. Our Code of Conduct explains the \"dos and don\u2019ts\" when interacting with other community members.

    As a contributor, you are also welcome to contact us directly if you have something on your mind that you don't want to discuss publicly. Please note, however, that due to the high volume of emails we receive, our team may be unable to get back to you immediately. We do our best to respond within five business days or less.

    "},{"location":"developer-guide/#creating-bug-reports","title":"Creating Bug Reports","text":"

    Before reporting a bug, first use our Troubleshooting Checklists to determine the cause of your problem. If you have a general question, need help, it could be a configuration issue, or a misunderstanding in how the software works:

    • you are welcome to ask in our Community Chat
    • or post your question in GitHub Discussions

    In order for us to investigate new bug reports, they must include a complete list of steps to reproduce the problem, the software versions used and information about the environment in which the problem occurred, such as browser type, browser version, browser plug-ins, operating system, storage type, processor type, and memory size.

    A template for creating bug reports can be found at photoprism.app/kb/reporting-bugs. We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app.

    When browsing issues, please note that our team and all issue subscribers receive an email notification from GitHub whenever a new comment is added, so these should only be used for sharing important information and not for discussions, questions, or expressing personal opinions.Thank you very much!

    "},{"location":"developer-guide/#submitting-pull-requests","title":"Submitting Pull Requests","text":"

    Follow our step-by-step guide to learn how to submit new features, bug fixes, and documentation enhancements.

    Pull requests solving \"help wanted\" issues are the easiest to merge and the most helpful to us, as they allow us to spend more time on core functionality and other issues that are difficult for external contributors to work on. If you are new to this project, anything labeled \"easy\" may be a good first contribution.

    Be aware that reviewing, testing and finally merging pull requests requires significant resources on our side. It can therefore take several months if it is not just a small fix, especially if extensive testing is needed to prevent bugs from getting into our stable version.

    "},{"location":"developer-guide/#thank-you-to-all-current-and-past-sponsors","title":"Thank You to All Current and Past Sponsors","text":"

    A big thank you to all of our sponsors, whose generous support has been and continues to be essential to the success of the project!

    Our project infrastructure is provided by the following companies:

    • GitHub hosts our code repositories and also provides many other important services
    • Docker approved us for their Open Source Program and hosts all of our app images
    • Element develops and operates the infrastructure that our community chat is based on
    • BrowserStack provides free access to their device and browser testing infrastructure

    View Sponsors \u203a View Credits \u203a

    Privacy Notice

    We operate a number of web services that help us develop and maintain our software in collaboration with the open source community, for example translate.photoprism.app.

    Because many of these apps and tools were originally developed for internal use without a high level of privacy in mind, we ask that you do not enter personal information such as your real name or personal email address if you want it to remain private.

    Personal details may otherwise show up in logs, source code, translation files, commit messages, and pull request comments.

    "},{"location":"developer-guide/code-quality/","title":"Quality and Best Practices","text":"

    The quality of code has a practical impact on both your agility and the cost of development:1

    • you can't change buggy and/or bloated code fast enough to be truly agile
    • existing bugs can easily increase development costs (and time) by 10x
    • the mess eventually becomes so big and so deep that you cannot clean it up anymore2

    Productivity vs Time

    All developers with more than a few years experience know that previous messes slow them down. And yet all developers feel the pressure to make messes in order to meet deadlines. In short, they don\u2019t take the time to go fast! You will not make the deadline by making a mess. Indeed, the mess will slow you down instantly, and will force you to miss the deadline. \u2014 Robert C. Martin

    "},{"location":"developer-guide/code-quality/#bottom-up-development","title":"Bottom-Up Development","text":"

    Breaking a problem down into small, coherent fragments lends itself to organization. Start with the basic low-level components and then proceed to higher-level abstractions.

    Bottom-up development emphasizes coding and early testing, which can begin before having a detailed understanding of the final system. In practice, this may never be the case as requirements are constantly evolving.

    Advantages of the bottom-up approach are component reusability, agility, and testability.

    I compared Mel's hand-optimized programs with the same code massaged by the optimizing assembler program, and Mel's always ran faster. That was because the \u201ctop-down\u201d method of program design hadn't been invented yet, and Mel wouldn't have used it anyway. He wrote the innermost parts of his program loops first. \u2014 The Story of Mel

    "},{"location":"developer-guide/code-quality/#opportunistic-refactoring","title":"Opportunistic Refactoring","text":"

    We encourage developers to refactor existing code when they notice a specific issue, even though this may seem difficult when working with a distributed team, branches, and pull requests due to potential merge conflicts and delayed feedback.

    It is best to do this while you are working on the same component anyway, for example to implement a feature or enhancement. This way you can easily validate if the proposed changes make sense and you avoid conflicts with others.

    Releasing imperfect code is not a problem as long as it is accompanied by automated tests. This makes it easy to refactor later without breaking anything or requiring detailed knowledge of the requirements and a lot of time for manual testing. Be pragmatic. Done is better than perfect.

    Potential security issues are an important exception. These should never be ignored. If you find a problem, please report it to us immediately so we can fix it.

    Feel free to think ahead, just don't code ahead. But also, don't feel the need to decide so many details ahead. Learn enough to get started and build only what you need. \u2014 J. B. Rainsberger

    "},{"location":"developer-guide/code-quality/#premature-optimization","title":"Premature Optimization","text":"

    One of the hardest parts of software development is knowing what to work on. Don't get carried away implementing unnecessary abstractions and focusing on scalability optimization before you've even validated the functionality of a feature or component.

    Instead of spending a lot of time on something you may not need, focus on user needs and test automation. That way, you'll make sure you're developing the right functionality, and you can refactor it later for scalability and other non-functional aspects without breaking anything.

    Also keep in mind that it's much easier and less effort to maintain small amounts of duplicate code than to choose the wrong abstraction.

    Premature optimization is the root of all evil. \u2014 Donald Knuth

    "},{"location":"developer-guide/code-quality/#be-careful-with-caching","title":"Be Careful with Caching","text":"

    There are two hard things in computer science: cache invalidation and naming things.

    A cache is just a memory leak you haven't met yet. \u2014 Dave Cheney

    "},{"location":"developer-guide/code-quality/#go-slow-before-you-go-fast","title":"Go Slow Before You Go Fast \ud83d\udc30","text":"

    Read the docs, understand the context, and talk to others to gather missing information before you start coding. Write tests. Stay focused.

    Don't worry that this will take too long. Take your time. It's the fastest and only sustainable way to get things done. You have to go slow before you can go fast.

    Simple, elegant solutions are more effective, but they are harder to find than complex ones, and they require more time, which we too often believe to be unaffordable. \u2014 Niklaus Wirth, Communications of the ACM, 1985

    "},{"location":"developer-guide/code-quality/#effectiveness-efficiency","title":"Effectiveness > Efficiency","text":"

    Optimize for effectiveness before efficiency when prioritizing tasks:

    • Effectiveness is about achieving a specific outcome, such as providing the features that best help users solve their problems.
    • Efficiency means doing things in an optimal way, for example, faster and cheaper. We all strive to be efficient, but that's worthless if it doesn't contribute to effectiveness.

    In contrast, a feature factory focuses on the quantity of new features rather than their quality:

    It is fundamentally the confusion between effectiveness and efficiency that stands between doing the right things and doing things right. There is surely nothing quite so useless as doing with great efficiency what should not be done at all. \u2014 Peter Drucker

    "},{"location":"developer-guide/code-quality/#test-automation-guidelines","title":"Test Automation Guidelines","text":"

    We strive for complete test coverage as it is a useful tool for finding untested parts of our code base. Test coverage is of limited use as a numerical statement of how good our tests are.

    The F.I.R.S.T. Principle includes five rules that good tests should follow:2

    • Fast. If tests are slow, you won't run them frequently, which makes them much less useful and increases the cost of development.
    • Independent. You should be able to run each test independently and run the tests in any order you like. When tests depend on each other, then the first one to fail causes a cascade of downstream failures, making diagnosis difficult and hiding downstream defects.
    • Repeatable. If your tests aren\u2019t repeatable in any environment, then you\u2019ll always have an excuse for why they fail. You\u2019ll also find yourself unable to run the tests when the environment isn\u2019t available.
    • Self-Validating. You should not have to read through a log file to tell whether the tests pass. If the tests aren\u2019t self-validating, then failure can become subjective and running the tests can require a long manual evaluation.
    • Timely. If you write the tests after the production code, you will generally find that the production code is difficult to test. Instead, add tests at implementation time to ensure that the code is testable, does what you expect it to do, and meets the requirements.

    Code that cannot be tested is flawed. \u2014 Anonymous

    "},{"location":"developer-guide/code-quality/#code-quality-reports","title":"Code Quality Reports","text":"

    goreportcard.com generates reports on the quality of Open Source Go projects. It uses several measures, including gofmt, go vet, go lint and gocyclo. If you find this helpful and also use the tool for your own projects, you can support the developers on Patreon.

    Take inspiration from quality reports, but keep in mind that not every reported issue must be fixed immediately.

    "},{"location":"developer-guide/code-quality/#security-best-practices","title":"Security Best Practices","text":"

    The Open Source Security Foundation (OpenSSF) maintains standardized security criteria and best practices for open-source projects:

    View Security Testing Guide \u203a

    1. Allen Holub, twitter.com/allenholub/status/1073738216140791808, 2018\u00a0\u21a9

    2. Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship, 2009\u00a0\u21a9\u21a9

    "},{"location":"developer-guide/configuration/","title":"Configuration","text":""},{"location":"developer-guide/configuration/#command-line-interface","title":"Command-Line Interface","text":"

    The photoprism help (or photoprism --help) command lists all supported subcommands and config options available in the current version:

    ./photoprism help\n

    Run the following to display all global config options and their current values:

    ./photoprism show config\n

    You can also use the photoprism show command to display supported search filters, file types, thumbnail sizes, and metadata tags:

    ./photoprism show --help\n
    "},{"location":"developer-guide/configuration/#config-options","title":"Config Options","text":"

    You can either use environment variables like PHOTOPRISM_CACHE_PATH, config files (in the storage/config directory) or command line parameters like --cache-path to change the value of config options.

    Learn more \u203a

    Changes to the config options always require a restart to take effect. For development purposes, you do not need to change any settings if you followed the steps described under Build Setup.

    "},{"location":"developer-guide/directories/","title":"Project Directory Structure","text":"

    The directory layout that we use for our public project repository is loosely based on golang-standards/project-layout:

    • / contains a Makefile, a readme, the license and various config files for dependency management, building and continuous integration
    • /assets contains subdirectories for various assets such as photos, built JS/HTML/CSS files and server-side HTML templates
    • /cmd contains the application source code (main package)
    • /docker contains Dockerfiles and related assets, e.g. for Development, TensorFlow and ARM64
    • /frontend contains our Web frontend JS/HTML/CSS source code
    • /internal contains the source code of our internal Go packages
    • /pkg contains the source code of our public Go packages
    • /scripts contains shell scripts used for building, deployment and continuous integration
    "},{"location":"developer-guide/documentation/","title":"Documentation","text":""},{"location":"developer-guide/documentation/#user-facing-documentation","title":"User Facing Documentation","text":"

    User-facing documentation lives in the photoprism-docs repository and can be browsed on docs.photoprism.app.

    See the README for how to easily edit the docs yourself.

    "},{"location":"developer-guide/documentation/#package-documentation-godoc","title":"Package Documentation (GoDoc)","text":"

    Godoc generates documentation automatically, so it's tightly coupled to the photoprism source code.

    View Package Docs

    Some highlights to explore:

    • cmd/photoprism - main application
    • internal/photoprism - main library package
    • internal/server - server initialization and routing
    • internal/api - server api
    • internal/commands - command line interface
    • internal/form - input validation (based on gin)
    • internal/entity - models (based on GORM)
    "},{"location":"developer-guide/faq/","title":"Frequently Asked Questions","text":""},{"location":"developer-guide/faq/#can-your-development-environment-be-used-under-windows","title":"Can your development environment be used under Windows?","text":"

    Yes, this is possible if you have Git and Docker Desktop installed. However, you are likely to experience problems when using our Makefile and other scripts directly on Windows, as they were developed and tested on Linux/Unix.

    We therefore recommend not to use Make for setting up your development environment and to instead run the required commands manually from the main project directory (where the compose.yaml file is located):

    docker compose --profile=all pull --ignore-pull-failures\ndocker compose build\ndocker compose up\n

    Once the services have been built and started as shown above, you can open a terminal session:

    docker compose exec photoprism bash\n

    In addition, you should disable the \"autocrlf\" option in Git under Windows to avoid problems with Linux/Unix line endings:

    git config --global core.autocrlf false\n

    That's it! Most of the other Make targets are used from within the terminal session, i.e. not under Windows, so no compatibility issues or missing dependencies are to be expected.

    Learn more \u203a

    "},{"location":"developer-guide/faq/#why-not-use-a-more-permissive-public-license-so-that-for-example-developers-at-google-can-contribute-more-easily","title":"Why not use a more permissive public license so that, for example, developers at Google can contribute more easily?","text":"

    Since we had hoped for a collaboration with Google and were aware of their AGPL policy, PhotoPrism was initially licensed under Apache 2.0, which is much more permissive. However, no one seemed interested, although we did talk to quite a few personal acquaintances at Google. That made it easy for our community to convince us to use AGPL instead.

    "},{"location":"developer-guide/faq/#isnt-it-insecure-that-thumbnail-urls-work-even-if-you-are-not-logged-in","title":"Isn't it insecure that thumbnail URLs work even if you are not logged in?","text":"

    Like most commercial image hosting services, we've chosen to use a cookie-free thumbnail API to minimize request latency and avoid unnecessary network traffic. If you were to copy private session cookies and use them in a different browser window, you would have a similar problem, except that they also work for other API endpoints, not just a single image.

    Even if URLs were to become invalid every minute: Digital copies are as good as originals. Once shared and downloaded, such images should be considered \"leaked\" because they are cached and can be re-shared by the recipient at any time, with no sure way to get all copies back. Any form of protection we could provide would essentially be \"snake oil\", could be circumvented, and would have a negative impact on the user experience, such as disabling the browser cache or context menu.

    For the highest level of protection, it is recommended to shield your private server from the public Internet. Always use HTTPS, a VPN and/or ideally TLS client certificates and make sure that only people you trust have access to your instance.

    Visit docs.photoprism.app/developer-guide/media/thumbnails/ to learn more.

    "},{"location":"developer-guide/faq/#does-your-software-depend-on-any-external-services","title":"Does your software depend on any external services?","text":"

    As explained in our Privacy Policy, reverse geocoding and interactive world maps depend on retrieving the necessary information from us and MapTiler AG, headquartered in Switzerland. Both services are provided with a very high level of privacy and confidentiality.

    Your use of these services is fully covered by us. Depending on your usage, this can save you much more than the cost of a PhotoPrism+ Membership, since other providers generally charge usage-based fees and often don't allow you to cache the data they provide, compromising performance and your privacy with unnecessary requests.

    In order to use the maps and view location details in PhotoPrism, as well as successfully run our test suite as a developer, you must allow requests to these API endpoints if you have a firewall installed and make sure your Internet connection is working.

    Should you wish to operate one or both of these services on your own premises, we can set up such a fully autonomous solution for you, provided you are prepared to cover the initial setup costs as well as ongoing maintenance fees for content licenses and updates.

    View Privacy Policy \u203a View Compliance FAQ \u203a

    Other applications sometimes use the API that OpenStreetMap provides for development and testing. In this case, their usage and privacy policies apply, which means that your request data is stored and used to generate publicly available reports. This differs from our self-hosted service, which ensures your privacy and provides a better user experience with faster loading times.

    "},{"location":"developer-guide/faq/#why-do-i-see-connection-errors-when-requesting-api-keys-at-startup","title":"Why do I see connection errors when requesting API keys at startup?","text":"

    Retrieving location data with reverse geocoding and loading the interactive world maps we provide requires a connection to external services. Please make sure that requests to these API endpoints are allowed if you have a firewall installed, and that your internet connection is working.

    Learn more \u203a

    "},{"location":"developer-guide/faq/#are-the-keys-for-using-interactive-world-maps-provided-free-of-charge","title":"Are the keys for using interactive world maps provided free of charge?","text":"

    All users have access to a high-resolution vector map that we host on our own infrastructure, so no commercial API key is required. It is based on data published by OpenStreetMap (OSM).

    In addition, we automatically provide our members and business customers with an API key for MapTiler's commercial service, which includes satellite, outdoor and 3D maps. You can test these on our public demo.

    Learn more \u203a

    Although experienced users could alternatively register test accounts with a commercial provider to gain access to additional map styles instead of signing up for a membership, we believe this would not be fair. Keep in mind that we have a much larger user base than others who might encourage their users to do so, and that providers might then stop offering free test accounts, which is something we don't want to be responsible for.

    "},{"location":"developer-guide/faq/#why-dont-you-use-the-free-map-tile-service-provided-by-openstreetmap","title":"Why don't you use the free map tile service provided by OpenStreetMap?","text":"

    Other free and open-source software sometimes uses the public maps that OpenStreetMap provides for development and testing. These are not intended for end-user applications like ours.

    Using their service also means that their usage and privacy policies apply, as your request data is stored and used to generate publicly available reports. This differs from our services, which ensure a high level of privacy and provide a better user experience with faster loading times.

    "},{"location":"developer-guide/issues/","title":"GitHub Issues","text":"

    We use GitHub issues for managing bugs, ideas, and enhancements. Issues labeled help wanted / easy can be good (first) contributions. You are welcome to email us if you want to work on something specific, need help with a pull request, or have something on your mind that you don't want to discuss publicly.

    When browsing issues, please note that our team and all issue subscribers receive an email notification from GitHub whenever a new comment is added, so these should only be used for sharing important information and not for discussions, questions, or expressing personal opinions.

    "},{"location":"developer-guide/issues/#writing-user-stories","title":"Writing User Stories","text":"

    Start describing new ideas and enhancements with a user story similar to this one:

    As a [type of person] I'd like to be able to [do something] so that I can [get some result].

    This makes it easier for everyone to understand who wants what and why.

    Reading between the lines consumes a lot of time that can be used more effectively. Vague requirements become even more expensive when the wrong things are implemented, or they don't provide value in the way they were implemented.

    "},{"location":"developer-guide/issues/#creating-bug-reports","title":"Creating Bug Reports","text":"

    In order for us to investigate new bug reports, they must include a complete list of steps to reproduce the problem, the software versions used and information about the environment in which the problem occurred, such as browser type, browser version, browser plug-ins, operating system, storage type, processor type, and memory size:

    https://www.photoprism.app/kb/reporting-bugs

    We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app. Before reporting a bug, first use our Troubleshooting Checklists to determine the cause of your problem. Contact us or a community member if you need help, it could be a configuration problem, or a misunderstanding in how the software works.

    This gives us the opportunity to improve our documentation and provide best-in-class support instead of dealing with unclear/duplicate bug reports or triggering a flood of notifications by replying to comments. Thank you very much!

    "},{"location":"developer-guide/issues/#acceptance-criteria","title":"Acceptance Criteria","text":"

    Issues should include a list of Acceptance Criteria that clearly state what is expected. We recommend using MAY, SHOULD, and MUST as keywords to indicate priorities.

    Clickable checkboxes for each item can be created via GitHub Markdown:

    Acceptance Criteria:\n\n- [ ] \"Log In\" button MUST be visible on /login page\n- [ ] \"Log In\" button MAY be disabled if password field is empty\n- [ ] Page SHOULD use existing Vuetify components\n- [ ] Login MUST work in latest Firefox, Safari and Chrome\n
    "},{"location":"developer-guide/pull-requests/","title":"Pull Requests","text":""},{"location":"developer-guide/pull-requests/#acceptance-criteria","title":"Acceptance Criteria","text":"

    Because we want to create the best possible product for our users, we have a set of criteria to ensure that all submissions are acceptable:

    • Features and enhancements must be fully implemented so that they can be released at any time without additional work
    • Automated unit and/or acceptance tests are mandatory to ensure the changes work as expected and to reduce repetitive manual work
    • Frontend components must be responsive to work and look properly on phones, tablets, and desktop computers; you must have tested them on all major browsers and different devices
    • Documentation and translation updates should be provided if needed
    • In case you submit database-related changes, they must be tested and compatible with SQLite 3 and MariaDB 10.5.12+

    These guidelines are not intended as a filter or barrier to participation. If you are unfamiliar with Open Source development, we will help you.

    "},{"location":"developer-guide/pull-requests/#contributor-license-agreement","title":"Contributor License Agreement","text":"

    After you submit your first pull request, you will be asked to accept our Contributor License Agreement (CLA). Visit photoprism.app/cla to learn more.

    "},{"location":"developer-guide/pull-requests/#how-to-create-and-submit-a-pull-request","title":"How to Create and Submit a Pull Request","text":""},{"location":"developer-guide/pull-requests/#fork-our-repository","title":"Fork our repository","text":"
    • Click the Fork button in the header of our main repository
    • Clone the forked repository on your local computer:
      • git clone https://github.com/[your username]/photoprism
    • Connect your local to our \"upstream\" main repository by adding it as a remote:
      • git remote add upstream https://github.com/photoprism/photoprism.git
    • Create a new branch from develop - it should have a short and descriptive name (not \"patch-1\") that does not already exist, for example:
      • git checkout -b feature/your_feature_name
    • See also https://guides.github.com/activities/forking/
    "},{"location":"developer-guide/pull-requests/#make-your-changes","title":"Make your changes","text":"
    • While you are working on it and your pull request is not merged yet, pull in changes from \"upstream\" often so that you stay up to date and there is a lower risk for merge conflicts:
      • git fetch upstream
      • git merge upstream/develop
    • We recommend running tests after each change to make sure you didn't break anything:
      • make test
    • Add tests for any new code
      • If you have questions about how to do this, please ask in your pull request
    • Run make fmt to ensure code is properly formatted according to our standards
    • If all tests are green and you see no other errors, commit your changes. To reference related GitHub issues, please end your commit message with the issue ID like #1234:
      • git status -s
      • git add .
      • git commit -m \"Your commit message #1234\"
    "},{"location":"developer-guide/pull-requests/#when-you-are-ready","title":"When you are ready...","text":"
    • Verify you didn't forget to add / commit files, output of git status -s should be empty
    • Push all commits to your forked remote repository on GitHub:git push -u origin feature/your_feature_name
    • Create a pull request with a helpful description of what it does
    • Wait for us to perform a code review and fix the remaining issues, if any
    • Update and/or add documentation if needed
    • Sign the Contributor License Agreement (CLA)

    You can also create a pull request if your changes are not yet complete or working. Just let us know it's in progress, so we don't try to merge them. We can help you with a code review or other feedback if needed. Please be patient with us.

    Be aware that reviewing, testing and finally merging pull requests requires significant resources on our side. It can therefore take several months if it is not just a small fix, especially if extensive testing is needed to prevent bugs from getting into our stable version.

    Privacy Notice

    We operate a number of web services that help us develop and maintain our software in collaboration with the open source community, for example translate.photoprism.app.

    Because many of these apps and tools were originally developed for internal use without a high level of privacy in mind, we ask that you do not enter personal information such as your real name or personal email address if you want it to remain private.

    Personal details may otherwise show up in logs, source code, translation files, commit messages, and pull request comments.

    "},{"location":"developer-guide/setup/","title":"Setting Up Your Development Environment","text":""},{"location":"developer-guide/setup/#step-1-use-git-to-clone-the-project-from-github","title":"Step 1: Use Git to Clone the Project from GitHub","text":"

    Before running any commands, please make sure you have Git, Make, Docker, and Docker Compose installed on your system. These are available for Mac, Linux, and Windows.1

    In case you are using Ubuntu Linux, you can run this script to install the latest Docker version including the Compose Plugin on your computer in one step:

    bash <(curl -s https://setup.photoprism.app/ubuntu/install-docker.sh)\n

    When working on Microsoft Windows or Apple macOS, you need to install the latest version of Docker Desktop and also disable \"autocrlf\" in Git to avoid errors2:

    git config --global core.autocrlf false\n

    Now change to the directory where you usually keep your development projects, and run this command to download the project from GitHub:

    git clone git@github.com:photoprism/photoprism.git\n

    Once all code has been downloaded, change to the project directory which should now exist:

    cd photoprism\n

    Developing on Windows

    Our standard development environment can also be used on Windows if you have Git, Docker Desktop, and a suitable IDE like GoLand installed. However, we recommend running the required docker compose commands manually instead of using make docker-build and make terminal. Learn more \u203a

    "},{"location":"developer-guide/setup/#step-2-launch-your-local-development-environment","title":"Step 2: Launch Your Local Development Environment","text":"

    Pull the latest Docker images and then launch the pre-configured build environment we provide to have an isolated development container pre-installed with all the tools you might need, including the latest versions of Go, NodeJS, and NPM:3

    make docker-build\ndocker compose up\n

    This environment is for testing and development purposes only. Do not use it in production. Also note that our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    "},{"location":"developer-guide/setup/#step-3-install-the-dependencies-and-start-developing","title":"Step 3: Install the Dependencies and Start Developing","text":"

    Open a terminal to run commands directly in your local development environment:

    make terminal\n

    Before starting to build, make sure all dependencies, such as NPM packages and TensorFlow models, are installed:

    make dep\n

    Congratulations! You can now build the frontend assets (JS), compile the backend binary (Go) and then run a custom PhotoPrism version in your local environment:

    make build-js\nmake build-go\n./photoprism start\n
    After PhotoPrism has been started as shown above, the user interface can be opened in a web browser by navigating to one of these URLs:

    • http://localhost:2342/ (HTTP)
    • https://app.localssl.dev/ (HTTPS)

    In the build environment, the default login is admin with the password photoprism. You can disable it with the --public command flag:

    ./photoprism --public start\n

    You can find the default settings in the compose.yaml or docker-compose.yml file located in the root of the project. Keep them when you run tests. Otherwise, the tests may fail for others, even if they succeed in your local environment.

    You can find a list of all make targets in the Makefile. For example, make test will run frontend and backend unit tests. Wrong filesystem permissions can be fixed by running make fix-permissions in a terminal.

    "},{"location":"developer-guide/setup/#optional-build-the-frontend-in-watch-mode","title":"Optional: Build the Frontend in Watch Mode","text":"

    The integrated web server not only provides the backend API, but is also used to serve static assets. These can be automatically rebuilt (updated) when you change a file. To do this, run the following command in a terminal, either inside or outside the container (outside is faster if your host is not running Linux):

    make watch-js\n

    Alternatively, you can change to the frontend directory and run NPM directly:

    cd frontend\nnpm run watch\n

    To update the frontend dependencies, also change to the frontend directory and run:

    npm update\n
    "},{"location":"developer-guide/setup/#optional-use-a-go-debugger","title":"Optional: Use a Go Debugger","text":"

    To debug the backend Go code, first make sure you have built and run the containers as described above:

    make docker-build\ndocker compose up\n

    Note: If you make changes in the Dockerfile to test things out, you can build and run the photoprism container individually:

    docker compose build photoprism\ndocker compose up photoprism\n

    Then open a terminal to the container as described above:

    make terminal\n

    and if you made code changes, make sure the rebuild the Go code:

    make build-go\n

    You can then run the Delve command from inside the container to start the debugger - the command-line options can be customized based on your needs:

    dlv --listen=:40000 --headless=true --log=true --log-output=debugger,debuglineerr,gdbwire,lldbout,rpc --accept-multiclient --api-version=2 exec ./photoprism -- start\n

    Once you run this command, you can use VSCode or Goland to add breakpoints and step through breakpoints in the code. Follow the instructions here to set up VSCode, and here to set up Goland to connect to the debugger.

    Once the debugger is running, you can view the app at http://localhost:2342/ and debug the code.

    Questions?

    • Radomir Sohlich wrote a pragmatic introduction to Makefiles for Go developers
    • we are using Go Modules for managing our dependencies (new in 1.11)
    • this guide was not tested on Windows, you might need to edit your compose.yaml or docker-compose.yml to make it work with Windows specific paths
    "},{"location":"developer-guide/setup/#apple-silicon-raspberry-pi-and-arm64","title":"Apple Silicon, Raspberry Pi, and ARM64","text":"

    Our development environment has been built into a single multi-arch image for 64-bit AMD, Intel, and ARM processors. That means, Apple Silicon, Raspberry Pi 3 / 4, and other ARM64-based devices can pull from the same repository.

    "},{"location":"developer-guide/setup/#multi-arch-docker-builds","title":"Multi-Arch Docker Builds","text":"

    For information about multi-architecture Docker builds, see the following documentation:

    • Docker Buildx
    • Leverage multi-CPU architecture support
    "},{"location":"developer-guide/setup/#qemu-quick-start","title":"QEMU Quick Start","text":"
    1. install qemu-user-static from docker hub: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes multiarch/qemu-user-static
    2. verify that dockers buildx command is installed docker buildx version. if missing, follow install instructions here
    3. create buildx builder: docker buildx create --name multiarch-builder && docker buildx inspect --builder multiarch-builder --bootstrap
    4. start building: make docker-development-multiarch or make docker-photoprism-multiarch
    1. Instead of using Docker, you can also set up your own build environment based on the steps documented in the Dockerfiles we provide. For this, you should have at least Go 1.22, TensorFlow for C, Make, NPM 10 and MariaDB 11 installed. Note that the test results will be unreliable without Docker. This method is therefore not well suited for contributors and we cannot provide support if something does not work as expected.\u00a0\u21a9

    2. If the Git config value \"core.autocrlf\" is set to \"true\", the following error may occur when trying to run shell scripts or Make targets: env: bash\\r: No such file or directory \u21a9

    3. Docker uses human-readable Dockerfiles that contain all the commands a user would invoke in a terminal to assemble a complete application image.\u00a0\u21a9

    "},{"location":"developer-guide/tests/","title":"Running Unit and Acceptance Tests","text":"

    In any process, obsessing about the wait times will yield greater enhancements than practically anything else, for longer than you might think. Automation, simplification, etc. are implementation details of that obsession. \u2014 Dan North

    "},{"location":"developer-guide/tests/#unit-tests","title":"Unit Tests","text":""},{"location":"developer-guide/tests/#go","title":"Go","text":"

    To run all unit tests, type make test or go test ./internal/... in a terminal.

    These make targets are currently defined for tests:

    • test: Executes all tests found in a) /internal with a timeout of 20 min and verbose output and b) frontend/tests/unit
    • test-short: Executes only fast tests found in /internal with a timeout of 5 min and verbose output
    • test-race: Same as test but with race condition detector (much slower) and higher timeout of 60 min
    • test-codecov: Same as test but creates a coverage log in coverage.txt and sends it to Codecov (don't use it locally)
    • test-coverage: Same as test but creates a coverage.txt file as well as a human-readable report in coverage.html; timeout is elevated to 30 min

    You can run single tests via go test -run in a package directory, e.g. /internal/photoprism:

    $ go test -run NameOfTest\n

    See docs for more info.

    "},{"location":"developer-guide/tests/#test-frameworks","title":"Test Frameworks","text":"

    Go comes with a cool testing framework, it allows you to write test code using the same language, without needing to learn any library or test engine. Go advanced testing tips & tricks contains a lot of useful information. We only import testify/assert to save a few lines for common assertions.

    Todo: Use a SQL mock driver to test database interactions, for example DATA-DOG/go-sqlmock.

    "},{"location":"developer-guide/tests/#slow-tests","title":"Slow Tests","text":"

    Slow tests and benchmarks can be skipped using the -short flag:

    func TestTimeConsuming(t *testing.T) {\n    if testing.Short() {\n        t.Skip(\"skipping test in short mode.\")\n    }\n    ...\n}\n

    To execute:

    go test -short\n
    "},{"location":"developer-guide/tests/#javascript","title":"Javascript","text":"

    To run all javascript unit tests, type make test-js in a terminal.

    In case you want to run a single test add .only to the test you want to run e.g.:

     it.only(\"should get album id\",  () => {\n        const values = {\n            ID: 5, AlbumName: \"Christmas 2019\", \n            AlbumSlug: \"christmas-2019\", AlbumUUID: 66\n        };\n        const album = new Album(values);\n        const result = album.getId();\n        assert.equal(result, \"66\");\n    });\n

    Test coverage output is saved to frontend/coverage/html

    "},{"location":"developer-guide/tests/#test-frameworks_1","title":"Test Frameworks","text":"

    To test javascript code we use mocha in combination with karma, chai, sinon and the karma-istanbul-coverage-reporter.

    "},{"location":"developer-guide/tests/#acceptance-tests","title":"Acceptance Tests","text":"

    Before you proceed run

    make dep-acceptance\n
    "},{"location":"developer-guide/tests/#running-tests-inside-the-docker-container","title":"Running Tests Inside the Docker Container","text":"

    In the development container environment, you can run the tests in headless chrome:

    make acceptance-run-chromium\n
    "},{"location":"developer-guide/tests/#test-frameworks_2","title":"Test Frameworks","text":"

    Our goal was to implement UI acceptance tests using JavaScript, so that frontend developers are able to run and write them without learning Go.

    To make a final decision, we compared TestCafe, Cypress and Nightwatch.js. We agreed on using TestCafe as tests were the most stable and pretty fast (because no long timeouts are needed).

    Feature TestCafe Cypress Nightwatch.js Supported Browsers Local Chrome, Firefox, Opera, Safari, Internet Explorer, Microsoft Edge, Google Chrome Canary, Chromium Chrome, Chromium, Chrome Canary, Electron Geckodriver, Chromedriver, Microsoft Webdriver, Safaridriver Supported Browsers Headless Chrome, Firefox Electron Problems running headless Continuous Integration Support yes yes yes Setup easy via npm easy via npm easy via npm Usability +++ +++ ++ Speed (3 tests) 2 min (headless chrome and firefox) 1 min (only chrome headless) 1 min (chrome headed) 5 min (headless electron) 2,5 min (chrome headed) 7 min (chrome headed) headless not working Stability nice unstable --> waiting times needed unstable --> waiting times needed Documentation +++ ++ ++ Notes easy to find elements easy to find elements additional library needed to find selectors by text

    Other test libraries and frameworks we currently don't use:

    • https://codecept.io/
    • https://funcunit.com/
    • https://www.browserstack.com/
    • http://nightwatchjs.org/ (used by Mozilla's Coral Project)
    • https://developers.google.com/web/updates/2017/04/headless-chrome
    "},{"location":"developer-guide/translations-weblate/","title":"Contributing Translations with Weblate","text":"

    We operate translate.photoprism.app so that you can easily add and update user interface translations without having any development skills.

    You can either sign in with your GitHub account or create a new account with an email account you have access to:

    • Provide an alternate address if you want your personal email to remain private as it may appear as authorship in commits, translation files, and pull requests.
    • You are also not required to provide your full name if you do not wish to do so.
    • Let us know privately that you have registered so that we can grant you permissions if needed.

    When you sign up, you will be asked to accept our Contributor License Agreement (CLA). Visit photoprism.app/cla to learn more.

    Sign upAgree to CLAAdd missing translationsReview translationsAdd new language
    1. Navigate to https://translate.photoprism.app/

    2. Click Register and create an account

    1. Sign in
    2. Select the language you want translate to

    3. Open your Dashboard and click Backend (or access to the Backend by clicking here)

    4. Click on View contributor agreement

    5. Accept the contributor agreement and click Submit

    6. Go back to your Dashboard and repeat steps 3-5 for the Frontend-Component

    Your Dashboard shows you how many strings have no translation yet.

    1. Click on the count
    2. Enter the translation and click Save and continue

    3. Repeat until all strings are translated

    Your translations will be reviewed and included in one of the next releases.

    If translations are missing, we pre-translate messages using services such as DeepL and Google Translate. This can lead to grammatical errors and misunderstandings. Native speakers should check existing translations and improve them if necessary.

    If you are a native speaker and would like to support us by reviewing existing translations, please contact us at hello@photoprism.app so that we can grant you the rights to review.

    1. From your Dashboard open one of the components by clicking on the language

    2. Click on Strings waiting for review

    3. Review the translation - edit the string if needed. Then select Approved and click Save and continue

    4. Repeat until all strings are reviewed

    Please contact us at hello@photoprism.app and we will set up a new language for you to translate!

    "},{"location":"developer-guide/translations/","title":"Translations","text":"

    Please don't use the method described here to add/edit translations anymore use this instead!

    PhotoPrism uses gettext for frontend and backend localization. It is one of the most widely used standards for user interface translation:

    • Human-readable messages such as File not found are used as identifiers to search for appropriate translations. They also serve as default values when no translation is available.
    • Messages can optionally contain placeholders for numbers and other variables, for example Found %{n} files.

    We recommend Poedit for creating and updating translations. The download is free for Mac, Windows and Linux. The source code can be obtained on GitHub.

    If translations are missing, we pre-translate messages using services such as DeepL and Google Translate. This can lead to grammatical errors and misunderstandings. Native speakers should check existing translations and improve them if necessary. You can use the version compare feature on GitHub to detect changes in translation files.

    "},{"location":"developer-guide/translations/#frontend","title":"Frontend","text":"

    Localizations can be found in /frontend/src/locales. The POT file, only containing message ids, is translations.pot.

    *.po files contain localized messages for each language identified by their locale, for example de.po for German and pt_BR.po for Brazilian Portuguese. You can open, edit and save them with Poedit to update existing translations.

    "},{"location":"developer-guide/translations/#add-new-translation","title":"Add new translation","text":"
    • In /frontend run npm run gettext-extract
    • Install a translation tool e.g. Poedit
    • Open the /frontend/src/locales/translations.pot file with Poedit
    • In Poedit click on \"Create New Translation\" at the bottom and select the language
    • Now you can start translating
    • When done, save your translation as *.po file using the language locale (e.g. de.po) as name
    • Add the new language to the Languages function in /frontend/src/options/options.js
    • Run npm run gettext-compile to compile existing translations into a single translations.json file
    • To test your translations you need to build the frontend again using npm run build or npm run watch
    "},{"location":"developer-guide/translations/#update-existing-translation","title":"Update existing translation","text":"
    • In /frontend run npm run gettext-extract
    • Install a translation tool e.g. Poedit
    • Open the *.po file of your language e.g. /frontend/src/locales/fr.po file with Poedit
    • In the Poedit menu click \"Catalogue\" --> \"Update from POT File\" --> select the translations.pot file from /frontend/src/locales
    • Now you can start proofreading and adding the missing translations
    • Once your done save the changes in the *.po file
    • Run npm run gettext-compile to compile existing translations into a single translations.json file
    • To test your translations you need to build the frontend again using npm run build or npm run watch

    A binary *.mo (machine object) file will be automatically saved along with every *.po file. You won't be able to open those in a text editor, but please include them in git commits or when sending translations via email. The compiled translations.json file is not required for pull requests and often causes merge conflicts.

    "},{"location":"developer-guide/translations/#backend","title":"Backend","text":"

    Only asynchronous notifications and certain API responses need translation to provide a consistent user experience. Technical log messages should be in English to avoid ambiguities and (even slightly) wrong translations.

    Localizations are kept in /assets/locales. The POT file, only containing message ids, is messages.pot.

    default.po files in sub directories contain localized messages for each language identified by their locale, for example de/default.po for German and pt_BR/default.po for Brazilian Portuguese. You can open, edit and save them with Poedit. Please also add and commit binary *.mo files, which will be automatically created by Poedit.

    "},{"location":"developer-guide/translations/#add-new-translation_1","title":"Add new translation","text":"
    • Run make generate to update /assets/locales/messages.pot
    • Open the /assets/locales/messages.pot file with Poedit
    • In Poedit click on \"Create New Translation\" at the bottom and select the language
    • Now you can start translating
    • When done, create a new directory (using the locale as name) and save your translation there as default.po
    "},{"location":"developer-guide/translations/#update-existing-translation_1","title":"Update existing translation","text":"
    • Run make generate to update /assets/locales/messages.pot
    • Open the /assets/locales/fr/default.po file with Poedit
    • In the Poedit menu click \"Catalogue\" --> \"Update from POT File\" --> select the messages.pot file from /assets/locales/
    • Now you can start proofreading and adding the missing translations
    • Once you're done, save the changes in the default.po file

    This will only work when you have gettext installed on your system. We recommend using our latest development image as described in the setup instructions.

    "},{"location":"developer-guide/api/","title":"Web Service API","text":""},{"location":"developer-guide/api/#rest-api-endpoints","title":"REST API Endpoints","text":"

    For the currently implemented REST request endpoints available under /api/v1, please refer to the automatically generated backend API documentation as well as the request forms and entity models in our public repository:

    • https://pkg.go.dev/github.com/photoprism/photoprism/internal/api
    • https://github.com/photoprism/photoprism/tree/develop/internal/api

    API request bodies and responses are usually JSON-encoded, except for binary data and some of the OAuth2 endpoints. Note that the Content-Type header must be set to application/json for this, as the request may otherwise fail with error 400.

    We welcome any contributions that help improve our API docs and make them easier to use for developers. To learn how to access the API while our Swagger documentation is not complete yet, we recommend checking the requests in the browser console that our JS frontend sends when you perform actions like creating an album - and then use the same method, URI, encoding, value names and types for sending requests with your own application.

    "},{"location":"developer-guide/api/#client-authentication","title":"Client Authentication","text":"

    When clients have a valid access token, e.g. obtained through the POST /api/v1/session or POST /api/v1/oauth/token endpoint, they can use a standard Bearer Authorization header to authenticate their requests:

    Authorization: Bearer <token>\n

    Submitting the access token with a custom X-Auth-Token header is supported as well:

    curl -H \"X-Auth-Token: 7dbfa37b5a3db2a9e9dd186479018bfe2e3ce5a71fc2f955\" \\\nhttp://localhost:2342/api/v1/photos?count=10\n

    Besides using the API endpoints provided for this, you can also generate valid access tokens by running the photoprism auth add command in a terminal.

    Learn more \u203a

    App passwords can be used as access tokens in the Bearer Authorization header without first creating a session access token, and to obtain short-lived session access tokens through the POST /api/v1/session endpoint.

    "},{"location":"developer-guide/api/#service-discovery-endpoints","title":"Service Discovery Endpoints","text":""},{"location":"developer-guide/api/#oauth2-authorization-server","title":"OAuth2 Authorization Server","text":"
    /.well-known/oauth-authorization-server\n

    \u21aa https://demo.photoprism.app/.well-known/oauth-authorization-server

    Learn more \u203a

    "},{"location":"developer-guide/api/#openid-configuration","title":"OpenID Configuration","text":"

    It is not yet possible to use PhotoPrism as an OpenID Connect (OIDC) Identity Provider, since not all the required endpoints and grant types have been fully implemented. However, querying the /.well-known/openid-configuration endpoint shows what has already been implemented, so the missing capabilities can be identified and added over time.

    \u21aa https://demo.photoprism.app/.well-known/openid-configuration

    Learn more \u203a

    "},{"location":"developer-guide/api/#deprecation-policy","title":"Deprecation Policy","text":"

    Our REST API endpoints are currently not covered by an official deprecation policy, so some routes and request parameters may change as we add new features in upcoming releases.

    However, we avoid making breaking changes, especially to endpoints that we know other developers are using.

    "},{"location":"developer-guide/api/auth/","title":"Client Authentication","text":"

    In order to grant limited access to API clients, you can use the photoprism auth and photoprism clients subcommands to generate authentication tokens for them. While app passwords are bound to user accounts and can be generated by anyone from the UI, OAuth2 access tokens and client credentials can be used to access the REST API without being bound to an account.

    "},{"location":"developer-guide/api/auth/#app-passwords","title":"App Passwords","text":"

    All users can generate app-specific passwords for their own use from the web interface by navigating to Settings > Account and then clicking the Apps and Devices button.

    Alternatively, running the following command in a terminal will generate a new app-specific password e.g. for the admin account, so that WebDAV-compatible clients can synchronize files even if 2FA is enabled for the account or the account password is changed:

    photoprism auth add -n Sync -s \"webdav\" admin\n

    You will then be shown the generated app password so you can copy it and keep it in a safe place or enter it directly into an app, as you will not be able to see it again:

    |-----------------------------|---------------------|\n| App Password                | Authorization Scope |\n|-----------------------------|---------------------|\n| HY8fxO-8hvNqB-43UV4q-1AZ0vu | webdav              |\n|-----------------------------|---------------------|\n

    For added security, we recommend setting an expiration date for the app passwords and access tokens you generate. Common scopes for use with app passwords are either \"*\" for Full Access or \"webdav\" for WebDAV-compatible file synchronization apps.

    App passwords can be used as access tokens in the Bearer Authorization header without first creating a session access token, and to obtain short-lived session access tokens through the POST /api/v1/session endpoint.

    "},{"location":"developer-guide/api/auth/#command-options","title":"Command Options","text":"

    The following flags can be used with the photoprism auth add command (if you omit name or scope, you will be asked to enter them interactively):

    Command Flag Description --name CLIENT, -n CLIENT CLIENT name to help identify the application --scope SCOPES, -s SCOPES authorization SCOPES e.g. \"metrics\" or \"photos albums\" (\"*\" to allow all) --expires LIFETIME, -e LIFETIME authentication LIFETIME in seconds, after which access expires (-1 to disable the limit) (default: 31536000)"},{"location":"developer-guide/api/auth/#authorization-scopes","title":"Authorization Scopes","text":"

    One or more of these scopes can be specified to limit the access to certain API endpoints:

    files, folders, shares, photos, videos, favorites, albums, moments, calendar, people, places, labels, config, settings, services, users, sessions, logs, webdav, metrics

    Clients authenticated with app passwords are unable to change the account password or manage user accounts, even if you specify all scopes or use the wildcard \"*\" to allow all.

    "},{"location":"developer-guide/api/auth/#access-tokens","title":"Access Tokens","text":"

    If you do not specify a username as argument for the photoprism auth add command, a client access token will be generated (the same flags and scopes as above can be used to limit token authorization and lifetime):

    |--------------------------------------------------|---------------------|\n| Access Token                                     | Authorization Scope |\n|--------------------------------------------------|---------------------|\n| 7dbfa37b5a3db2a9e9dd186479018bfe2e3ce5a71fc2f955 | files folders       |\n|--------------------------------------------------|---------------------|\n

    Generating access tokens is a good choice for developers and other advanced users to connect scripts and external services to the PhotoPrism API, e.g. services that collect metrics or start indexing at regular intervals.

    Please note, however, that client access tokens cannot be used to synchronize files via WebDAV, even if the token authorization scope is set to \"webdav\" or \"*\", as this requires a registered user account. Access tokens can also not be used as a direct password replacement for apps, since clients are not allowed to use the POST /api/v1/session endpoint, which is required for logging in through the user interface.

    "},{"location":"developer-guide/api/auth/#client-credentials","title":"Client Credentials","text":"

    If clients support authentication via OAuth2 client credentials, you can use the following terminal commands to generate a client_id and client_secret for them, list registered clients, and delete client credentials that are no longer used:

    CLI Command Description photoprism clients ls [search] Lists registered client applications photoprism clients add [username] Registers a new client application photoprism clients show [identifier] Shows client configuration details photoprism clients mod [identifier] Updates client application settings photoprism clients rm [identifier] Deletes the specified client application photoprism clients reset --yes Removes all registered client applications

    For example, running the following in a terminal will generate credentials for Prometheus, with access limited to the metrics endpoint:

    photoprism clients add -n Prometheus -s metrics\n

    You will then be shown the generated client_id and client_secret so you can copy them and keep them in a safe place:

    |------------------|----------------------------------|\n| Client ID        | Client Secret                    |\n|------------------|----------------------------------|\n| csce0w2joodmirvi | 5VKkBeZLDvojjpE9XzCMXShnrxmxHWvN |\n|------------------|----------------------------------|\n

    OAuth2 client credentials cannot be directly used for synchronizing files via WebDAV, as a password replacement for apps, or for logging in to the web interface.

    "},{"location":"developer-guide/api/auth/#command-options_1","title":"Command Options","text":"

    The following parameters can be used with the photoprism clients add command, e.g. to limit the number of access tokens the client can request:

    Command Flag Description --name CLIENT, -n CLIENT CLIENT name to help identify the application --role ROLE, -r ROLE client authorization ROLE (default: \"client\") --scope SCOPES, -s SCOPES client authorization SCOPES e.g. \"metrics\" or \"photos albums\" (\"*\" to allow all) --expires LIFETIME, -e LIFETIME access token LIFETIME in seconds, after which a new token must be requested (default: 86400) --tokens NUMBER, -t NUMBER maximum NUMBER of access tokens that the client can request (-1 to disable the limit) (default: 10)

    If you omit the name or scope parameter, you will be asked to enter them interactively. One or more of these scopes can be specified to limit the access to certain API endpoints:

    files, folders, shares, photos, videos, favorites, albums, moments, calendar, people, places, labels, config, settings, services, users, sessions, logs, webdav, metrics

    When requesting access tokens, clients can further restrict the scope of the tokens by passing the scope parameter to the POST /api/v1/oauth/token endpoint.

    "},{"location":"developer-guide/api/docs/","title":"Swagger API Documentation","text":"

    Our interactive API documentation, publicly available at docs.photoprism.dev, is automatically generated from the source code annotations in the /internal/api package.

    Running the following command in your local development environment will update the /internal/api/swagger.json file, which contains the API endpoint documentation in machine-readable form:

    make swag\n

    You can view it as HTML by navigating to /api/v1/docs/index.html after you have rebuilt and started photoprism with the commands make build and ./photoprism start:

    Any help with adding annotations to improve our documentation is much appreciated!

    The /api/v1/docs API documentation endpoint is disabled in production builds.

    "},{"location":"developer-guide/api/oauth2/","title":"OAuth2 Grant Types","text":"

    The OAuth 2.0 specification is an authorization framework that contains a set of methods, or grants, that a client application can use to obtain an access token. Each grant type is designed for a specific use case:

    • Password Grant
    • Client Credentials

    The access token can then be passed to an API endpoint, which checks it to determine validity and authorization scope.

    Support for the Authorization Code Flow is planned for a future release.

    "},{"location":"developer-guide/api/oauth2/#server-endpoints","title":"Server Endpoints","text":"Resource Endpoint Methods Authorization /api/v1/oauth/authorize GET, POST Token /api/v1/oauth/token POST UserInfo /api/v1/oauth/userinfo GET, POST Registration not implemented yet Introspection not implemented yet Revocation /api/v1/oauth/revoke POST End Session not implemented yet Device Authorization not implemented yet

    Clients can query the /.well-known/oauth-authorization-server and /.well-known/openid-configuration endpoints for automatic service discovery:

    • https://demo.photoprism.app/.well-known/oauth-authorization-server
    • https://demo.photoprism.app/.well-known/openid-configuration

    Note that the Authorization and UserInfo endpoints cannot be used yet as they are still under development.

    "},{"location":"developer-guide/api/oauth2/#access-tokens","title":"Access Tokens","text":"

    When clients have a valid access token, e.g. obtained through the POST /api/v1/oauth/token endpoint, they can use a standard Bearer Authorization header to authenticate their requests:

    Authorization: Bearer <token>\n

    Submitting the access token with a custom X-Auth-Token header is supported as well:

    curl -H \"X-Auth-Token: 7dbfa37b5a3db2a9e9dd186479018bfe2e3ce5a71fc2f955\" \\\nhttp://localhost:2342/api/v1/photos?count=10\n

    Besides using the API endpoints provided for this, you can also generate valid access tokens by running the photoprism auth add command in a terminal.

    Learn more \u203a

    App passwords can be used as access tokens in the Bearer Authorization header without first creating a session access token, and to obtain short-lived session access tokens through the POST /api/v1/session endpoint.

    "},{"location":"developer-guide/api/oauth2/#related-issues","title":"Related Issues","text":"
    • API: Expose Prometheus-style metrics endpoint #3730
    • Monitoring: Add a Prometheus-compatible API endpoint #213
    • Account: Add Support for 2-Factor Authentication #808
    • Account: Add Support for OpenID Connect (OIDC) #782
    • Multi-User Photo Gallery with private and shared photos/albums #98
    "},{"location":"developer-guide/api/oauth2/#protocol-references","title":"Protocol References","text":"
    • https://prometheus.io/docs/prometheus/latest/configuration/configuration/#oauth2
    • https://www.scottbrady91.com/oauth/client-authentication#:~:text=OAuth%20client%20secrets
    • https://www.scottbrady91.com/oauth/oauth-is-not-user-authorization
    • https://www.oauth.com/oauth2-servers/access-tokens/refreshing-access-tokens/
    • https://www.oauth.com/oauth2-servers/access-tokens/access-token-lifetime/
    • https://www.oauth.com/oauth2-servers/access-tokens/access-token-response/
    • https://www.oauth.com/oauth2-servers/client-registration/client-id-secret/
    • https://www.oauth.com/oauth2-servers/authorization/the-authorization-request/
    • https://www.oauth.com/oauth2-servers/authorization/requiring-user-login/
    • https://www.oauth.com/oauth2-servers/authorization/the-authorization-interface/
    • https://www.oauth.com/oauth2-servers/authorization/the-authorization-response/
    • https://learn.microsoft.com/en-us/linkedin/shared/authentication/programmatic-refresh-tokens
    • https://oauth.net/2/grant-types/client-credentials/
    • https://oauth.net/2/scope/
    • https://datatracker.ietf.org/doc/html/rfc6749#section-4.4
    • https://auth0.com/docs/get-started/authentication-and-authorization-flow/call-your-api-using-the-client-credentials-flow#example-post-to-token-url
    • https://auth0.com/intro-to-iam/what-is-oauth-2
    • https://auth0.com/docs/authenticate/protocols/oauth
    • https://auth0.com/docs/secure/tokens/id-tokens/id-token-structure
    • https://auth0.com/docs/authenticate/protocols/openid-connect-protocol
    • https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2#grant-type-client-credentials
    • https://aaronparecki.com/oauth-2-simplified/
    • https://rclone.org/webdav/
    • https://owncloud.dev/clients/rclone/webdav-sync-oidc/
    • https://www.webdavsystem.com/server/documentation/choosing_authentication/
    • http://www.webdav.org/specs/rfc2617.html#rfc.section.4.1
    • https://frontegg.com/blog/oauth-grant-types
    "},{"location":"developer-guide/api/oauth2/#authentication-libraries","title":"Authentication Libraries","text":"
    • zitadel/oidc
    • https://pkg.go.dev/golang.org/x/oauth2
    • https://pkg.go.dev/golang.org/x/oauth2/jwt
    • go-oauth2/oauth2
    • pquerna/otp
    "},{"location":"developer-guide/api/oauth2/#documentation-examples","title":"Documentation Examples","text":"
    • https://learn.microsoft.com/en-us/machine-learning-server/operationalize/how-to-manage-access-tokens
    • https://docs.semui.co/administration-guide/openid
    • https://api.stackexchange.com/docs/authentication
    • https://dev.fitbit.com/build/reference/web-api/developer-guide/authorization/
    • https://cloudentity.com/developers/basics/oauth-client-authentication/client-secret-authentication/
    • https://developer.okta.com/docs/reference/api/oidc/#get-started
    • https://www.authelia.com/configuration/identity-providers/open-id-connect/
    • https://goauthentik.io/integrations/sources/oauth/#openid-connect
    • https://developers.google.com/identity/openid-connect/openid-connect
    • https://connect2id.com/products/server/docs/api
    • https://connect2id.com/products/server/docs/api/discovery
    • https://connect2id.com/products/server/docs/api/authorization
    • https://connect2id.com/products/server/docs/api/token
    • https://www.zoho.com/accounts/protocol/oauth/token-limits.html
    • https://docs.github.com/en/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication-recovery-methods
    • https://help.akana.com/content/current/cm/api_oauth/oauth_oauth20/m_oauth20_getTokenPOST.htm
    • https://help.akana.com/content/current/cm/api_oauth/aaref/Ref_Values.htm#values_oauthgranttypes
    "},{"location":"developer-guide/api/oidc/","title":"Single Sign-On via OpenID Connect","text":"

    OpenID Connect (OIDC) extends OAuth 2.0 to allow users to log in and optionally register through an external identity provider instead of manually entering a username and password:

    "},{"location":"developer-guide/api/oidc/#authentication-flow","title":"Authentication Flow","text":"

    Learn more \u203a

    "},{"location":"developer-guide/api/oidc/#config-options","title":"Config Options","text":"Environment CLI Flag Default Description PHOTOPRISM_OIDC_URI --oidc-uri issuer URI for single sign-on via OpenID Connect, e.g. https://accounts.google.com PHOTOPRISM_OIDC_CLIENT --oidc-client client ID for single sign-on via OpenID Connect PHOTOPRISM_OIDC_SECRET --oidc-secret client SECRET for single sign-on via OpenID Connect PHOTOPRISM_OIDC_PROVIDER --oidc-provider custom identity provider NAME, e.g. Google PHOTOPRISM_OIDC_ICON --oidc-icon custom identity provider icon URI PHOTOPRISM_OIDC_REDIRECT --oidc-redirect automatically redirect unauthenticated users to the configured identity provider PHOTOPRISM_OIDC_REGISTER --oidc-register allow new users to create an account when they sign in with OpenID Connect PHOTOPRISM_OIDC_USERNAME --oidc-username preferred_username preferred username CLAIM for new OpenID Connect users (preferred_username, name, nickname, email) PHOTOPRISM_OIDC_WEBDAV --oidc-webdav allow new OpenID Connect users to use WebDAV when they have a role that allows it PHOTOPRISM_DISABLE_OIDC --disable-oidc disable single sign-on via OpenID Connect, even if an identity provider has been configured

    Your PhotoPrism instance and the OpenID Connect Identity Provider (IdP) must be accessible via HTTPS and have valid TLS certificates configured for it. Please also make sure that the hostname in the Redirect URL configured on the IdP matches the Site URL used by PhotoPrism. Single sign-on via OIDC can otherwise not be enabled.

    "},{"location":"developer-guide/api/oidc/#identity-providers","title":"Identity Providers","text":"

    To authenticate users via OIDC, you can either set up and use a self-hosted identity provider such as ZITADEL or Keycloak, or configure a public identity provider service such as those operated by Google, Microsoft, GitHub, or Amazon.

    Single sign-on can be configured automatically if the identity provider offers a standardized /.well-known/openid-configuration endpoint for service discovery, for example:

    • https://accounts.google.com/.well-known/openid-configuration
    "},{"location":"developer-guide/api/oidc/#issuer-uri","title":"Issuer URI","text":"

    The Issuer URI in your configuration must match the issuer value returned by the /.well-known/openid-configuration endpoint of your OpenID Connect Identity Provider (IdP), for example https://accounts.google.com if you use Google for authentication.

    You may not modify the URI in any way, e.g. by adding or omitting slashes at the end. If the values do not match, the validation will fail and users cannot be redirected to your provider's login page. For security reasons, only a generic error message is displayed in this case.

    "},{"location":"developer-guide/api/oidc/#redirect-url","title":"Redirect URL","text":"

    The Redirect URL that must be specified when registering a new client with an Identity Provider is as follows, where {hostname} must be replaced by the hostname in the Site URL, e.g. configured via PHOTOPRISM_SITE_URL:

    https://{hostname}/api/v1/oidc/redirect\n

    Note that both the Site URL configured for your instance and the Redirect URL must start with https:// and that their hostnames must match, as the use of secure connections is a strict requirement for OpenID Connect.

    "},{"location":"developer-guide/api/oidc/#local-development","title":"Local Development","text":"

    Our development environment comes with a pre-configured Keycloak OIDC Identity Provider running at https://keycloak.localssl.dev/ for local testing:

    • https://keycloak.localssl.dev/realms/master/.well-known/openid-configuration

    An admin account for managing users and a user account for testing single sign-on are pre-registered. Both have the password photoprism.

    Please do not use this test identity provider in a production environment as its configuration is not secure.

    "},{"location":"developer-guide/api/oidc/#preferred-username","title":"Preferred Username","text":"

    When a new user signs in with OpenID Connect1, their preferred username may already be registered. In this case, a random 6-digit number is appended to resolve the conflict.

    The config option PHOTOPRISM_OIDC_USERNAME allows you to change the preferred username for new accounts from preferred_username to name, nickname, or verified email. Names are changed to lowercase handles so that, for example, \"Jens Mander\" becomes \"jens.mander\".

    "},{"location":"developer-guide/api/oidc/#existing-accounts","title":"Existing Accounts","text":"

    Super admins can manually connect existing user accounts2 under Settings > Users by changing the authentication to OIDC and then setting the Subject ID to match the account identifier from the configured Identity Provider:

    The Edit Account dialog may additionally contain a text field for the Issuer URL. It does not need to be entered manually as it is set automatically after the first login.

    Alternatively, you can run the following command in a terminal to allow authentication via OIDC and set a Subject ID to connect existing accounts:

    photoprism users mod --auth=oidc --auth-id=[sub] [username]\n

    Learn more \u203a

    "},{"location":"developer-guide/api/oidc/#passwords","title":"Passwords","text":"

    Changing the authentication of an account to OIDC does not remove a previously set password, so that it can still be used to log in (optionally also in combination with 2FA).

    If a local password has been set for an account, you can remove it by running the following command in a terminal:

    photoprism passwd --rm [username]\n

    Super admins can alternatively set the account password to a long random value through the Admin Web UI or CLI to effectively prevent local authentication.

    Learn more \u203a

    "},{"location":"developer-guide/api/oidc/#deleting-accounts","title":"Deleting Accounts","text":"

    Deleted accounts remain linked to the Subject ID, so logging in via OIDC is no longer possible and no new account can be registered for the same Subject ID either.

    If you wish to change the connected user account or create a new account instead, you must therefore change the authentication of the old account e.g. to None before deleting it:

    photoprism users mod --auth=none [username]\n

    To restore a previously deleted account, admins can follow the same steps as for creating a new account with the same username through the Admin Web UI or the photoprism users add command. You will then be asked if you want to restore the account.

    Learn more \u203a

    "},{"location":"developer-guide/api/oidc/#service-discovery","title":"Service Discovery","text":"

    It is not yet possible to use PhotoPrism as an Identity Provider, since not all the required endpoints and grant types have been fully implemented.

    However, querying the /.well-known/openid-configuration endpoint shows what has already been implemented, so the missing capabilities can be identified and added over time:

    • https://demo.photoprism.app/.well-known/openid-configuration
    "},{"location":"developer-guide/api/oidc/#related-issues","title":"Related Issues","text":"
    • Auth: Add support for single sign-on via OpenID Connect (OIDC) #782
    • Auth: Add userinfo API endpoint to get information about the logged in user #4369
    • Auth: Add authorize API endpoint to implement the authorization code flow #4368
    "},{"location":"developer-guide/api/oidc/#software-libraries","title":"Software Libraries","text":"
    • zitadel/oidc by https://zitadel.com/
    "},{"location":"developer-guide/api/oidc/#protocol-references","title":"Protocol References","text":"
    • https://openid.net/developers/how-connect-works/
    • https://dl.photoprism.app/pdf/publications/20220113-Volkmann_OpenID_Connect_Thesis.pdf
    • https://oauth.net/openid-for-verifiable-credentials/
    • https://developers.google.com/identity/openid-connect/openid-connect
    • https://www.ory.sh/docs/oauth2-oidc/authorization-code-flow
    • https://developer.okta.com/docs/concepts/oauth-openid/
    • https://developer.okta.com/docs/reference/api/oidc/
    • https://developer.okta.com/docs/reference/api/oauth-clients/
    • https://auth0.com/docs/authenticate/protocols/openid-connect-protocol
    • https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc#openid-connect-scopes
    • https://owncloud.dev/clients/rclone/webdav-sync-oidc/
    • https://blog.cubieserver.de/2022/complete-guide-to-nextcloud-oidc-authentication-with-authentik/
    • https://auth0.com/docs/get-started/applications/configure-applications-with-oidc-discovery
    • https://connect2id.com/products/server/docs/api/authorization
    • https://www.authlete.com/developers/definitive_guide/authorization_endpoint_spec/
    • https://www.oauth.com/oauth2-servers/authorization/the-authorization-request/
    • https://www.oauth.com/oauth2-servers/authorization/requiring-user-login/
    • https://www.oauth.com/oauth2-servers/authorization/the-authorization-interface/
    • https://www.oauth.com/oauth2-servers/authorization/the-authorization-response/
    "},{"location":"developer-guide/api/oidc/#session-management","title":"Session Management","text":"
    • https://openid.net/specs/openid-connect-session-1_0.html#OPiframe
    • https://zitadel.com/blog/session-timeouts-logouts
    • https://ldapwiki.com/wiki/Wiki.jsp?page=OpenID%20Connect%20Session%20Management
    • https://ldapwiki.com/wiki/Wiki.jsp?page=OpenID%20Connect%20Back-Channel%20Logout
    • https://openid.net/specs/openid-connect-rpinitiated-1_0.html
    • https://auth0.com/docs/authenticate/login/logout/log-users-out-of-auth0
    • https://medium.com/@robert.broeckelmann/openid-connect-logout-eccc73df758f
    "},{"location":"developer-guide/api/oidc/#frequently-asked-questions","title":"Frequently Asked Questions","text":""},{"location":"developer-guide/api/oidc/#is-it-possible-to-set-a-default-role-for-new-oidc-users","title":"Is it possible to set a default role for new OIDC users?","text":"

    For security reasons, our Personal Editions currently default to the Guest role, which admins can then upgrade after checking the eligibility of newly registered accounts.

    Learn more \u203a

    "},{"location":"developer-guide/api/oidc/#can-i-configure-a-custom-claim-for-the-preferred-username","title":"Can I configure a custom claim for the preferred username?","text":"

    You can choose between preferred_username, name, nickname and email, where preferred_username is the default. The other options are used as a fallback if no value is returned for the configured claim.

    Note that it is not possible to use a non-standard claim such as username, as this could lead to conflicts and potential security issues, e.g. if the value is not unique or not reliably set.

    Learn more \u203a

    1. PHOTOPRISM_OIDC_REGISTER must be set to \"true\" to allow new users to create an account\u00a0\u21a9

    2. Admins cannot change the authentication of their own user account through the Admin Web UI so that they do not accidentally lock themselves out e.g. by setting it to None.\u00a0\u21a9

    "},{"location":"developer-guide/api/search/","title":"Search API Endpoints","text":""},{"location":"developer-guide/api/search/#get-apiv1photos","title":"GET /api/v1/photos","text":"

    When querying the /api/v1/photos endpoint, matching files will be returned along with the metadata of the corresponding photos. This may be unexpected at first due to the endpoint name. However, this approach allows to sort results e.g. by color or file size, which differ at the file level and therefore require dynamic sorting/grouping of multiple matching files that may belong to the same photo (see Stacks).

    To simplify processing in the client/user interface, you can set the merged parameter to true so that consecutive files with the same photo ID are merged into a single result with the Files property containing the related files.

    In particular, multiple files are returned for a single photo in the case of multi-file/hybrid media formats such as Live Photos, as these consist of a photo and a video file. The same applies to RAW/JPEG.

    If you only want the primary image (thumbnail) to be returned, you can set the primary filter to true. In this case, it is not necessary to also set merged to true as a single file will be returned for each photo. This can simplify things if you don't need to know anything about the additional files, e.g. when just rendering thumbnails without metadata.

    "},{"location":"developer-guide/api/search/#composite-id-and-uids","title":"Composite ID and UIDs","text":"

    The ID returned by the search API is a composite ID consisting of the photo ID and the related file ID (usually the file selected as primary image), so that this ID is unique as required for rendering in the user interface.

    Note that you do not need this composite ID to communicate with any of our API endpoints. Instead, you will either have to pass a UID (e.g. when updating photo metadata) or the SHA1 Hash of a file (e.g. when requesting a thumbnail).

    Using random UIDs prevents possible caching issues in proxies/clients, e.g. if the index is reset on the server and the numeric IDs would thus start at 0 again. They are also hard to guess, so a time-consuming brute force attack is required instead of simply enumerating integers starting from 0.

    "},{"location":"developer-guide/api/search/#response-example","title":"Response Example","text":"
    [{\n    \"ID\": \"11-21\",\n    \"UID\": \"pse1yzastu36irsj\",\n    \"Type\": \"image\",\n    \"TypeSrc\": \"\",\n    \"TakenAt\": \"2012-08-27T12:40:25Z\",\n    \"TakenAtLocal\": \"2012-08-27T14:40:25Z\",\n    \"TakenSrc\": \"meta\",\n    \"TimeZone\": \"Europe/Madrid\",\n    \"Path\": \"2012\",\n    \"Name\": \"20120827-144025-Bodegas-Ysios-Winery-Laguardia-Spain\",\n    \"OriginalName\": \"\",\n    \"Title\": \"Bodegas Ysios Winery / Laguardia / Spain\",\n    \"Description\": \"\",\n    \"Year\": 2012,\n    \"Month\": 8,\n    \"Day\": 27,\n    \"Country\": \"es\",\n    \"Stack\": 0,\n    \"Favorite\": false,\n    \"Private\": false,\n    \"Iso\": 100,\n    \"FocalLength\": 28,\n    \"FNumber\": 11,\n    \"Exposure\": \"1/160\",\n    \"Quality\": 4,\n    \"Resolution\": 8,\n    \"Color\": 2,\n    \"Scan\": false,\n    \"Panorama\": false,\n    \"CameraID\": 5,\n    \"CameraSrc\": \"meta\",\n    \"CameraSerial\": \"1730906408\",\n    \"CameraMake\": \"Canon\",\n    \"CameraModel\": \"EOS 30D\",\n    \"LensID\": 5,\n    \"LensModel\": \"EF28mm f/1.8 USM\",\n    \"Lat\": 42.568302,\n    \"Lng\": -2.5908582,\n    \"CellID\": \"s2:0d4ff9b1b32c\",\n    \"PlaceID\": \"es:7v314KSnaCLy\",\n    \"PlaceSrc\": \"meta\",\n    \"PlaceLabel\": \"Laguardia, Euskadi, Spain\",\n    \"PlaceCity\": \"Laguardia\",\n    \"PlaceState\": \"Euskadi\",\n    \"PlaceCountry\": \"es\",\n    \"InstanceID\": \"\",\n    \"FileUID\": \"fse1yza2r4gsjq6f\",\n    \"FileRoot\": \"/\",\n    \"FileName\": \"2012/20120827-144025-Bodegas-Ysios-Winery-Laguardia-Spain.jpg\",\n    \"Hash\": \"4bc82c3ea5aaa323aea801fe0125b554af8e49af\",\n    \"Width\": 3368,\n    \"Height\": 2246,\n    \"Portrait\": false,\n    \"Merged\": false,\n    \"CreatedAt\": \"2024-05-25T17:52:22.291451832Z\",\n    \"UpdatedAt\": \"2024-05-25T17:52:22.307889576Z\",\n    \"EditedAt\": \"0001-01-01T00:00:00Z\",\n    \"CheckedAt\": \"2024-05-26T06:15:09Z\",\n    \"Files\": [\n        {\n        \"UID\": \"fse1yza2r4gsjq6f\",\n        \"PhotoUID\": \"pse1yzastu36irsj\",\n        \"Name\": \"2012/20120827-144025-Bodegas-Ysios-Winery-Laguardia-Spain.jpg\",\n        \"Root\": \"/\",\n        \"Hash\": \"4bc82c3ea5aaa323aea801fe0125b554af8e49af\",\n        \"Size\": 2473990,\n        \"Primary\": true,\n        \"Codec\": \"jpeg\",\n        \"FileType\": \"jpg\",\n        \"MediaType\": \"image\",\n        \"Mime\": \"image/jpeg\",\n        \"Width\": 3368,\n        \"Height\": 2246,\n        \"Orientation\": 1,\n        \"AspectRatio\": 1.5,\n        \"Colors\": \"616121222\",\n        \"Luminance\": \"AACA5A888\",\n        \"Diff\": 767,\n        \"Chroma\": 15,\n        \"CreatedAt\": \"2024-05-25T17:52:22.291451832Z\",\n        \"UpdatedAt\": \"2024-05-25T17:52:22.307889576Z\",\n        \"Markers\": []\n        }\n    ]\n}]\n
    "},{"location":"developer-guide/api/search/#response-headers","title":"Response Headers","text":"Header Type Example Notes X-Count int 120 Actual number of files returned X-Limit int 120 Maximum number of files requested X-Offset int 0 File offset X-Download-Token string 3qjg1db2 Security token required to download original files X-Preview-Token string 174utza5 Security token required for the Thumbnail Image API

    In order to fetch all results, you can perform a follow-up query if the number in the X-Count response header matches X-Limit. For this, the offset request parameter must be set to the number of files already returned.

    "},{"location":"developer-guide/api/search/#request-parameters","title":"Request Parameters","text":"Name Type Example Notes q string dog color:red Search query as entered by the user in the search toolbar, see Search Filters s string ariqwb43p5dh9h13 Limits the result scope to the specified album UID count int 1000 Maximum number of files to be returned offset int 0 File offset order string added Sort order e.g. added, updated, edited, relevance, duration, size, newest, oldest, similar, name, title, or random merged bool true Merges consecutive files that belong to the same photo into a single result, see above for an explanation"},{"location":"developer-guide/api/search/#search-filters","title":"Search Filters","text":"

    The following search filters can be used as regular GET request parameters or submitted as part of a search query with the q request parameter:

    Filter Type Examples Notes dist decimal dist:50 Distance to Position (km) lat decimal lat:41.894043 GPS Position (Latitude) lng decimal lng:-87.62448 GPS Position (Longitude) chroma number chroma:70 Chroma (0-100) diff number diff:-1 diff:2 Differential Perceptual Hash (000000-FFFFFF) quality number quality:0 quality:3 Minimum quality score (1-7) album string album:berlin Album UID or Name, supports * wildcards albums string albums:\"South Africa & Birds\" Album Names (combinable with & and |) alt string alt:300-500 GPS Altitude (m) camera string camera:canon Camera Make/Model Name category string category:airport Location Category city string city:\"Berlin\" Location City (separate with |) color string color:\"red|blue\" Color Name (purple, magenta, pink, red, orange, gold, yellow, lime, green, teal, cyan, blue, brown, white, grey, black) (separate with |) country string country:\"de|us\" Location Country Code (separate with |) day string day:3|13 Day of Month (1-31, separate with |) f string f:2.8-4.5 Aperture (f-number) face string face:PN6QO5INYTUSAATOFL43LL2ABAV5ACZG Face ID, yes, no, new, or kind faces string faces:yes faces:3 Minimum number of Faces (yes = 1) favorite string favorite:true favorite:false Finds images by favorite status filename string filename:\"2021/07/12345.jpg\" File Name with path and extension (separate with |) folder string folder:\"*/2020\" Path Name (separate with |), supports * wildcards geo string geo:yes Finds pictures with or without coordinates hash string hash:2fd4e1c67a2d SHA1 File Hash (separate with |) id string id:123e4567-e89b-... Finds pictures by Exif UID, XMP Document ID or Instance ID iso string iso:200-400 ISO Number (light sensitivity) keywords string keywords:\"sand&water\" Keywords (combinable with & and |) label string label:cat|dog Label Names (separate with |) latlng string latlng:\"name\" GPS Bounding Box (Lat N, Lng E, Lat S, Lng W) lens string lens:ef24 Lens Make/Model Name mm string mm:28-35 Focal Length (35mm equivalent) month string month:7|10 Month (1-12, separate with |) mp string mp:3-6 Resolution in Megapixels (MP) name string name:\"IMG_9831-112*\" File Name without path and extension (separate with |) near string near:pqbcf5j446s0futy Finds nearby pictures (UID) olc string olc:8FWCHX7W+ OLC Position (Open Location Code) original string original:\"IMG_9831-112*\" Original file name of imported files (separate with |) path string path:2020/Holiday Path Name (separate with |), supports * wildcards people string people:\"Jane & John\" Subject Names (combinable with & and |) person string person:\"Jane Doe & John Doe\" Subject Names, exact matches (combinable with & and |) s2 string s2:4799e370ca54c8b9 S2 Position (Cell ID) scan string scan:true scan:false Finds scanned photos and documents state string state:\"Baden-W\u00fcrttemberg\" Location State (separate with |) subject string subject:\"Jane Doe & John Doe\" Alias for person subjects string subjects:\"Jane & John\" Alias for people title string title:\"Lake*\" Title (separate with |) type string type:raw Media Type (image, video, raw, live, animated); separate with | uid string uid:pqbcf5j446s0futy Limits results to the specified internal unique IDs year string year:1990|2003 Year (separate with |) animated switch animated:yes Finds animated GIFs archived switch archived:yes Finds archived pictures error switch error:yes Finds pictures with errors hidden switch hidden:yes Finds hidden pictures (broken or unsupported) landscape switch landscape:yes Finds pictures in landscape format live switch live:yes Finds Live Photos and short videos mono switch mono:yes Finds pictures with few or no colors panorama switch panorama:yes Finds pictures with an aspect ratio > 1.9:1 photo switch photo:yes Finds only photos, no videos portrait switch portrait:yes Finds pictures in portrait format primary switch primary:yes Finds primary JPEG files only private switch private:yes Finds private pictures public switch public:yes Excludes private pictures raw switch raw:yes Finds pictures with RAW image file review switch review:yes Finds pictures in review square switch square:yes Finds images with an aspect ratio of 1:1 stack switch stack:yes Finds pictures with more than one media file stackable switch stackable:yes Finds pictures that can be stacked with additional media files unsorted switch unsorted:yes Finds pictures not in an album unstacked switch unstacked:yes Finds pictures with a file that has been removed from a stack vector switch vector:yes Finds vector graphics only video switch video:yes Finds video files only added timestamp added:\"2006-01-02T15:04:05Z\" Finds pictures added at or after this time after timestamp after:\"2022-01-30\" Finds pictures taken on or after this date before timestamp before:\"2022-01-30\" Finds pictures taken on or before this date edited timestamp edited:\"2006-01-02T15:04:05Z\" Finds pictures edited at or after this time taken timestamp taken:\"2022-01-30\" Finds pictures taken on the specified date updated timestamp updated:\"2006-01-02T15:04:05Z\" Finds pictures updated at or after this time"},{"location":"developer-guide/api/thumbnails/","title":"Thumbnail Image API","text":""},{"location":"developer-guide/api/thumbnails/#introduction","title":"Introduction","text":"

    Like most commercial image hosting services, we have chosen to implement a cookie-free thumbnail API to minimize request latency by avoiding unnecessary network traffic:

    • When a browser requests static files such as images from a server over HTTPS, it is generally unnecessary to send a cookie with each request if the URLs cannot be guessed, so for most practical use cases
    • One possible use of cookies may be to prevent the user from intentionally or accidentally sharing confidential thumbnail URLs with others
    • This is possible with most image hosting services/social media sites, and could also be considered a feature if you just want to share a few thumbnails without a lot of bells and whistles
    • Once an image has been downloaded by someone else, blocking the original URL provides little additional security, as digital copies are just as good as the original, see info box below
    • Keeping that in mind, previously shared URLs can be invalidated by changing the security token in your config
    • This will invalidate the browser cache on all connected devices, requiring previously cached thumbnails to be downloaded again
    • Be aware that frequent token changes result in performance degradation and a poor user experience.

    In addition to better performance, a major advantage of cookie-free thumbnails is that they can be easily integrated into a content delivery network (CDN), since there is no need to check cookies or add other complex logic on edge servers.

    Since most users only have one domain/host name and modern web applications can store authentication tokens in localStorage, our Thumbnail Image API does not currently require or use cookies.

    "},{"location":"developer-guide/api/thumbnails/#security-considerations","title":"Security Considerations","text":"

    Digital copies are as good as originals: Once shared and downloaded, such images should be considered \"leaked\" because they are cached and can be re-shared by the recipient at any time, with no sure way to get all copies back, even if the download URL becomes invalid or the service is shut down completely.

    Any form of protection we could provide would essentially be \"snake oil\", could be circumvented, and would have a negative impact on the user experience, such as disabling the browser cache or context menu.

    For the highest level of protection, it is recommended to shield your private server from the public Internet. Always use HTTPS, a VPN and/or ideally TLS client certificates and make sure that only people you trust have access to your instance.

    "},{"location":"developer-guide/api/thumbnails/#further-reading","title":"Further Reading","text":"
    • https://ourcodeworld.com/articles/read/1341/why-you-should-use-a-cookie-less-domain-for-serving-your-static-content-cdn
    • https://pragmaticwebsecurity.com/articles/oauthoidc/localstorage-xss.html
    "},{"location":"developer-guide/api/thumbnails/#image-endpoint-uri","title":"Image Endpoint URI","text":"

    A GET request to the following URI returns a thumbnail image of the specified size for the picture referenced by the SHA1 hash:

    /api/v1/t/:hash/:token/:size\n

    If the instance requires authentication, you must also specify a valid security token, e.g.:

    /api/v1/t/a2195b6840f46b555719d8e22e9b080e61a7317c/10d68214/tile_500\n

    With public mode enabled, you can instead use \"public\" as security token placeholder:

    /api/v1/t/a2195b6840f46b555719d8e22e9b080e61a7317c/public/tile_500\n
    "},{"location":"developer-guide/api/thumbnails/#video-endpoint-uri","title":"Video Endpoint URI","text":"

    A GET request to the following URI returns the video referenced by the SHA1 hash (transcoding and range requests are supported for streaming):

    /api/v1/videos/:hash/:token/:format\n

    If the instance requires authentication, you must also specify a valid security token, e.g.:

    /api/v1/videos/51843134d75f4cbde534270cdd5954067f887ee6/10d68214/avc\n

    With public mode enabled, you can instead use \"public\" as security token placeholder:

    /api/v1/videos/51843134d75f4cbde534270cdd5954067f887ee6/public/avc\n

    The only target format currently available for transcoding is avc (MPEG-4 AVC). Additional formats will become available in a future release.

    "},{"location":"developer-guide/api/thumbnails/#thumbnail-sizes","title":"Thumbnail Sizes","text":"

    The smallest configurable static and dynamic size limit is 720px, so most sizes up to fit_720 are always generated by default. Higher size limits generate thumbnails with more detail at higher resolutions - either statically (pre-generated while indexing) or on demand if the configuration permits.

    Optional thumbnail sizes cannot be pre-generated and are only rendered on request, for example when sharing an image on Instagram.

    The following overview shows the name, dimensions, and aspect ratio for each thumbnail size as well as a description of how it is used:

    Name Width Height Aspect Ratio Available Usage colors 3 3 1:1 Always Color Detection tile_50 50 50 1:1 Always List View tile_100 100 100 1:1 Always Places View left_224 224 224 1:1 On-Demand TensorFlow right_224 224 224 1:1 On-Demand TensorFlow tile_224 224 224 1:1 Always TensorFlow, Mosaic View tile_500 500 500 1:1 Always Cards View fit_720 720 720 Preserved Always SD TV, Mobile tile_1080 1080 1080 1:1 Optional Instagram fit_1280 1280 1024 Preserved On-Demand HD TV, SXGA fit_1600 1600 900 Preserved Optional Social Media fit_1920 1920 1200 Preserved On-Demand Full HD fit_2048 2048 2048 Preserved Optional DCI 2K, Tablets fit_2560 2560 1600 Preserved On-Demand Quad HD, Notebooks fit_3840 3840 2400 Preserved Optional 4K Ultra HD fit_4096 4096 4096 Preserved On-Demand DCI 4K, Retina 4K fit_7680 7680 4320 Preserved On-Demand 8K Ultra HD 2

    \u21aa internal/thumb/sizes.go

    Generated thumbnail files are stored in the storage/cache/thumbnails folder, where the path and file name depend on the thumbnail size and original file hash. Learn more \u203a

    "},{"location":"developer-guide/api/clients/go/","title":"Go API Client","text":"

    Maintained by Kris N\u00f3va

    GitHub URL: kris-nova/photoprism-client-go

    "},{"location":"developer-guide/api/clients/go/#installation","title":"Installation","text":"
    go get github.com/kris-nova/photoprism-client-go\n
    "},{"location":"developer-guide/api/clients/go/#methods","title":"Methods","text":"
    func New(connURL *url.URL, token, downloadToken string) *V1Client\nfunc (v1 *V1Client) AddPhotosToAlbum(albumUUID string, photoIDs []string) error\nfunc (v1 *V1Client) ApprovePhoto(uuid string) error\nfunc (v1 *V1Client) CancelIndex() error\nfunc (v1 *V1Client) CloneAlbum(album Album) (Album, error)\nfunc (v1 *V1Client) CreateAlbum(album Album) (Album, error)\nfunc (v1 *V1Client) DeleteAlbums(albumUUIDs []string) error\nfunc (v1 *V1Client) DELETE(payload interface{}, endpointFormat string, a ...interface{}) *V1Response\nfunc (v1 *V1Client) DeletePhotosFromAlbum(albumUUID string, photoIDs []string) error\nfunc (v1 *V1Client) DislikeAlbum(uuid string) error\nfunc (v1 *V1Client) DislikePhoto(uuid string) error\nfunc (v1 *V1Client) Endpoint(str string) string\nfunc (v1 *V1Client) GetAlbumDownload(uuid string) ([]byte, error)\nfunc (v1 *V1Client) GetAlbums(options *AlbumOptions) ([]Album, error)\nfunc (v1 *V1Client) GetAlbum(uuid string) (Album, error)\nfunc (v1 *V1Client) GET(endpointFormat string, a ...interface{}) *V1Response\nfunc (v1 *V1Client) GetPhotoDownload(uuid string) ([]byte, error)\nfunc (v1 *V1Client) GetPhotos(options *PhotoOptions) ([]Photo, error)\nfunc (v1 *V1Client) GetPhoto(uuid string) (Photo, error)\nfunc (v1 *V1Client) GetPhotoYaml(uuid string) ([]byte, error)\nfunc (v1 *V1Client) Index() error\nfunc (v1 *V1Client) LikeAlbum(uuid string) error\nfunc (v1 *V1Client) LikePhoto(uuid string) error\nfunc (v1 *V1Client) PhotoPrimary(uuid, fileuuid string) error\nfunc (v1 *V1Client) POST(payload interface{}, endpointFormat string, a ...interface{}) *V1Response\nfunc (v1 *V1Client) PUT(payload interface{}, endpointFormat string, a ...interface{}) *V1Response\nfunc (v1 *V1Client) SetToken(token string)\nfunc (v1 *V1Client) UpdateAlbum(album Album) (Album, error)\nfunc (v1 *V1Client) UpdatePhoto(photo Photo) (Photo, error)\n
    "},{"location":"developer-guide/api/clients/go/#example","title":"Example","text":"
    package main\n\nimport (\n    \"fmt\"\n    \"io/ioutil\"\n    \"path\"\n\n    photoprism \"github.com/kris-nova/photoprism-client-go\"\n    \"github.com/kris-nova/logger\"\n)\n\nfunc main()\n    logger.Level = 4\n\n    uuid := \"pqnzigq351j2fqgn\" // This is a known ID\n    client := photoprism.New(\"http://localhost:8080\")\n    err := client.Auth(photoprism.NewClientAuthLogin(\"admin\", \"missy\"))\n    if err != nil\n        logger.Critical(\"Error logging into API: %v\", err)\n        os.Exit(1)\n    }\n\n    // -----------------\n    // GetPhoto()\n    //\n    photo, err := client.V1().GetPhoto(uuid)\n    if err != nil\n        logger.Critical(\"Error fetching photo: %v\", err)\n        os.Exit(1)\n    }\n\n    // -----------------\n    // UpdatePhoto()\n    //\n    photo.PhotoTitle = \"A really great photo!\"\n    photo, err = client.V1().UpdatePhoto(photo)\n    if err != nil\n        logger.Critical(\"Error updating photo: %v\", err)\n        os.Exit(1)\n    }\n\n    // -----------------\n    // GetPhotoDownload()\n    //\n    file, err := client.V1().GetPhotoDownload(photo.UUID)\n    if err != nil\n        logger.Critical(\"Error getting photo download: %v\", err)\n        os.Exit(1)\n    }\n\n    for _, f := range photo.Files\n        fileName := fmt.Sprintf(\"/tmp/%s\", path.Base(f.FileName))\n        logger.Always(fileName)\n        ioutil.WriteFile(fileName, file, 0666)\n    }\n    os.Exit(0)\n}\n
    "},{"location":"developer-guide/database/","title":"Database Schema","text":"

    This schema description is for illustrative purposes only and should not be used to update or replace an existing production database.

    "},{"location":"developer-guide/database/#entity-relationship-diagram","title":"Entity-Relationship Diagram","text":"

    \u21aa docs.photoprism.app/developer-guide/database/schema/

    "},{"location":"developer-guide/database/#mermaid-markup","title":"Mermaid Markup","text":"

    With Mermaid.js you can generate visual diagrams from this markup file:

    \u21aa mariadb.mmd

    "},{"location":"developer-guide/database/#mariadb-sql-dump","title":"MariaDB SQL Dump","text":"

    An SQL schema dump can be created using the command shown below, for example:

    \u21aa mariadb.sql

    If you need an updated dump, you can run this command in your development environment:

    mariadb-dump --no-data --skip-add-locks --skip-comments \\\n --skip-opt --skip-set-charset photoprism > mariadb.sql\n

    Please note that the dump we provide is only updated at irregular intervals and should therefore not be used to update or replace an existing production database.

    "},{"location":"developer-guide/database/#schema-migrations","title":"Schema Migrations","text":"

    \u21aa docs.photoprism.app/developer-guide/database/migrations/

    \u21aa github.com/photoprism/photoprism/tree/develop/internal/migrate

    "},{"location":"developer-guide/database/migrations/","title":"Schema Migrations","text":"

    When using Docker Compose, you can prepend commands like docker compose exec [service] [command] to run them in a service container. Should this fail with no container found, make sure the service has been started, you have specified an existing service (usually photoprism) and you are in the folder where your compose.yaml or docker-compose.yml file is located.

    "},{"location":"developer-guide/database/migrations/#show-migration-status","title":"Show Migration Status","text":"

    Run the photoprism migrations ls command in a terminal to see a list of all migrations and their current status:

    $ photoprism migrations ls\n\n|-----------------|---------|---------------------|---------------------|--------|\n|       ID        | Dialect |     Started At      |     Finished At     | Status |\n|-----------------|---------|---------------------|---------------------|--------|\n| 20211121-094727 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20211124-120008 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-030000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-040000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-050000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-060000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-061000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-070000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-071000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-080000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-081000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-083000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-090000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-091000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-093000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220421-200000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220521-000001 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220521-000002 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220521-000003 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n|-----------------|---------|---------------------|---------------------|--------|\n
    "},{"location":"developer-guide/database/migrations/#run-specific-migrations","title":"Run Specific Migrations","text":"

    To explicitly re-run specific migrations, you can pass them as arguments to the photoprism migrations run command:

    $ photoprism migrations run 20220521-000003\n\nINFO[2022-07-12T11:45:29Z] migrate: 20220521-000003 successful [12.967654ms] \nINFO[2022-07-12T11:45:29Z] migration completed in 40.89123ms            \nINFO[2022-07-12T11:45:29Z] closed database connection \n
    "},{"location":"developer-guide/database/migrations/#retry-failed-migrations","title":"Retry Failed Migrations","text":"

    To automatically retry previously failed migrations, pass the -f flag to the photoprism migrations run command:

    $ photoprism migrations run -f\n
    "},{"location":"developer-guide/database/schema/","title":"Entity-Relationship Diagram","text":"

    Please keep in mind that some information may be outdated or missing, as these docs were automatically generated from the source code.

    user_uiduser_uiduser_uiduser_uidcell_idplace_idlabel_idcategory_idplace_idphoto_idphoto_idfile_idservice_idfile_idservice_idfile_uidface_idsubject_uidsubject_uidcamera_idcell_idlens_idplace_idphoto_countryalbum_countrypathcreated_byuser_uidplace_countrykeyword_idphoto_idlabel_idphoto_idalbum_uidphoto_uiduser_uidcreated_byuiduser_uidclient_uiduser_uidphoto_uidalbum_uiduser_uidalbumsdatetime created_atdatetime updated_atdatetime published_atdatetime deleted_atvarbinary(42) : album_uidvarbinary(42) : parent_uidvarbinary(160) : album_slugvarchar(1024) : album_pathvarbinary(8) : album_typevarchar(160) : album_titlevarchar(160) : album_locationvarchar(100) : album_categoryvarchar(1024) : album_captionvarchar(2048) : album_descriptionvarchar(1024) : album_notesvarbinary(2048) : album_filtervarbinary(32) : album_ordervarbinary(255) : album_templatevarchar(100) : album_statevarbinary(2) : album_countryint(11) : album_yearint(11) : album_monthint(11) : album_daytinyint(1) : album_favoritetinyint(1) : album_privatevarbinary(128) : thumbvarbinary(8) : thumb_srcvarbinary(42) : created_byint(10) : unsigned idalbums_usersvarbinary(42) : team_uidint(10) : unsigned permvarbinary(42) : uidvarbinary(42) : user_uidauth_clientsdatetime created_atdatetime updated_atdatetime expires_atdatetime deleted_atvarbinary(42) : user_uidvarchar(200) : client_namevarbinary(16) : client_typevarbinary(255) : callback_urlvarbinary(128) : auth_providervarbinary(128) : auth_methodvarchar(1024) : auth_scopebigint(20) : auth_expiresbigint(20) : auth_timeouttinyint(1) : auth_enabledbigint(20) : last_activevarbinary(16) : ref_idvarchar(64) : user_namevarbinary(255) : client_urlvarbinary(255) : auth_domainvarbinary(255) : auth_idbigint(20) : auth_tokensvarbinary(42) : client_uidauth_sessionsdatetime login_atdatetime created_atdatetime updated_atvarchar(64) : client_ipvarbinary(42) : user_uidvarchar(64) : user_namevarbinary(128) : auth_providervarbinary(128) : auth_methodvarbinary(255) : auth_domainvarbinary(128) : auth_idvarchar(1024) : auth_scopebigint(20) : last_activebigint(20) : sess_expiresbigint(20) : sess_timeoutvarbinary(64) : preview_tokenvarbinary(64) : download_tokenvarbinary(4096) : access_tokenvarbinary(512) : refresh_tokenvarbinary(1024) : id_tokenvarchar(512) : user_agentvarbinary(4096) : data_jsonvarbinary(16) : ref_idvarchar(64) : login_ipvarbinary(2048) : idauth_usersdatetime login_atdatetime expires_atdatetime verified_atdatetime consent_atdatetime born_atdatetime created_atdatetime updated_atdatetime deleted_atvarbinary(64) : user_uuidvarbinary(42) : user_uidvarbinary(128) : auth_providervarbinary(255) : auth_idvarchar(255) : user_namevarchar(200) : display_namevarchar(255) : user_emailvarchar(255) : backup_emailvarchar(64) : user_rolevarchar(1024) : user_attrtinyint(1) : super_admintinyint(1) : can_logintinyint(1) : webdavvarbinary(1024) : base_pathvarbinary(1024) : upload_pathtinyint(1) : can_invitevarbinary(64) : invite_tokenvarchar(64) : invited_byvarbinary(64) : verify_tokenvarbinary(64) : reset_tokenvarbinary(64) : preview_tokenvarbinary(64) : download_tokenvarbinary(128) : thumbvarbinary(8) : thumb_srcvarbinary(16) : ref_idint(11) : idauth_users_detailsdatetime created_atdatetime updated_atvarbinary(42) : subj_uidvarbinary(8) : subj_srcvarbinary(42) : place_idvarbinary(8) : place_srcvarbinary(42) : cell_idint(11) : birth_yearint(11) : birth_monthint(11) : birth_dayvarchar(32) : name_titlevarchar(64) : given_namevarchar(64) : middle_namevarchar(64) : family_namevarchar(32) : name_suffixvarchar(64) : nick_namevarbinary(8) : name_srcvarchar(16) : user_gendervarchar(512) : user_aboutvarchar(2048) : user_biovarchar(512) : user_locationvarbinary(2) : user_countryvarchar(32) : user_phonevarbinary(512) : site_urlvarbinary(512) : profile_urlvarbinary(512) : feed_urlvarbinary(512) : avatar_urlvarchar(64) : org_titlevarchar(128) : org_namevarchar(255) : org_emailvarchar(32) : org_phonevarbinary(512) : org_urlvarbinary(512) : id_urlvarbinary(42) : user_uidauth_users_settingsdatetime created_atdatetime updated_atvarbinary(32) : ui_themevarbinary(32) : ui_languagevarbinary(64) : ui_time_zonevarbinary(32) : maps_styleint(11) : maps_animatevarbinary(1024) : index_pathint(11) : index_rescanvarbinary(1024) : import_pathint(11) : import_moveint(11) : download_originalsint(11) : download_media_rawint(11) : download_media_sidecarvarbinary(1024) : upload_pathvarbinary(128) : default_pagevarbinary(42) : user_uidauth_users_sharesdatetime expires_atdatetime created_atdatetime updated_atvarbinary(42) : link_uidvarchar(512) : commentint(10) : unsigned permvarbinary(16) : ref_idvarbinary(42) : user_uidvarbinary(42) : share_uidcamerasdatetime created_atdatetime updated_atdatetime deleted_atvarbinary(160) : camera_slugvarchar(160) : camera_namevarchar(160) : camera_makevarchar(160) : camera_modelvarchar(100) : camera_typevarchar(2048) : camera_descriptionvarchar(1024) : camera_notesint(10) : unsigned idcategoriesint(10) : unsigned label_idint(10) : unsigned category_idcellsdatetime created_atdatetime updated_atvarchar(200) : cell_namevarchar(100) : cell_streetvarchar(50) : cell_postcodevarchar(50) : cell_categoryvarbinary(42) : place_idvarbinary(42) : idcountriesvarbinary(160) : country_slugvarchar(160) : country_namevarchar(2048) : country_descriptionvarchar(1024) : country_notesint(10) : unsigned country_photo_idvarbinary(2) : iddetailsdatetime created_atdatetime updated_atvarchar(2048) : keywordsvarbinary(8) : keywords_srcvarchar(2048) : notesvarbinary(8) : notes_srcvarchar(1024) : subjectvarbinary(8) : subject_srcvarchar(1024) : artistvarbinary(8) : artist_srcvarchar(1024) : copyrightvarbinary(8) : copyright_srcvarchar(1024) : licensevarbinary(8) : license_srcvarchar(1024) : softwarevarbinary(8) : software_srcint(10) : unsigned photo_idduplicatesvarbinary(128) : file_hashbigint(20) : file_sizebigint(20) : mod_timevarbinary(755) : file_namevarbinary(16) : file_rooterrorsdatetime error_timevarbinary(32) : error_levelvarbinary(2048) : error_messageint(10) : unsigned idfacesdouble sample_radiusdouble collision_radiusmediumblob embedding_jsondatetime matched_atdatetime created_atdatetime updated_atvarbinary(8) : face_srcint(11) : face_kindtinyint(1) : face_hiddenvarbinary(42) : subj_uidint(11) : samplesint(11) : collisionsvarbinary(64) : idfilesdatetime photo_taken_atdouble file_fpsfloat file_aspect_ratiodatetime created_atdatetime updated_atdatetime published_atdatetime deleted_atint(10) : unsigned photo_idvarbinary(42) : photo_uidvarbinary(64) : time_indexvarbinary(32) : media_idbigint(20) : media_utcvarbinary(64) : instance_idvarbinary(42) : file_uidvarbinary(1024) : file_namevarbinary(16) : file_rootvarbinary(755) : original_namevarbinary(128) : file_hashbigint(20) : file_sizevarbinary(32) : file_codecvarbinary(16) : file_typevarbinary(16) : media_typevarbinary(64) : file_mimetinyint(1) : file_primarytinyint(1) : file_sidecartinyint(1) : file_missingtinyint(1) : file_portraittinyint(1) : file_videobigint(20) : file_durationint(11) : file_framesint(11) : file_widthint(11) : file_heightint(11) : file_orientationvarbinary(8) : file_orientation_srcvarbinary(64) : file_projectiontinyint(1) : file_hdrtinyint(1) : file_watermarkvarbinary(64) : file_color_profilevarbinary(16) : file_main_colorvarbinary(18) : file_colorsvarbinary(18) : File_luminanceint(11) : file_diffsmallint(6) : file_chromavarchar(64) : file_softwarevarbinary(512) : file_errorbigint(20) : mod_timebigint(20) : created_inbigint(20) : updated_inint(10) : unsigned idfiles_sharedatetime created_atdatetime updated_atvarbinary(16) : statusvarbinary(512) : errorint(11) : errorsint(10) : unsigned file_idint(10) : unsigned service_idvarbinary(255) : remote_namefiles_syncdatetime remote_datedatetime created_atdatetime updated_atint(10) : unsigned file_idbigint(20) : remote_sizevarbinary(16) : statusvarbinary(512) : errorint(11) : errorsvarbinary(255) : remote_nameint(10) : unsigned service_idfoldersdatetime created_atdatetime updated_atdatetime modified_atdatetime published_atdatetime deleted_atvarbinary(1024) : pathvarbinary(16) : rootvarbinary(16) : folder_typevarchar(200) : folder_titlevarchar(100) : folder_categoryvarchar(2048) : folder_descriptionvarbinary(32) : folder_ordervarbinary(2) : folder_countryint(11) : folder_yearint(11) : folder_monthint(11) : folder_daytinyint(1) : folder_favoritetinyint(1) : folder_privatetinyint(1) : folder_ignoretinyint(1) : folder_watchvarbinary(42) : folder_uidkeywordsvarchar(64) : keywordtinyint(1) : skipint(10) : unsigned idlabelsdatetime created_atdatetime updated_atdatetime published_atdatetime deleted_atvarbinary(42) : label_uidvarbinary(160) : label_slugvarbinary(160) : custom_slugvarchar(160) : label_nameint(11) : label_prioritytinyint(1) : label_favoritevarchar(2048) : label_descriptionvarchar(1024) : label_notesint(11) : photo_countvarbinary(128) : thumbvarbinary(8) : thumb_srcint(10) : unsigned idlensesdatetime created_atdatetime updated_atdatetime deleted_atvarbinary(160) : lens_slugvarchar(160) : lens_namevarchar(160) : lens_makevarchar(160) : lens_modelvarchar(100) : lens_typevarchar(2048) : lens_descriptionvarchar(1024) : lens_notesint(10) : unsigned idlinksdatetime created_atdatetime modified_atvarbinary(42) : share_uidvarbinary(160) : share_slugvarbinary(160) : link_tokenint(11) : link_expiresint(10) : unsigned link_viewsint(10) : unsigned max_viewstinyint(1) : has_passwordvarchar(512) : commentint(10) : unsigned permvarbinary(16) : ref_idvarbinary(42) : created_byvarbinary(42) : link_uidmarkersdouble face_distmediumblob embeddings_jsonmediumblob landmarks_jsonfloat xfloat yfloat wfloat hdatetime matched_atdatetime created_atdatetime updated_atvarbinary(42) : file_uidvarbinary(8) : marker_typevarbinary(8) : marker_srcvarchar(160) : marker_nametinyint(1) : marker_reviewtinyint(1) : marker_invalidvarbinary(42) : subj_uidvarbinary(8) : subj_srcvarbinary(64) : face_idint(11) : qint(11) : sizesmallint(6) : scorevarbinary(128) : thumbvarbinary(42) : marker_uidpasswordsdatetime created_atdatetime updated_atvarbinary(255) : hashvarbinary(255) : uidphotosdatetime taken_atdatetime taken_at_localfloat photo_latfloat photo_lngfloat photo_f_numberdatetime created_atdatetime updated_atdatetime edited_atdatetime published_atdatetime checked_atdatetime estimated_atdatetime deleted_atvarbinary(64) : uuidvarbinary(8) : taken_srcvarbinary(42) : photo_uidvarbinary(8) : photo_typevarbinary(8) : type_srcvarchar(200) : photo_titlevarbinary(8) : title_srcvarchar(4096) : photo_descriptionvarbinary(8) : description_srcvarbinary(1024) : photo_pathvarbinary(255) : photo_namevarbinary(755) : original_nametinyint(4) : photo_stacktinyint(1) : photo_favoritetinyint(1) : photo_privatetinyint(1) : photo_scantinyint(1) : photo_panoramavarbinary(64) : time_zonevarbinary(42) : place_idvarbinary(8) : place_srcvarbinary(42) : cell_idint(11) : cell_accuracyint(11) : photo_altitudevarbinary(2) : photo_countryint(11) : photo_yearint(11) : photo_monthint(11) : photo_dayint(11) : photo_isovarbinary(64) : photo_exposureint(11) : photo_focal_lengthsmallint(6) : photo_qualityint(11) : photo_facessmallint(6) : photo_resolutionbigint(20) : photo_durationsmallint(6) : photo_colorint(10) : unsigned camera_idvarbinary(160) : camera_serialvarbinary(8) : camera_srcint(10) : unsigned lens_idvarbinary(42) : created_byint(10) : unsigned idphotos_albumsdatetime created_atdatetime updated_atint(11) : ordertinyint(1) : hiddentinyint(1) : missingvarbinary(42) : photo_uidvarbinary(42) : album_uidphotos_keywordsint(10) : unsigned photo_idint(10) : unsigned keyword_idphotos_labelsvarbinary(8) : label_srcsmallint(6) : uncertaintyint(10) : unsigned photo_idint(10) : unsigned label_idphotos_usersvarbinary(42) : team_uidint(10) : unsigned permvarbinary(42) : uidvarbinary(42) : user_uidplacesdatetime created_atdatetime updated_atvarchar(400) : place_labelvarchar(100) : place_districtvarchar(100) : place_cityvarchar(100) : place_statevarbinary(2) : place_countryvarchar(300) : place_keywordstinyint(1) : place_favoriteint(11) : photo_countvarbinary(42) : idreactionsdatetime reacted_atint(11) : reactedvarbinary(42) : uidvarbinary(42) : user_uidvarbinary(64) : reactionservicesdatetime sync_datedatetime created_atdatetime updated_atdatetime deleted_atvarchar(160) : acc_namevarchar(160) : acc_ownervarchar(255) : acc_urlvarbinary(255) : acc_typevarbinary(255) : acc_keyvarbinary(255) : acc_uservarbinary(255) : acc_passvarbinary(16) : acc_timeoutvarbinary(512) : acc_errorint(11) : acc_errorstinyint(1) : acc_sharetinyint(1) : acc_syncint(11) : retry_limitvarbinary(1024) : share_pathvarbinary(16) : share_sizeint(11) : share_expiresvarbinary(1024) : sync_pathvarbinary(16) : sync_statusint(11) : sync_intervaltinyint(1) : sync_uploadtinyint(1) : sync_downloadtinyint(1) : sync_filenamestinyint(1) : sync_rawint(10) : unsigned idsubjectsdatetime created_atdatetime updated_atdatetime deleted_atvarbinary(8) : subj_typevarbinary(8) : subj_srcvarbinary(160) : subj_slugvarchar(160) : subj_namevarchar(160) : subj_aliasvarchar(512) : subj_aboutvarchar(2048) : subj_biovarchar(1024) : subj_notestinyint(1) : subj_favoritetinyint(1) : subj_hiddentinyint(1) : subj_privatetinyint(1) : subj_excludedint(11) : file_countint(11) : photo_countvarbinary(128) : thumbvarbinary(8) : thumb_srcvarbinary(42) : subj_uidusers"},{"location":"developer-guide/machine-learning/classification/","title":"Image Classification","text":"

    Image classification is performed using a pre-trained model, NASNet Mobile 224, that we have chosen because of its size, performance and accuracy. To get a basic understanding of how this works, you can read Image Classification using Deep Neural Networks.

    In addition, we manually matched the model classification with the labels you see in our UI:

    cat:\n  label: cat\n  threshold: 0.3\n  priority: 5\n  categories:\n    - animal\n\ntabby cat:\n  see: cat\n

    This was necessary because we didn't find a taxonomy suitable for consumers (mainly just scientific ones) and needed a lot of control to fine tune terms and their probability thresholds. The raw results were not useful to a typical user. Indexing too many words, categories and alternatives also negatively affects performance and leads to noise.

    It took us several months of testing until we were happy with the results and there are still labels to improve.

    "},{"location":"developer-guide/machine-learning/classification/#updating-labels","title":"Updating labels","text":"

    After editing or adding labels in rules.yml, you now have to run make generate in the main project directory to generate native Go source from this file.

    "},{"location":"developer-guide/machine-learning/classification/#pre-trained-models","title":"Pre-trained Models","text":"

    See also: TensorFlow Hub

    Source: https://github.com/tensorflow/models/blob/master/research/slim/README.md

    Neural nets work best when they have many parameters, making them powerful function approximators. However, this means they must be trained on very large datasets. Because training models from scratch can be a very computationally intensive process requiring days or even weeks, there are various pre-trained models available. These CNNs have been trained on the ILSVRC-2012-CLS image classification dataset.

    Note that the VGG and ResNet V1 parameters have been converted from their original caffe formats (here and here), whereas the Inception and ResNet V2 parameters have been trained internally at Google. Also be aware that these accuracies were computed by evaluating using a single image crop. PhotoPrism uses three crops, except for square images.

    Model TF-Slim File Checkpoint Top-1 Accuracy Top-5 Accuracy Inception V1 Code inception_v1_2016_08_28.tar.gz 69.8 89.6 Inception V2 Code inception_v2_2016_08_28.tar.gz 73.9 91.8 Inception V3 Code inception_v3_2016_08_28.tar.gz 78.0 93.9 Inception V4 Code inception_v4_2016_09_09.tar.gz 80.2 95.2 Inception-ResNet-v2 Code inception_resnet_v2_2016_08_30.tar.gz 80.4 95.3 ResNet V1 50 Code resnet_v1_50_2016_08_28.tar.gz 75.2 92.2 ResNet V1 101 Code resnet_v1_101_2016_08_28.tar.gz 76.4 92.9 ResNet V1 152 Code resnet_v1_152_2016_08_28.tar.gz 76.8 93.2 ResNet V2 50^ Code resnet_v2_50_2017_04_14.tar.gz 75.6 92.8 ResNet V2 101^ Code resnet_v2_101_2017_04_14.tar.gz 77.0 93.7 ResNet V2 152^ Code resnet_v2_152_2017_04_14.tar.gz 77.8 94.1 ResNet V2 200 Code TBA 79.9* 95.2* VGG 16 Code vgg_16_2016_08_28.tar.gz 71.5 89.8 VGG 19 Code vgg_19_2016_08_28.tar.gz 71.1 89.8 MobileNet_v1_1.0_224 Code mobilenet_v1_1.0_224.tgz 70.9 89.9 MobileNet_v1_0.50_160 Code mobilenet_v1_0.50_160.tgz 59.1 81.9 MobileNet_v1_0.25_128 Code mobilenet_v1_0.25_128.tgz 41.5 66.3 MobileNet_v2_1.4_224^* Code mobilenet_v2_1.4_224.tgz 74.9 92.5 MobileNet_v2_1.0_224^* Code mobilenet_v2_1.0_224.tgz 71.9 91.0 NASNet-A_Mobile_224# Code nasnet-a_mobile_04_10_2017.tar.gz 74.0 91.6 NASNet-A_Large_331# Code nasnet-a_large_04_10_2017.tar.gz 82.7 96.2 PNASNet-5_Large_331 Code pnasnet-5_large_2017_12_13.tar.gz 82.9 96.2 PNASNet-5_Mobile_224 Code pnasnet-5_mobile_2017_12_13.tar.gz 74.2 91.9

    ^ ResNet V2 models use Inception pre-processing and input image size of 299 (use --preprocessing_name inception --eval_image_size 299 when using eval_image_classifier.py). Performance numbers for ResNet V2 models are reported on the ImageNet validation set.

    (#) More information and details about the NASNet architectures are available at this README

    All 16 float MobileNet V1 models reported in the MobileNet Paper and all 16 quantized TensorFlow Lite compatible MobileNet V1 models can be found here.

    (^#) More details on MobileNetV2 models can be found here.

    (*): Results quoted from the paper.

    Here is an example of how to download the Inception V3 checkpoint:

    $ CHECKPOINT_DIR=/tmp/checkpoints\n$ mkdir ${CHECKPOINT_DIR}\n$ wget http://download.tensorflow.org/models/inception_v3_2016_08_28.tar.gz\n$ tar -xvf inception_v3_2016_08_28.tar.gz\n$ mv inception_v3.ckpt ${CHECKPOINT_DIR}\n$ rm inception_v3_2016_08_28.tar.gz\n
    "},{"location":"developer-guide/machine-learning/classification/#landmark-detection","title":"Landmark detection","text":"

    DELF: DEep Local Features - https://github.com/tensorflow/models/tree/master/research/delf - Tensorflow implementation

    Source: https://gitcdn.xyz/cdn/Tony607/blog_statics/ce9c3391932e24655b78e27a54543f28f11f3af0/images/landmark/query.jpg

    "},{"location":"developer-guide/machine-learning/classification/#types-of-neural-networks","title":"Types of neural networks","text":"

    Source: http://www.asimovinstitute.org/neural-network-zoo/

    "},{"location":"developer-guide/machine-learning/classification/#external-resources","title":"External Resources","text":"
    • https://ai.facebook.com/blog/roberta-an-optimized-method-for-pretraining-self-supervised-nlp-systems/ - An optimized method for pretraining self-supervised NLP systems
    • zihangdai/xlnet - Generalized Autoregressive Pretraining for Language Understanding
    • https://pjreddie.com/darknet/yolo/ - real time image detection
    • https://pjreddie.com/darknet/imagenet/ - using use Darknet to classify images
    • ZanLabs/go-yolo - Golang binding for YOLO/Darknet recognition framework
    • https://itnext.io/implementing-yolo-v3-in-tensorflow-tf-slim-c3c55ff59dbe - Implementing YOLO v3 in Tensorflow (TF-Slim)
    • chewxy/lingo - provides the data structures and algorithms required for natural language processing
    • https://modelzoo.co/ - Discover open source deep learning code and pretrained models
    • https://polarr.ai/ - Efficient and Immersive C.V. experiences on the edge
    • gildasch/gildas-ai
    • https://www.tensorflow.org/lite/guide/hosted_models
    • https://github.com/tensorflow/models/tree/master/research/slim/nets/nasnet
    • https://www.wikidata.org/wiki/Wikidata:Database_download
    • ropensci/wikitaxa
    • https://datahub.io/collections/yago - YAGO3 is a huge semantic knowledge base, derived from Wikipedia WordNet and GeoNames
    • Google AI Blog: Improving Inception and Image Classification in TensorFlow
    • CNN Architectures: LeNet, AlexNet, VGG, GoogLeNet, ResNet and more
    • Gildas Chabot - AI image search with Go & Tensorflow (slides)
    "},{"location":"developer-guide/machine-learning/face-recognition/","title":"Face Recognition","text":"

    To recognize faces, PhotoPrism first extracts crops from your images using the Pigo face detection library. It is based on pixel intensity comparisons.

    These are then fed into TensorFlow to compute 512-dimensional vectors for characterization.

    In the final step, the DBSCAN algorithm attempts to cluster these so-called face embeddings so that they can be assigned to people with a few clicks.

    "},{"location":"developer-guide/machine-learning/face-recognition/#configuration","title":"Configuration","text":"

    We recommend that only advanced users and developers change these parameters.

    Environment CLI Flag Default Description PHOTOPRISM_FACE_SIZE --face-size 50 minimum size of faces in PIXELS (20-10000) PHOTOPRISM_FACE_SCORE --face-score 9.000000 minimum face QUALITY score (1-100) PHOTOPRISM_FACE_OVERLAP --face-overlap 42 face area overlap threshold in PERCENT (1-100) PHOTOPRISM_FACE_CLUSTER_SIZE --face-cluster-size 80 minimum size of automatically clustered faces in PIXELS (20-10000)\u2002members only PHOTOPRISM_FACE_CLUSTER_SCORE --face-cluster-score 15 minimum QUALITY score of automatically clustered faces (1-100)\u2002members only PHOTOPRISM_FACE_CLUSTER_CORE --face-cluster-core 4 NUMBER of faces forming a cluster core (1-100)\u2002members only PHOTOPRISM_FACE_CLUSTER_DIST --face-cluster-dist 0.640000 similarity DISTANCE of faces forming a cluster core (0.1-1.5)\u2002members only PHOTOPRISM_FACE_MATCH_DIST --face-match-dist 0.460000 similarity OFFSET for matching faces with existing clusters (0.1-1.5)\u2002members only
    • A reasonable range for the similarity distance between face embeddings is between 0.60 and 0.70, with a higher value being more aggressive and leading to larger clusters with more false positives.
    • To cluster a smaller number of faces, you can reduce the kernel to 3 or 2 similar faces.
    "},{"location":"developer-guide/machine-learning/face-recognition/#external-resources","title":"External Resources","text":"
    • https://cheppers.com/deploying-distributed-face-recognition-application-kubernetes - Deploying a distributed face-recognition application with Kubernetes
    • http://dlib.net/ - Machine learning library, good for face recognition
    • Kagami/go-face - implements face recognition for Go using dlib
    • https://hackernoon.com/face-recognition-with-go-676a555b8a7e - face recognition howto using Kagami/go-face
    • esimov/pigo - Pigo is a face detection library implemented in Go
    • https://gocv.io/ - GoCV gives access to the OpenCV 4 computer vision library
    • davidsandberg/facenet - Face Recognition using Tensorflow (Python, not Go)
    "},{"location":"developer-guide/machine-learning/models/","title":"Additional Computer Vision Models","text":"

    Our photoprism/photoprism-vision repository on GitHub provides supplementary computer vision models which can be accessed as web services by PhotoPrism and other applications. Their RESTful API accepts an image URL and returns, for example, a suitable caption in response.

    Note that this has not been officially released yet. Further documentation and models will be added over time.

    "},{"location":"developer-guide/machine-learning/models/#models","title":"Models","text":"

    The currently integrated models, each with its own endpoint, are kosmos-2, vit-gpt2-image-captioning, and blip-image-captioning large:

    "},{"location":"developer-guide/machine-learning/models/#kosmos-2","title":"Kosmos-2","text":"

    Komsos-2 is the most accurate model of the three. It was developed by Microsoft, and this application uses the transformers implementation of the original model, as described in its Huggingface. This model was released in June 2023, and offers object detection and spatial reasoning. Kosmos-2 has very accurate accurate image captions (a .04-.1 increase in clip score when compared to the other two models offered), and is the default model used.

    "},{"location":"developer-guide/machine-learning/models/#vit-gpt2","title":"VIT-GPT2","text":"

    This model was released by nlpconnect. This model combined VIT and GPT-2 to create a multi-modal image captioning model. I have found this to be the least performing of the three, but your mileage may vary.

    "},{"location":"developer-guide/machine-learning/models/#blip","title":"BLIP","text":"

    This model was released by Salesforce in 2022. The primary purpose for this model was to increase both image understanding and text generation using novel techniques. It has achieved a +2.8% CIDEr result, and I've found this model to be more performant than VIT-GPT2, but Kosmos-2 to be slightly better (a .4 increase in CLIP score).

    "},{"location":"developer-guide/machine-learning/models/#dependencies","title":"Dependencies","text":""},{"location":"developer-guide/machine-learning/models/#flask","title":"Flask","text":"

    Flask is the framework that is used for the API. It allows for API creation with Python, which is key for this application as it utilizes ML.

    "},{"location":"developer-guide/machine-learning/models/#pytorch","title":"PyTorch","text":"

    PyTorch is key for working with the ML models to generate the outputs. It also enables GPU processing, speeding up the image processing with the models. PyTorch primarily creates and handles tensors, which are crucial for the function of the models.

    "},{"location":"developer-guide/machine-learning/models/#transformers","title":"Transformers","text":"

    Transformers is used for downloading and loading the models. In addition to this it is used in the image processing with the models.

    "},{"location":"developer-guide/machine-learning/models/#pillow","title":"Pillow","text":"

    Pillow is used to take the supplied URl and convert it into the format needed to input into the models.

    "},{"location":"developer-guide/machine-learning/models/#hardware-acceleration-libraries","title":"Hardware Acceleration Libraries","text":"

    Numpy could be used for further hardware acceleration. It isn't included in the application by default to save space and keep from installing unnecessary dependencies. Numpy can be configured to use the GPU for computations. PyTorch already enables GPU processing, so numpy may not make a signficant difference.

    "},{"location":"developer-guide/machine-learning/models/#build-setup","title":"Build Setup","text":"

    Before installing the Python dependencies, please make sure that you have Git and Python 3.12+ (incl. pip) installed on your system, e.g. by running the following command on Ubuntu/Debian Linux:

    sudo apt-get install -y git python3 python3-pip python3-venv python3-wheel\n

    You can then install the required libraries in a virtual environment by either using the Makefiles we provide (i.e. run make in the main project directory or a subdirectory) or by manually running the following commands in a service directory, for example:

    git clone git@github.com:photoprism/photoprism-vision.git\ncd photoprism-vision/describe\npython3 -m venv ./venv\n. ./venv/bin/activate\n./venv/bin/pip install --disable-pip-version-check --upgrade pip\n./venv/bin/pip install --disable-pip-version-check -r requirements.txt\n
    "},{"location":"developer-guide/machine-learning/models/#usage","title":"Usage","text":"

    Run the Python file app.py in the describe subdirectory to start the describe service after you have installed the dependencies (more services, e.g. for OCR and tag generation, may follow):

    ./venv/bin/python app.py\n

    The service then listens on port 5000 by default and its API endpoints for generating captions support both GET and POST requests. It can be tested with the curl command (curl.exe on Windows) as shown in the example below:

    curl -v -H \"Content-Type: application/json\" \\\n  --data '{\"url\":\"https://dl.photoprism.app/img/team/avatar.jpg\"}' \\\n  -X POST http://localhost:5000/api/v1/vision/describe\n

    At a minimum, a valid image url must be specified for this. In addition, a model name and an arbitrary id can be passed. The API will return the same id in the response. If no id is passed, a randomly generated UUID will be returned instead.

    If your client submits POST requests, the request body must be JSON-encoded, e.g.:

    {\n    \"id\": \"3487da77-246e-4b4c-9437-67507177bcd7\",\n    \"url\": \"https://dl.photoprism.app/img/team/avatar.jpg\"\n}\n

    Alternatively, you can perform GET requests with URL-encoded query parameters, which is easier to test without an HTTP client:

    http://localhost:5000/api/v1/vision/describe?url=https%3A%2F%2Fdl.photoprism.app%2Fimg%2Fteam%2Favatar.jpg&id=3487da77-246e-4b4c-9437-67507177bcd7

    "},{"location":"developer-guide/machine-learning/models/#api-endpoints","title":"API Endpoints","text":""},{"location":"developer-guide/machine-learning/models/#apiv1visiondescribe","title":"/api/v1/vision/describe","text":"

    This is the default endpoint of the API. An image url should be passed in with the key \"url\", and optionally a \"model\" and/or \"id\" value can be passed in. The \"model\" key allows the user to specify which of the three models they would like to use. If no model is given, the application will default to using the kosmos-2 model.

    "},{"location":"developer-guide/machine-learning/models/#apiv1visiondescribekosmos-2patch14-224","title":"/api/v1/vision/describe/kosmos-2/patch14-224","text":"

    This is the endpoint for the Kosmos-2 model. An image url should be passed in with the key \"url\", and optionally a \"model\" and/or \"id\" value can be passed in.

    "},{"location":"developer-guide/machine-learning/models/#apiv1visiondescribevit-gpt2-image-captioning","title":"/api/v1/vision/describe/vit-gpt2-image-captioning","text":"

    This is the endpoint for the VIT GPT-2 model. An image url should be passed in with the key \"url\", and optionally an \"id\" value can be passed in.

    "},{"location":"developer-guide/machine-learning/models/#apiv1visiondescribeblip-image-captioning-large","title":"/api/v1/vision/describe/blip-image-captioning-large","text":"

    This is the endpoint for the BLIP model. An image url should be passed in with the key \"url\", and an \"id\" value can be passed in.

    "},{"location":"developer-guide/machine-learning/models/#example-request","title":"Example Request","text":"

    POST /api/v1/vision/describe

    {\n    \"id\": \"b0db2187-7a09-438c-8649-a9c6c0f7b8a1\",\n    \"model\": \"kosmos-2\"\n    \"url\": \"https://dl.photoprism.app/img/team/avatar.jpg\",\n}\n
    "},{"location":"developer-guide/machine-learning/models/#example-response","title":"Example Response","text":"
    {\n    \"id\": \"b0db2187-7a09-438c-8649-a9c6c0f7b8a1\",\n    \"model\": {\n        \"name\": \"kosmos-2\",\n        \"version\": \"patch14-224\"\n    },\n    \"result\": {\n        \"caption\": \"An image of a man in a suit smiling.\"\n    }\n}\n
    "},{"location":"developer-guide/machine-learning/models/#code-structure","title":"Code Structure","text":""},{"location":"developer-guide/machine-learning/models/#model-loading-and-initialization","title":"Model Loading and Initialization","text":"
    MODEL_DIR = \"models\"\nKOSMOS_MODEL_PATH = os.path.join(MODEL_DIR, \"kosmos-2-patch14-224\")\nVIT_MODEL_PATH = os.path.join(MODEL_DIR, \"vit-gpt2-image-captioning\")\nBLIP_MODEL_PATH = os.path.join(MODEL_DIR, \"blip-image-captioning-large\")\n

    This code block creates the paths for the models. This will be useful when downloading/loading the models. It uses os.path to assemble the correct path depending on if the system is Windows-based or UNIX-based.

    "},{"location":"developer-guide/machine-learning/models/#downloading-models","title":"Downloading Models","text":"
    def download_model(model_name, save_path):\n    if not os.path.exists(save_path):\n        print(f\"Downloading {model_name}...\")\n        if model_name == \"microsoft/kosmos-2-patch14-224\":\n            AutoModelForVision2Seq.from_pretrained(model_name).save_pretrained(save_path)\n            AutoProcessor.from_pretrained(model_name).save_pretrained(save_path)\n        elif model_name == \"nlpconnect/vit-gpt2-image-captioning\":\n            VisionEncoderDecoderModel.from_pretrained(model_name).save_pretrained(save_path)\n            ViTImageProcessor.from_pretrained(model_name).save_pretrained(save_path)\n            AutoTokenizer.from_pretrained(model_name).save_pretrained(save_path)\n        elif model_name == \"Salesforce/blip-image-captioning-large\":\n            BlipForConditionalGeneration.from_pretrained(model_name).save_pretrained(save_path)\n            BlipProcessor.from_pretrained(model_name).save_pretrained(save_path)\n        print(f\"{model_name} downloaded and saved to {save_path}\")\n    else:\n        print(f\"{model_name} already exists at {save_path}\")\n

    Here the code is checking if the models already exist or not. If they don't exist it is downloading them, if they do it is skipping the downloading.

    os.makedirs(MODEL_DIR, exist_ok=True)\ndownload_model(\"microsoft/kosmos-2-patch14-224\", KOSMOS_MODEL_PATH)\ndownload_model(\"nlpconnect/vit-gpt2-image-captioning\", VIT_MODEL_PATH)\ndownload_model(\"Salesforce/blip-image-captioning-large\", BLIP_MODEL_PATH)\n

    Here the code is downloading the models by calling the function in the previous block.

    "},{"location":"developer-guide/machine-learning/models/#loading-models","title":"Loading Models","text":"
    print(\"Loading models...\")\nkosmosModel = AutoModelForVision2Seq.from_pretrained(KOSMOS_MODEL_PATH)\nkosmosProcessor = AutoProcessor.from_pretrained(KOSMOS_MODEL_PATH)\n\nvitModel = VisionEncoderDecoderModel.from_pretrained(VIT_MODEL_PATH)\nvitFeature_extractor = ViTImageProcessor.from_pretrained(VIT_MODEL_PATH)\nvitTokenizer = AutoTokenizer.from_pretrained(VIT_MODEL_PATH)\n\nblipProcessor = BlipProcessor.from_pretrained(BLIP_MODEL_PATH)\nblipModel = BlipForConditionalGeneration.from_pretrained(BLIP_MODEL_PATH)\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nvitModel.to(device)\n

    Here the models are being loaded after they have been saved.

    "},{"location":"developer-guide/machine-learning/models/#services","title":"Services","text":"
    def kosmosGenerateResponse(url):\n    try:\n        image = Image.open(requests.get(url, stream=True).raw)\n    except Exception as e:\n        return \"fetchError\", f\"Unable to fetch image: {str(e)}\"\n\n    prompt = \"<grounding>An image of\"\n\n    try:\n        inputs = kosmosProcessor(text=prompt, images=image, return_tensors=\"pt\")\n        generated_ids = kosmosModel.generate(\n            pixel_values=inputs[\"pixel_values\"],\n            input_ids=inputs[\"input_ids\"],\n            attention_mask=inputs[\"attention_mask\"],\n            image_embeds=None,\n            image_embeds_position_mask=inputs[\"image_embeds_position_mask\"],\n            use_cache=True,\n            max_new_tokens=128,\n        )\n\n        generated_text = kosmosProcessor.batch_decode(generated_ids, skip_special_tokens=True)[0]\n        processed_text, entities = kosmosProcessor.post_process_generation(generated_text)\n    except Exception as e:\n        return \"processingError\", f\"Error during processing: {str(e)}\"\n\n    return \"ok\", processed_text\n\ndef vitGenerateResponse(url):\n    vitModel.to(device)    \n\n    max_length = 16\n    num_beams = 4\n    gen_kwargs = {\"max_length\": max_length, \"num_beams\": num_beams}\n\n    def predict_step(url):\n        image = Image.open(requests.get(url, stream=True).raw)\n        images = []\n\n        if image.mode != \"RGB\":\n            image = image.convert(mode=\"RGB\")\n\n        images.append(image)\n\n        pixel_values = vitFeature_extractor(images=images, return_tensors=\"pt\").pixel_values\n        pixel_values = pixel_values.to(device)\n\n        output_ids = vitModel.generate(pixel_values, **gen_kwargs)\n\n        preds = vitTokenizer.batch_decode(output_ids, skip_special_tokens=True)\n        preds = [pred.strip() for pred in preds]\n        return preds\n\n    processed_text = predict_step(url)  # returns prediction\n\n    return \"ok\", processed_text\n\ndef blipGenerateResponse(url):\n    img_url = url\n    raw_image = Image.open(requests.get(img_url, stream=True).raw).convert('RGB')\n\n    inputs = blipProcessor(raw_image, return_tensors=\"pt\")\n\n    out = blipModel.generate(**inputs)\n    processed_text = blipProcessor.decode(out[0], skip_special_tokens=True)\n\n    return \"ok\", processed_text\n

    These are the services to generate the captions. There is a function for each model.

    "},{"location":"developer-guide/machine-learning/models/#api-endpoints_1","title":"API Endpoints","text":""},{"location":"developer-guide/machine-learning/models/#default-endpoint","title":"Default Endpoint","text":"
    @app.route('/api/v1/vision/describe', methods=['POST', 'GET'])\ndef generateResponse():\n    if request.method == 'POST':\n        if not request.is_json:\n            return jsonify({\"error\": \"Request must be JSON\"}), 400\n        data = request.get_json()\n    elif request.method == 'GET':\n        data = request.args\n\n    url = data.get('url')\n    model = data.get('model')\n    id = data.get('id')\n\n    if not url:\n        return jsonify({\"error\": \"URL is required\"}), 400\n\n    if model == \"kosmos-2\" or not model:\n        status, result = kosmosGenerateResponse(url)\n        if status == \"fetchError\":\n            return jsonify({\"error\": result}), 500\n        elif status == \"processingError\":\n            return jsonify({\"error\": result}), 500\n        elif status == \"ok\":\n            if id:\n                return jsonify({\"id\": id, \"result\": {\"caption\": result}, \"model\": {\"name\": \"kosmos-2\", \"version\": \"patch14-224\"}}), 200\n            return jsonify({\"id\": uuid.uuid4(), \"result\": {\"caption\": result}, \"model\": {\"name\": \"kosmos-2\", \"version\": \"patch14-224\"}}), 200\n    elif model == \"vit-gpt2-image-captioning\":\n        status, result = vitGenerateResponse(url)\n        if status == \"ok\":\n            if id:\n                return jsonify({\"id\": id, \"result\": {\"caption\": result}, \"model\": {\"name\": model, \"version\": \"latest\"}}), 200\n            return jsonify({\"id\": uuid.uuid4(), \"result\": {\"caption\": result}, \"model\": {\"name\": model, \"version\": \"latest\"}}), 200\n        return jsonify({\"error\": \"Error during processing\"})\n    elif model == \"blip-image-captioning-large\":\n        status, result = blipGenerateResponse(url)\n        if status =='ok':\n            if id:\n                return jsonify({\"id\": id, \"result\": {\"caption\": result}, \"model\": {\"name\": model, \"version\": \"latest\"}}), 200\n            return jsonify({\"id\": uuid.uuid4(), \"result\": {\"caption\": result}, \"model\": {\"name\": model, \"version\": \"latest\"}}), 200\n        return jsonify({\"error\": \"Error during processing\"})\n

    This is the default endpoint. It checks to see if a model is specified, and if it is it calls the service associated with that model and returns the respose with the data. If a model isn't specified it uses kosmos-2.

    "},{"location":"developer-guide/machine-learning/models/#specific-endpoints","title":"Specific Endpoints","text":"
    @app.route('/api/v1/vision/describe/kosmos-2/patch14-224', methods=['POST', 'GET'])\ndef kosmosController():\n    if request.method == 'POST':\n        if not request.is_json:\n            return jsonify({\"error\": \"Request must be JSON\"}), 400\n        data = request.get_json()\n    elif request.method == 'GET':\n        data = request.args\n\n    url = data.get('url')\n    id = data.get('id')\n\n    if not url:\n        return jsonify({\"error\": \"URL is required\"}), 400\n\n    status, result = kosmosGenerateResponse(url)\n\n    if status == \"fetchError\":\n        return jsonify({\"error\": result}), 500\n    elif status == \"processingError\":\n        return jsonify({\"error\": result}), 500\n    elif status == \"ok\":\n        if id:\n            return jsonify({\"id\": id, \"result\": {\"caption\": result}, \"model\": {\"name\": \"kosmos-2\", \"version\": \"patch14-224\"}}), 200\n        return jsonify({\"id\": uuid.uuid4(), \"result\": {\"caption\": result}, \"model\": {\"name\": \"kosmos-2\", \"version\": \"patch14-224\"}}), 200\n\n\n\n\n@app.route('/api/v1/vision/describe/vit-gpt2-image-captioning', methods=['POST', 'GET'])\ndef vitController():\n    if request.method == 'POST':\n        if not request.is_json:\n            return jsonify({\"error\": \"Request must be JSON\"}), 400\n        data = request.get_json()\n    elif request.method == 'GET':\n        data = request.args\n\n    url = data.get('url')\n    id = data.get('id')\n\n    if not url:\n        return jsonify({\"error\": \"URL is required\"}), 400\n\n    status, result = vitGenerateResponse(url)\n\n    if status == \"ok\":\n        if id:\n            return jsonify({\"id\": id, \"result\": {\"caption\": result}, \"model\": {\"name\": \"vit-gpt2-image-captioning\", \"version\": \"latest\"}}), 200\n        return jsonify({\"id\": uuid.uuid4(), \"result\": {\"caption\": result}, \"model\": {\"name\": \"vit-gpt2-image-captioning\", \"version\": \"latest\"}}), 200\n\n    return jsonify({\"error\": \"Error during processing\"})\n\n\n\n@app.route('/api/v1/vision/describe/blip-image-captioning-large', methods=['POST', 'GET'])\ndef blipController():\n    if request.method == 'POST':\n        if not request.is_json:\n            return jsonify({\"error\": \"Request must be JSON\"}), 400\n        data = request.get_json()\n    elif request.method == 'GET':\n        data = request.args\n\n    url = data.get('url')\n    id = data.get('id')\n\n    if not url:\n        return jsonify({\"error\": \"URL is required\"}), 400\n\n    status, result = blipGenerateResponse(url)\n\n    if status == \"ok\":\n        if id:\n            return jsonify({\"id\": id, \"result\": {\"caption\": result}, \"model\": {\"name\": \"vit-gpt2-image-captioning\", \"version\": \"latest\"}}), 200\n        return jsonify({\"id\": uuid.uuid4(), \"result\": {\"caption\": result}, \"model\": {\"name\": \"vit-gpt2-image-captioning\", \"version\": \"latest\"}}), 200\n\n    return jsonify({\"error\", \"Error during processing\"})\n

    These are the endpoints for each model. They do some error handling, run the service, and return the response.

    "},{"location":"developer-guide/machine-learning/models/#contributors","title":"Contributors","text":"

    We would like to thank everyone involved, especially Aatif Dawawala who got things rolling and contributed much of the initial code:

    • Aatif Dawawala
    • Niaz Faridani-Rad

    Learn more \u203a

    "},{"location":"developer-guide/machine-learning/models/#submitting-pull-requests","title":"Submitting Pull Requests","text":"

    Follow our step-by-step guide to learn how to submit new features, bug fixes, and documentation enhancements.

    Learn more \u203a

    "},{"location":"developer-guide/machine-learning/models/#license-and-disclaimer","title":"License and Disclaimer","text":"

    The files in the photoprism/photoprism-vision repository are licensed under the Apache License, Version 2.0 (the \u201cLicense\u201d).

    Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

    Learn more \u203a

    "},{"location":"developer-guide/media/","title":"Supported Media and Sidecar File Formats","text":"Format Description Type Extensions AVIF AV1 Image File Format Image .avif AVIFS AV1 Image Sequence Image .avifs, .avis BMP Bitmap Image .bmp GIF Graphics Interchange Format Image .gif HEIC High Efficiency Image Container Image .avci, .avcs, .heic, .heif, .hif HEICS HEIC Image Sequence Image .heics, .heifs JPG Joint Photographic Experts Group (JPEG) Image .jfi, .jfif, .jif, .jpe, .jpeg, .jpg JXL JPEG XL Image .jxl MPO Stereoscopic JPEG (3D) Image .mpo PNG Portable Network Graphics Image .apng, .pn, .png, .pnga PSD Adobe Photoshop Image .psd THM Thumbnail Image Image .thm TIFF Tag Image File Format Image .tif, .tiff WEBP Google WebP Image .webp DNG Adobe Digital Negative Raw .dng RAW Unprocessed Sensor Data Raw .3fr, .ari, .arw, .bay, .cap, .cr2, .cr3, .crw, .data, .dcr, .dcs, .drf, .eip, .erf, .fff, .gpr, .iiq, .k25, .kdc, .mdc, .mef, .mos, .mrw, .nef, .nrw, .obm, .orf, .pef, .ptx, .pxn, .r3d, .raf, .raw, .rw2, .rwl, .rwz, .sr2, .srf, .srw, .x3f AAE Apple Image Edits XML Sidecar .aae JSON Serialized JSON Data (Exiftool, Google Photos) Sidecar .json MD Markdown Formatted Text Sidecar .markdown, .md NFO Info Text Sidecar .nfo TXT Plain Text Sidecar .txt XML Extensible Markup Language Sidecar .xml XMP Adobe Extensible Metadata Platform Sidecar .xmp YML Serialized YAML Data (Config, Metadata) Sidecar .yaml, .yml AI Adobe Illustrator Vector .ai EPS Encapsulated PostScript Vector .epi, .eps, .eps2, .eps3, .epsf, .epsi, .ept PS Adobe PostScript Vector .ps, .ps2, .ps3 SVG Scalable Vector Graphics Vector .svg 3G2 Mobile Multimedia Container (CDMA2000) Video .3g2 3GP Mobile Multimedia Container (3G) Video .3gp ASF Advanced Systems Format Video .asf AV1 AOMedia Video 1 Video .av1 AVC Advanced Video Coding (H.264, MPEG-4 Part 10) Video .avc AVI Microsoft Audio Video Interleave Video .avi DV DV Video Video .dv EVC Essential Video Coding (MPEG-5 Part 1) Video .evc FLV Adobe Flash Video .f4v, .flv HEVC High Efficiency Video Coding (H.265) Video .hevc M2TS Blu-ray MPEG-2 Transport Stream Video .m2ts M4V Apple iTunes Multimedia Container Video .m4v MJPG Motion JPEG Video .mjpeg, .mjpg MKV Matroska Multimedia Container Video .mkv MOV Apple QuickTime Video .mov, .qt MP2 MPEG 2 (H.262) Video .mp2, .mpv MP4 Multimedia Container (MPEG-4 Part 14) Video .mp, .mp4 MPG Moving Picture Experts Group (MPEG) Video .mpeg, .mpg MTS Advanced Video Coding High Definition (AVCHD) Video .mts MXF Material Exchange Format Video .mxf OGV Ogg Media (OGG) Video .ogg, .ogv, .ogx VVC Versatile Video Coding (H.266) Video .vvc WEBM Google WebM Video .webm WMV Windows Media Video .wmv"},{"location":"developer-guide/media/#cli-command","title":"CLI Command","text":"

    Run photoprism show file-formats in a terminal to list supported media and sidecar file formats.

    Command Flags:

    • --md, -m format as machine-readable Markdown
    • --csv, -c export as semicolon separated values
    • --tsv, -t export as tab separated values
    • --short, -s hide format descriptions
    "},{"location":"developer-guide/media/colors/","title":"Color Profiles","text":""},{"location":"developer-guide/media/colors/#standard-rgb","title":"Standard RGB","text":"

    sRGB is the default color space used when generating thumbnails. PhotoPrism and web browsers assume this color space for all pictures that do not have an embedded ICC color profile.

    "},{"location":"developer-guide/media/colors/#icc-profiles","title":"ICC Profiles","text":"

    An ICC color profile for wide-gamut displays can optionally be embedded in image and video files. For color profiles other than sRGB and Display P3, the thumbnails must be generated with libvips by setting the PHOTOPRISM_THUMB_LIBRARY config option to vips or auto so that the ICC profiles are preserved:

    Environment CLI Flag Default Description PHOTOPRISM_THUMB_LIBRARY --thumb-library auto image processing LIBRARY to be used for generating thumbnails (auto, imaging, vips) PHOTOPRISM_THUMB_COLOR --thumb-color auto standard color PROFILE for thumbnails (auto, preserve, srgb, none)

    Image colors may otherwise not be displayed correctly, which is particularly noticeable with ProPhoto RGB and Adobe RGB, as these cover a wide range of colors:

    "},{"location":"developer-guide/media/colors/#color-detection","title":"Color Detection","text":"

    Color detection is performed while indexing using a 3x3 thumbnail that covers the top, bottom, and center of an image e.g. https://demo.photoprism.app/library/browse?color=green.

    Learn more \u203a

    "},{"location":"developer-guide/media/heif/","title":"HEIC / HEIF","text":"

    HEIC / HEIF is a new image file format employing HEVC (h.265) image coding for the best compression ratios currently possible. Newer iPhones use it for internal photo storage. It is supported on iOS 11 and macOS High Sierra and later.

    "},{"location":"developer-guide/media/heif/#testing-conversion-and-orientation","title":"Testing Conversion and Orientation","text":"

    Let's say you have Docker installed and want to test HEIF image conversion and orientation with Debian 12 \"Bookworm\", you can simply run this command to open a terminal:

    docker run --rm -v ${PWD}:/test -w /test -ti debian:bookworm bash\n

    This will mount the current working directory as /test. Of course, you can also specify a full path instead of ${PWD}.

    The available Ubuntu, Debian and PhotoPrism images can be found on Docker Hub:

    • https://hub.docker.com/_/ubuntu
    • https://hub.docker.com/_/debian
    • https://hub.docker.com/r/photoprism/photoprism/tags

    Now install exiftool and libheif-examples (includes the heif-convert command) via apt:

    apt update\napt install -y libheif-examples exiftool\n

    Finally, run the heif-convert command (-q 92 is optional and determines the JPEG quality/compression):

    root@1ad9fb887a4f:/test# heif-convert -q 92 IMG_8437.HEIC IMG_8437.HEIC.jpg\nFile contains 1 image\nWritten to IMG_8437.HEIC.jpg\n

    To view the image metadata, run exiftool -n <filename> and optionally use grep to filter the output:

    root@1ad9fb887a4f:/test# exiftool -n IMG_8437.HEIC.jpg | grep ation\nFile Modification Date/Time     : 2022:09:18 08:16:28+00:00\nOrientation                     : 6\nExposure Compensation           : 0\nroot@1ad9fb887a4f:/test# exiftool -n IMG_8437.HEIC | grep ation\nFile Modification Date/Time     : 2022:09:17 16:57:40+00:00\nOrientation                     : 6\nExposure Compensation           : 0\nHEVC Configuration Version      : 1\nMin Spatial Segmentation IDC    : 0\nRotation                        : 270\n

    Rotation and Orientation are the important values you should pay attention to and compare. The rotation is in degrees.

    "},{"location":"developer-guide/media/heif/#exiftool-parameters","title":"Exiftool Parameters","text":"
    • -n displays the raw values without changes
    • -j will format the output as JSON
    • -g groups the output by metadata source
    "},{"location":"developer-guide/media/heif/#exif-orientation","title":"Exif Orientation","text":"

    The Exif orientation values are numbered from 1 to 8:

    1. = 0 degrees: the correct orientation, no adjustment is required.
    2. = 0 degrees, mirrored: image has been flipped back-to-front.
    3. = 180 degrees: image is upside down.
    4. = 180 degrees, mirrored: image has been flipped back-to-front and is upside down.
    5. = 90 degrees: image has been flipped back-to-front and is on its side.
    6. = 90 degrees, mirrored: image is on its side.
    7. = 270 degrees: image has been flipped back-to-front and is on its far side.
    8. = 270 degrees, mirrored: image is on its far side.

    Learn more \u203a

    "},{"location":"developer-guide/media/heif/#software-libraries-and-references","title":"Software Libraries and References","text":"
    • https://www.idownloadblog.com/2017/10/18/how-to-convert-heif-to-jpeg-imazing-heic-converter/ - How to convert HEIF images to JPEGs with iMazing HEIC Converter
    • strukturag/libheif - libheif is a ISO/IEC 23008-12:2017 HEIF file format decoder and encoder (C++)
    • nokiatech/heif - Reader/Writer Engine is an implementation of the HEIF standard to demonstrate its powerful features and capabilities (C++)
    • monostream/tifig - A fast HEIF image converter aimed at thumbnailing
    • perkeep/perkeep#969 - HEIC/HEVC support in Perkeep
    • jdeng/goheif - A decoder/converter for HEIC based on libde265 (CGO)
    "},{"location":"developer-guide/media/import/","title":"Importing, Indexing, and Uploading","text":"

    PhotoPrism is meant to be a complete photo management solution that not only lets you index your existing pictures, but also add or upload new files to your photo collection or delete existing files to free up space.

    You can therefore choose to index your originals directly, leaving all file and folder names unchanged, or use the optional import feature that automatically removes duplicates, gives files a unique name and sorts them by year and month.

    To learn more, follow our First Steps \ud83d\udc63 tutorial which will guide you through the interface and settings.

    "},{"location":"developer-guide/media/import/#read-only-mode","title":"Read-Only Mode","text":"

    Early in development there was some debate about whether PhotoPrism should be responsible for naming files, see Support Photos In Place on Hard Drive #41. Once you want the software to automatically create new files or merge photo libraries from different devices, this is often the only viable option.

    Besides the index-only functionality for users who want to name their files manually, we have also introduced a read-only mode for those who want to use the software as a gallery with limited features, see Read-Only Mode #56.

    "},{"location":"developer-guide/media/import/#post-processing","title":"Post Processing","text":"

    After importing new pictures, it would be great if users could quickly sort and review them. This way, the library stays clean and organized, even without applying filters to hide bad shots.

    This is common workflow among users of Adobe Bridge, a tool used by many professionals. They go through complete asset collections and sort photos using keyboard shortcuts, where keys toggle pre-defined tags or flags like \"favorite\".

    "},{"location":"developer-guide/media/import/#upload-options","title":"Upload Options","text":"

    Users can either use WebDAV to upload files and add them to their originals or import folder, or use the Web Upload to add them to a temporary directory on the server, from which they will be imported to the originals folder.

    We might later use WebAssembly or the File API to improve the performance and capabilities of the Web Upload.

    "},{"location":"developer-guide/media/live/","title":"Hybrid Photo/Video Formats","text":""},{"location":"developer-guide/media/live/#apple-iphone-and-ipad","title":"Apple iPhone and iPad","text":"

    iOS Live Photos consist of a JPEG/HEIC image and a QuickTime AVC/HEVC video, which are both required for viewing.

    We recommend using an app like PhotoSync to upload Live Photos to PhotoPrism, since the iOS web upload usually only submits the HEIC image file without the video.

    "},{"location":"developer-guide/media/live/#android-devices","title":"Android Devices","text":"

    Some Samsung and Google Android devices support taking \"Motion Photos\" with the included Camera app. Motion Photos are JPEG/HEIC image with a short MP4 video embedded after the image data.

    The image part of these files can be opened in any image viewer that supports JPEG/HEIC, but the video part cannot. However, since the MP4 video is simply appended at the end of the image file, it can be easily read by our software and streamed through the API as needed.

    "},{"location":"developer-guide/media/live/#introductory-tutorials","title":"Introductory Tutorials","text":"Title Date URL How to detect Android motion photos in Flutter May 2023 https://ente.io/blog/tech/android-motion-photos-flutter/ Stripping Embedded MP4s out of Android 12 Motion Photos Oct 2021 https://mjanja.ch/2021/10/stripping-embedded-mp4s-out-of-android-12-motion-photos/ Google Pixel \"Motion Photo\" Howto Mar 2021 https://linuxreviews.org/Google_Pixel_%22Motion_Photo%22 go-mp4: Golang Library and CLI Tool for MP4 Jul 2020 https://dev.to/sunfishshogi/go-mp4-golang-library-and-cli-tool-for-mp4-52o1 Working with Motion Photos Jan 2019 https://medium.com/android-news/working-with-motion-photos-da0aa49b50c Google: Behind the Motion Photos Technology in Pixel 2 Mar 2018 https://blog.research.google/2018/03/behind-motion-photos-technology-in.html"},{"location":"developer-guide/media/live/#software-libraries-and-references","title":"Software Libraries and References","text":"Title URL Web Video Codec Guide https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs Media Container Formats https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers MP4 Signature Format https://www.file-recovery.com/mp4-signature-format.htm List of file signatures (Wikipedia) https://en.wikipedia.org/wiki/List_of_file_signatures Go library for reading and writing MP4 files abema/go-mp4 Go library for buffered I/O with io.Seeker interface sunfish-shogi/bufseekio How to use the io.Reader interface https://yourbasic.org/golang/io-reader-interface-explained/ AV1 Codec ISO Media File Format https://aomediacodec.github.io/av1-isobmff"},{"location":"developer-guide/media/live/#related-github-issues","title":"Related GitHub Issues","text":"
    • #439 (Samsung: Initial support for Motion Photos)
    • #1739 (Google: Initial support for Motion Photos)
    • #2788 (Metadata: Flag Samsung/Google Motion Photos as Live Photos)
    • cliveontoast/GoMoPho#23 (Google Motion Photos Video Extractor: Add Android 12 Support)
    "},{"location":"developer-guide/media/live/#related-pull-requests","title":"Related Pull Requests","text":"
    • #3709 (Google: Initial support for Motion Photos)
    • #3722 (Google: Add support for Motion Photos)
    • #3660 (Samsung: Improved support for Motion Photos)
    "},{"location":"developer-guide/media/raw/","title":"RAW Images","text":"

    Professional and semi-professional photographers often keep their originals in a lossless RAW format, close to how they were taken with the physical sensor, rather than in a compressed image format like JPEG, especially if they shoot with a digital SLR camera. Newer mobile phones may also be able to capture images in RAW mode. Our goal is to provide top-notch support for all RAW images, regardless of camera make and model. A full list of file types and extensions can be found in our Knowledge Base.

    "},{"location":"developer-guide/media/raw/#raw-conversion","title":"RAW Conversion","text":"

    Since web browsers generally cannot display RAW image files directly, they must be converted. This is done during import or initial indexing. It can also be triggered manually in a terminal with the photoprism convert command.

    "},{"location":"developer-guide/media/raw/#adobe-xmp","title":"Adobe XMP","text":"

    PhotoPrism currently supports Darktable and RawTherapee as RAW image converters (as well as Sips on macOS). Darktable fully supports XMP sidecar files, RawTherapee might only partially. However, XMP is only a \"container\" format, so the fields (namespaces) used there to indicate how an image should be converted (as well as other metadata) differ between Lightroom/Photoshop, Darktable, and RawTherapee.

    In other words, just because an application generally supports XMP that doesn't mean it can use metadata created with another application or by another vendor like Adobe. If you think that's confusing, well, that's because it is. You have an open format, but you still suffer from vendor lock-in - probably not entirely unintentional on Adobe's part.

    From our experience, some basic edits done with Adobe tools - such as cropping - might be preserved when you convert the same RAW image with other software like Darktable. Advanced edits, such as lens or color corrections, will likely not be applied.

    Learn more \u203a

    "},{"location":"developer-guide/media/raw/#darktable","title":"Darktable","text":"

    If installed, Darktable CLI can be used for RAW image conversion. Note that PhotoPrism will only run one instance of darktable-cli at the same time if the PHOTOPRISM_RAW_PRESETS config option is enabled, so existing Darktable presets can be applied (requires an exclusive lock).

    "},{"location":"developer-guide/media/raw/#using-darktable-as-library","title":"Using Darktable as library","text":"

    We had the idea to use cgo and link directly against libdarktable.so to convert RAW images to JPEG, see darktable-dev mailing list. However this requires calling/wrapping a large number of C functions and also has other disadvantages. The idea is postponed until a clear benefit becomes visible. Running darktable-cli or any other binary that does the job (see above) seems to be the way to go.

    "},{"location":"developer-guide/media/raw/#rawtherapee","title":"RawTherapee","text":"

    If installed, RawTherapee CLI can also be used for RAW image conversion. If it used by default if Darktable isn't installed or disabled.

    "},{"location":"developer-guide/media/raw/#jpeg-size-limit","title":"JPEG Size Limit","text":"

    RawTherapee cannot limit the resolution of JPEG files when converting files from other formats such as RAW, DNG, HEIC or AVIF. In general, when converting images, the resolution of the generated JPEG files can be limited with the environment variable PHOTOPRISM_JPEG_SIZE or the CLI parameter --jpeg-size.

    "},{"location":"developer-guide/media/raw/#scriptable-image-processing-system-sips","title":"Scriptable Image Processing System (Sips)","text":"

    On a Mac, PhotoPrism can convert multiple files at once using Sips (pre-installed on OS X). It is not available for other operating systems.

    "},{"location":"developer-guide/media/raw/#comparison-of-raw-to-jpeg-converters","title":"Comparison of RAW to JPEG converters","text":"
    • darktable - popular open-source photography app and raw developer; available for Mac, Linux, and Windows; supports XMP (compatible with photoshop/lightroom?)
    • Mac OS X ships with sips: sips -s format jpeg IMAGE.RAW --out IMAGE.JPG
    • Photivo - open-source photo processor; available for Mac, Linux, and Windows; no XMP support?
    • RawTherapee - open-source RAW image processing app; available for Mac, Linux, and Windows; no XMP support?
    • digiKam - open-source digital photo management application based on Qt (KDE); available for Mac, Linux, and Windows; supports XMP (compatible with photoshop/lightroom?)
    • UFRaw - Unidentified Flying Raw is a utility to read and manipulate raw images from digital cameras
    Tool Command line options Compatible OS JPG Diff* XMP support Possible settings EXIF Diff* Compatible with Raspberry (ARM64) Darktable 1) darktable-cli IMG_0310.CR2 IMG_0310_darktable1.jpg 2) darktable-cli IMG_0310_EDITED.CR2 IMG_0310_EDITED.xmp IMG_0310_EDITED_darktable2.jpg macOS, Linux, Windows **** yes (but seems to be not compatible with adobe xmps) ? **** yes Sips sips -s format jpeg IMG_0310.CR2 --out IMG_0310_sips.jpg macOS ***** no ? **** not available for ubuntu Rawtherapee rawtherapee-cli -o IMG_0310_rawtherapee.jpg -c IMG_0310.CR2 macOS, Linux, Windows *** no ? **** yes UFraw ufraw-batch --out-type=jpg --output=IMG_0310_ufraw.jpg IMG_0310.CR2 macOS, Linux, Windows * no ? ** yes ImageMagick magick IMG_0310.CR2 IMG_0310_magick.jpg macOS, Linux, Windows * no ? ** yes Digikam ? macOS, Linux, Windows - - - - - Photiva ? macOS, Linux, Windows - - - - - * Compared to JPG/EXIF converted from photoshop"},{"location":"developer-guide/media/raw/#image-diff","title":"Image Diff","text":"

    The following table shows the difference between the JPEG files converted by Darktable, Sips, Rawtherapee, UFraw and ImageMagick compared to Adobe Photoshop. Red are pixel that differ from the photoshop version, white are equal pixels. In total 5 Images have been compared (full results). The diff was created using ewanmellor/git-diff-image.

    Tool Diff (left is Photoshop) Darktable Sips Rawtherapee UFraw ImageMagick"},{"location":"developer-guide/media/raw/#exif-diff","title":"EXIF Diff","text":"

    The following table shows the difference between the JPG files converted by the tools compared to JPG files converted by photoshop. 5 Images have been compared.

    Info Photoshop Sips Darktable Raw-therapee Image Magick UFraw Kind identical identical identical identical identical identical Size 2.881.969 bytes (EXIF)/ 2.7M (ls -alh) 2.673.777 bytes / 2.5M 5.446.710 bytes / 5.2M always bigger 3.467.998 bytes / 3.3M 3.467.998 bytes / 3.3M 1.558.587 bytes / 1.5M always smaller Where reference different different different different different Created reference different different different different different Modified reference different different different different different Dimension identical - 5472\u200a\u00d7\u200a3648 5472\u200a\u00d7\u200a3648 different - 5494\u200a\u00d7\u200a3666 different - 5488\u200a\u00d7\u200a3662 different - 5496\u200a\u00d7\u200a3670 different - 5496\u200a\u00d7\u200a3670 Device Make refrence identical identical identical not set not set Colour space reference identical identical identical identical identical Colour profile Adobe RGB (1998) different - Display P3 different - sRGB different - RTv2_sRGB not set not set Focal length reference identical identical identical not set not set Alpha Channel reference identical identical identical not set not set Red eye reference identical identical identical not set not set Metering Mode reference identical identical identical not set not set F number reference identical identical identical not set not set Exposure program reference identical identical identical not set not set Exposure time reference identical identical identical not set not set Latitude reference sometimes very small differences identical identical not set not set Longitude reference sometimes very small differences identical identical not set not set"},{"location":"developer-guide/media/raw/#adobe-photoshop-lightroom","title":"Adobe Photoshop / Lightroom","text":"

    Ideally we can convert images directly with Photoshop or Lightroom, if installed. It seems like Photoshop comes with some sort of command-line automation tool based on NodeJS: adobe-photoshop/generator-core

    See https://photo.stackexchange.com/questions/39532/command-line-approach-to-develop-raw-images-with-adobe-xmp-sidecars

    This needs further investigation. Contributions welcome!

    "},{"location":"developer-guide/media/raw/#todo-reading-list","title":"Todo / Reading List","text":"
    • Compare the quality and XMP compatibility of different RAW converters #65
    • Investigate if PhotoShop or Lightroom can be used to convert images on the command line
    • Collect example RAW files of common camera brands and models
    • Figure out how we can implement \"auto-enhance\" (automatically fix exposure and write XMP file while converting to JPEG)
    • https://howtogimp.com/raw-photos-in-gimp/ - RAW converters to use with GIMP
    • mdouchement/hdr - HDR is a library that handles RAW image format written with Golang
    "},{"location":"developer-guide/media/samples/","title":"File Samples","text":"

    We welcome image and video file samples from our community. You can email them to samples@photoprism.app, either as attachments or with a download link.

    Please include the file format and GitHub issue number (if applicable) or other helpful reference (e.g. camera model) in the subject line, and let us know if we have permission to upload your files to dl.photoprism.app/samples for future use.

    Contact Us Submit Files Browse Archive

    "},{"location":"developer-guide/media/storage/","title":"File Storage","text":"

    At the moment, all media and sidecar files as well as thumbnail images are stored directly in the server's file system, see Config Options.

    "},{"location":"developer-guide/media/storage/#filesystem-abstraction","title":"Filesystem Abstraction","text":"

    We may eventually want to add a filesystem abstraction e.g. to speed up testing or support cloud storage. Afero seems to be the gold standard for this in the Go world:

    • spf13/afero
    "},{"location":"developer-guide/media/storage/#related-github-issues","title":"Related GitHub Issues","text":"
    • Support for cloud storage with encryption
    • Embedded Database + Bleve for search? #55
    • Postgres support? #47
    "},{"location":"developer-guide/media/storage/#software-libraries-and-references","title":"Software Libraries and References","text":"
    • rclone/rclone - rsync for cloud storage (Google Drive, AWS, One Drive, ...)
    • @minio - Object Storage for AI
    • upper.io/db.v3 - a productive data access layer for Go
    • LevelDB - fast key-value storage library written at Google (written in Go)
    • Bleve - full-text search and indexing for Go
    • TiDB - a distributed HTAP database compatible with the MySQL protocol (written in Go)
    • KSQL - the Streaming SQL Engine for Apache Kafka
    • CockroachDB - ultra-resilient SQL for global business (written in Go)
    • LedisDB - a high performance NoSQL like Redis powered by Go
    • go-pg/pg - Golang ORM with focus on PostgreSQL features and performance
    • go-redis/redis - Type-safe Redis client for Golang
    "},{"location":"developer-guide/media/thumbnails/","title":"Thumbnail Generation and Storage","text":""},{"location":"developer-guide/media/thumbnails/#introduction","title":"Introduction","text":"

    PhotoPrism can use libvips and the native disintegration/imaging package to generate thumbnails for display in search results and in the full-screen viewer.

    The smallest configurable size limit is 720px, which corresponds to the fit_720 thumbnail size. Most sizes up to 720x720 will be generated by default and should therefore always be available.

    Besides their obvious use in the frontend, the indexer also uses thumbnails for color detection, face recognition and image classification, see Advanced Settings in the User Guide.

    "},{"location":"developer-guide/media/thumbnails/#standard-sizes","title":"Standard Sizes","text":"

    The smallest configurable static and dynamic size limit is 720px, so most sizes up to fit_720 are always generated by default. Higher size limits generate thumbnails with more detail at higher resolutions - either statically (pre-generated while indexing) or on demand if the configuration permits.

    Optional thumbnail sizes cannot be pre-generated and are only rendered on request, for example when sharing an image on Instagram.

    The following overview shows the name, dimensions, and aspect ratio for each thumbnail size as well as a description of how it is used:

    Name Width Height Aspect Ratio Available Usage colors 3 3 1:1 Always Color Detection tile_50 50 50 1:1 Always List View tile_100 100 100 1:1 Always Places View left_224 224 224 1:1 On-Demand TensorFlow right_224 224 224 1:1 On-Demand TensorFlow tile_224 224 224 1:1 Always TensorFlow, Mosaic View tile_500 500 500 1:1 Always Cards View fit_720 720 720 Preserved Always SD TV, Mobile tile_1080 1080 1080 1:1 Optional Instagram fit_1280 1280 1024 Preserved On-Demand HD TV, SXGA fit_1600 1600 900 Preserved Optional Social Media fit_1920 1920 1200 Preserved On-Demand Full HD fit_2048 2048 2048 Preserved Optional DCI 2K, Tablets fit_2560 2560 1600 Preserved On-Demand Quad HD, Notebooks fit_3840 3840 2400 Preserved Optional 4K Ultra HD fit_4096 4096 4096 Preserved On-Demand DCI 4K, Retina 4K fit_7680 7680 4320 Preserved On-Demand 8K Ultra HD 2

    \u21aa internal/thumb/sizes.go

    "},{"location":"developer-guide/media/thumbnails/#color-profiles","title":"Color Profiles","text":"

    sRGB is the standard color space used when generating thumbnails. An ICC color profile for wide-gamut displays can optionally be embedded.

    Learn more \u203a

    "},{"location":"developer-guide/media/thumbnails/#file-storage","title":"File Storage","text":"

    Generated thumbnail files are stored in the storage/cache/thumbnails folder, where the path and file name depend on the thumbnail size and original file hash, e.g.:

    storage/cache/thumbnails/1/a/3/1a30c1f...9_100x100_center.jpg\n
    "},{"location":"developer-guide/media/thumbnails/#downscaling-filters","title":"Downscaling Filters","text":""},{"location":"developer-guide/media/thumbnails/#linear","title":"Linear","text":"

    Bilinear interpolation takes a weighted average of the four neighborhood pixels to calculate its final interpolated value. The result is a much smoother image than the original image. When all known pixel distances are equal, then the interpolated value is simply their sum divided by four. This technique performs interpolation in both directions, horizontal and vertical. This technique gives better result than nearest neighbor interpolation and take less computation time compared to bicubic interpolation.

    "},{"location":"developer-guide/media/thumbnails/#cubic","title":"Cubic","text":"

    Catmull-Rom is a local interpolating spline developed for computer graphics purposes. Its initial use was in design of curves and surfaces, and has recently been used in several applications. Catmull-Rom splines are a family of cubic interpolating splines formulated such that the tangent at each point is calculated using the previous and next point on the spline. The results are similar to ones produced by bicubic interpolation with regards to sharpness, but the Catmull-Rom reconstruction is clearly superior in smooth signal region.

    "},{"location":"developer-guide/media/thumbnails/#lanczos","title":"Lanczos","text":"

    The Lanczos interpolation function is a mathematical formula used to smoothly interpolate the value of a digital image between its samples. It maps each sample of the given image to a translated and scaled copy of the Lanczos kernel, which is a sinc function windowed by the central hump of a dilated sinc function. The sum of these translated and scaled kernels is then evaluated at the desired pixel. Lanczos interpolation has the best properties in terms of detail preservation and minimal generation of aliasing artifacts for geometric transformations not involving strong down sampling. However higher order Lanczos interpolation requires high computational time, which makes it unsuitable for most commercial software.

    "},{"location":"developer-guide/media/thumbnails/#blackman","title":"Blackman","text":"

    Blackman is a modification of Lanczos that has better control of ringing artifacts.

    "},{"location":"developer-guide/media/thumbnails/#server-api","title":"Server API","text":"

    Like most commercial image hosting services, we have chosen to implement a cookie-free thumbnail API to minimize request latency by avoiding unnecessary network traffic.

    Learn more \u203a

    "},{"location":"developer-guide/media/thumbnails/#further-reading","title":"Further Reading","text":"
    • https://en.wikipedia.org/wiki/Image_scaling
    • https://en.wikipedia.org/wiki/Lanczos_resampling
    • https://en.wikipedia.org/wiki/Comparison_gallery_of_image_scaling_algorithms
    "},{"location":"developer-guide/media/videos/","title":"Video File Support","text":""},{"location":"developer-guide/media/videos/#codecs-and-containers","title":"Codecs and Containers","text":"

    For maximum browser compatibility, PhotoPrism can transcode video codecs and containers supported by FFmpeg to MPEG-4 AVC.

    Running the following command in a terminal displays a list of supported codecs:

    ffmpeg -decoders\n

    See our advanced setup guide to learn how to configure hardware video transcoding.

    Please Note:

    1. Not all video and audio formats can be played with every browser. For example, AAC - the default audio codec for MPEG-4 AVC / H.264 - is supported natively in Chrome, Safari, and Edge, while it is only optionally supported by the OS in Firefox and Opera.
    2. HEVC/H.265 video files can have a .mp4 file extension too, which is often associated with AVC only. This is because MP4 is a container format, meaning that the actual video content may be compressed with H.264, H.265, or something else. The file extension doesn't really tell you anything other than that it's probably a video file.
    3. In case FFmpeg is disabled or not installed, videos cannot be indexed because still images cannot be created. You should also have ExifTool enabled to extract metadata such as duration, resolution, and codec.
    "},{"location":"developer-guide/media/videos/#hybrid-photovideo-formats","title":"Hybrid Photo/Video Formats","text":"

    For more information on hybrid photo/video file formats, e.g. Apple Live Photos and Samsung/Google Motion Photos, see github.com/photoprism/photoprism/tree/develop/pkg/media and /developer-guide/media/live/.

    "},{"location":"developer-guide/media/videos/#standard-resolutions","title":"Standard Resolutions","text":"

    The PHOTOPRISM_FFMPEG_SIZE config option allows to limit the resolution of transcoded videos. It accepts the following standard sizes, while other values are automatically adjusted to the next supported size:

    Size Usage 720 SD TV, Mobile 1280 HD TV, SXGA 1920 Full HD 2048 DCI 2K, Tablets 2560 Quad HD, Notebooks 3840 4K Ultra HD 4096 DCI 4K, Retina 4K 7680 8K Ultra HD 2"},{"location":"developer-guide/media/videos/#technical-references-and-tutorials","title":"Technical References and Tutorials","text":"Title URL Web Video Codec Guide https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs Web Video Content-Type Headers https://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter Media Container Formats https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers MP4 Signature Format https://www.file-recovery.com/mp4-signature-format.htm List of file signatures (Wikipedia) https://en.wikipedia.org/wiki/List_of_file_signatures How to use the io.Reader interface https://yourbasic.org/golang/io-reader-interface-explained/ AV1 Codec ISO Media File Format https://aomediacodec.github.io/av1-isobmff"},{"location":"developer-guide/metadata/cameras/","title":"Camera Brands and Models","text":""},{"location":"developer-guide/metadata/cameras/#market-share-by-brand-2016","title":"Market Share by Brand (2016)","text":"

    DSLR Cameras

    Brand Share Canon 63.3% Nikon 31.6% Ricoh Imaging (Pentax) 4.8%

    System Cameras

    Brand Share Olympus 26.8% Canon 18.5% Sony 17.9%

    See also Brands with the highest market share 2016.

    "},{"location":"developer-guide/metadata/cameras/#most-popular-brands-on-flickr","title":"Most Popular Brands on Flickr","text":"

    https://www.flickr.com/cameras

    "},{"location":"developer-guide/metadata/cameras/#camera-models-supported-by-photoshop","title":"Camera Models Supported by Photoshop","text":"

    https://helpx.adobe.com/camera-raw/kb/camera-raw-plug-supported-cameras.html

    "},{"location":"developer-guide/metadata/colors/","title":"Color Detection","text":"

    Color detection is performed while indexing using a 3x3 thumbnail that covers the top, bottom, and center of an image e.g. https://demo.photoprism.app/library/browse?color=green.

    "},{"location":"developer-guide/metadata/colors/#standard-colors","title":"Standard Colors","text":"

    The following color names can be used when searching for pictures:

    ID Name Code Sample 0 Black #000000 1 Brown #795548 2 Grey #9E9E9E 3 White #FFFFFF 4 Purple #9C27B0 5 Gold #FFDF00 6 Blue #3F51B5 7 Cyan #00BCD4 8 Teal #009688 9 Green #4CAF50 A Lime #CDDC39 B Yellow #FFEB3B C Magenta #FF00FF D Orange #FF9800 E Red #F44336 F Pink #E91E63"},{"location":"developer-guide/metadata/colors/#color-examples","title":"Color Examples","text":"

    This overview shows additional examples of matching color codes:

    ID Name Code Sample 0 Black #000000 1 Brown #A1887F #8D6E63 #A07F6C #9B7B5B #75645B #795548 #6D4C41 #5D4037 #9B6136 #C1A487 #AA8062 #6B5546 #B4B59C #B2B49B 2 Grey #E0E0E0 #9E9E9E #757575 #616161 #424242 #847A72 #DFE0E1 3 White #FFFFFF #E4E4E4 #E7E7E7 4 Purple #F3E5F5 #E1BEE7 #CE93D8 #BA68C8 #AB47BC #9C27B0 #9B318F #86007E #8E24AA #7B1FA2 #6A1B9A #4A148C #AA00FF #EDE7F6 #D1C4E9 #B39DDB #9575CD #7E57C2 #5E35B1 #673AB7 #512DA8 #4527A0 #311B92 #B388FF #7C4DFF #8E6493 #5E3A5E #440E79 #483678 #4E3880 #3B0E79 5 Gold #EDDEAC #E8B451 #C08A3E #A27D4b #755531 #D19327 #DEA253 #D5AA6F #F5EAD4 6 Blue #3F51B5 #C5CAE9 #5C6BC0 #3949AB #303F9F #283593 #1A237E #536DFE #3D5AFE #304FFE #2196F3 #BBDEFB #90CAF9 #64B5F6 #42A5F5 #1E88E5 #1976D2 #1565C0 #0D47A1 #82B1FF #448AFF #2979FF #2962FF #03A9F6 #B3E5FC #81D4FA #4FC3F7 #29B6F6 #039BE5 #0288D1 #0277BD #01579B #80D8FF #40C4FF #00B0FF #0091EA #607D8B #78909C #546E7A #37474F #E4EBFD #7DD3EA #076399 #28446B #4AC8F5 #0800F4 #012D5F 7 Cyan #B2EBF2 #80DEEA #4DD0E1 #26C6DA #00B8D4 #00BCD4 #00ACC1 #0097A7 #00838F #006064 #84FFFF #18FFFF #00E5FF 8 Teal #009688 #00897B #00796B #00695C #045D5C #245A5F #03454F #2C545E #174741 9 Green #E8F5E9 #C8E6C9 #ABC7B0 #A5D6A7 #81C784 #66BB6A #4CAF50 #43A047 #388E3C #2E7D32 #1B5E20 #F1F8E9 #DCEDC8 #C5E1A5 #AED581 #8BC34A #9CCC65 #7CB342 #689F38 #558B2F #33691E #B9F6CA #69F0AE #00C853 #00E676 #CCFF90 #B2FF59 #76FF03 #64DD17 #DDD579 #EEECA2 #244E3B #9A9D47 #BEBD76 #5C5A30 #B3C16C #ACA783 #474C25 #CDD087 #796D41 A Lime #F0F4C3 #E6EE9C #DCE775 #D4E157 #CDDC39 #C0CA33 #AFB42B #EEFF41 #C6FF00 #AEEA00 B Yellow #FFF9C4 #FFF59D #FFF176 #FFEE58 #FFFF8D #FFFF00 #FFD54F #FFCA28 #E3CE81 #D1AF52 #EEBB2B #D3A83A #C5A702 #9F8201 #E8CE03 C Magenta #FF00FF #E500E5 #F000B5 #CE009B #C0055B #B00085 #A82863 #5B002F #4B0121 #860225 #CB023D #64071A #9E0047 #DC7ACF D Orange #FF9800 #FFA726 #FB8C00 #F57C00 #EF6C00 #FF9100 #FF6D00 #FD9A31 #7D2704 #FD571F #F86704 #FD9A00 #FE8A00 #F19652 #E58347 #C94C30 #9F5601 #FA6801 #F9A825 #BB723D E Red #FF5252 #F44336 #EF5350 #E53935 #F6292E #FC252D #D32F2F #C62828 #BA2830 #B71C1C #D50000 #DB0806 #CF0904 #D81A14 #CC1708 #D80807 #DE2616 #EE240F #A1211F #701219 #511218 #491114 F Pink #FCE4EC #FDC8EB #E79FA6 #F8BBD0 #F48FB1 #FF80AB #FF4081 #F50057 #F06292 #EC407A #E91E63 #D81B60 #C2185B"},{"location":"developer-guide/metadata/colors/#hcl-vs-hsl","title":"HCL vs HSL","text":"

    Our color library stores colors in RGB and provides methods to convert them to different color spaces, including HCL and HSL:

    Learn more \u203a

    "},{"location":"developer-guide/metadata/colors/#color-profiles","title":"Color Profiles","text":"

    sRGB is the standard color space used when generating thumbnails. An ICC color profile for wide-gamut displays can optionally be embedded.

    Learn more \u203a

    "},{"location":"developer-guide/metadata/geocoding/","title":"Reverse Geocoding","text":"

    If enabled, reverse geocoding enriches photo and video metadata with details such as country, state, city, and category.

    "},{"location":"developer-guide/metadata/geocoding/#privacy-policy","title":"Privacy Policy","text":"

    As explained in detail in our Privacy Policy, reverse geocoding depends on retrieving the necessary information from a backend that we provide for this purpose.

    The costs are currently fully covered by us for all users, including non-sponsors, and we ensure a very high level of privacy and confidentiality:

    • API requests are not logged permanently.
    • Our API approximates the coordinates and encodes them with a fuzzy S2 cell ID that does not include the house number or any other data identifying a specific residential address, except possibly in very sparsely populated areas of the world. Even then, we cannot trace the request back to a person, picture or point in time.
    • We may store your server's IP address and other HTTP request headers for a limited time to perform authorization checks, prevent abuse, and implement rate limits. Since the traffic is encrypted, no one intercepting the server-to-server communication can see the exact request and response; only the fact that you exchanged data with our backend.

    Other open source applications sometimes use the free developer APIs operated by openstreetmap.org. In this case, their usage and privacy policies apply, which means that your request data is stored and used to create publicly available reports. This is different from our approach, which focuses on our users' privacy and user experience.

    "},{"location":"developer-guide/metadata/geocoding/#example-request","title":"Example Request","text":"

    GET https://places.photoprism.app/v1/location/149ce78563

    {\n  \"id\": \"s2:149ce78563\",\n  \"name\": \"Elafonisi Kite Club\",\n  \"street\": \"\u039a\u03b5\u03c6\u03b1\u03bb\u03ae\",\n  \"postcode\": \"\",\n  \"category\": \"nature\",\n  \"timezone\": \"Europe/Athens\",\n  \"lat\": 35.269638,\n  \"lng\": 23.536951,\n  \"place\": {\n    \"id\": \"gr:GCd9oey68OFr\",\n    \"label\": \"\u03a7\u03c1\u03c5\u03c3\u03bf\u03c3\u03ba\u03b1\u03bb\u03af\u03c4\u03b9\u03c3\u03c3\u03b1, Greece\",\n    \"district\": \"\",\n    \"city\": \"\u03a7\u03c1\u03c5\u03c3\u03bf\u03c3\u03ba\u03b1\u03bb\u03af\u03c4\u03b9\u03c3\u03c3\u03b1\",\n    \"state\": \"\u0391\u03c0\u03bf\u03ba\u03b5\u03bd\u03c4\u03c1\u03c9\u03bc\u03ad\u03bd\u03b7 \u0394\u03b9\u03bf\u03af\u03ba\u03b7\u03c3\u03b7 \u039a\u03c1\u03ae\u03c4\u03b7\u03c2\",\n    \"country\": \"gr\",\n    \"keywords\": \"\u03c7\u03c1\u03c5\u03c3\u03bf\u03c3\u03ba\u03b1\u03bb\u03af\u03c4\u03c3\u03b1\"\n  },\n  \"events\": [],\n  \"licence\": \"Data \u00a9 PhotoPrism\"\n}\n
    "},{"location":"developer-guide/metadata/geocoding/#location-data","title":"Location Data","text":"

    See: internal/maps/location.go

    "},{"location":"developer-guide/metadata/geocoding/#world-maps","title":"World Maps","text":"

    PhotoPrism also includes four high-resolution world maps that allow you to browse photos by location, see Web User Interface > Interactice Maps. Visit try.photoprism.app/library/places to try them on our demo.

    "},{"location":"developer-guide/metadata/geocoding/#related-resources","title":"Related Resources","text":""},{"location":"developer-guide/metadata/geocoding/#event-discovery","title":"Event Discovery","text":"
    • https://schedjoules.github.io/event-discovery-api/#introduction
    • https://www.gdeltproject.org/data.html#rawdatafiles
    "},{"location":"developer-guide/metadata/geocoding/#libraries","title":"Libraries","text":"
    • https://tegola.io/ - An open source vector tile server written in Go
    • https://tegola.io/tutorials/tegola-with-open-layers/ - Using Tegola with OpenLayers
    • go-spatial/tegola-osm - scripts for importing and running a mirror of OSM with tegola
    • https://pelias.io/ - Pelias Geocoder (GitHub)
    • Leaflet/Leaflet - JavaScript library for mobile-friendly interactive maps
    • tidwall/tile38 - a geospatial database and realtime geofencing server
    • melihmucuk/geocache - an in-memory cache that is suitable for geolocation based applications
    • aaronland/go-slippy-tiles - a proxy for map tiles
    • paulmach/osm - a general purpose library for reading, writing and working with OpenStreetMap data
    • maguro/pbf - a Go-based OpenStreetMap PBF encoder/ decoder
    • golang/geo - S2 geometry library in Go
    • https://gist.github.com/antoniomo/3371e44cbe2f0cc75a525aac0d188cfb - example for S2 geometry library
    • tidwall/redcon - Redis compatible server framework (Go)
    • siddontang/ledisdb - a high performance NoSQL DB powered by Go (homepage)
    • https://github.com/blevesearch/bleve/tree/master/geo - geo support in bleve
    "},{"location":"developer-guide/metadata/geocoding/#commercial-services","title":"Commercial Services","text":"
    • https://developers.google.com/maps/documentation/
    • https://www.mapbox.com/maps/
    "},{"location":"developer-guide/metadata/geocoding/#tutorials","title":"Tutorials","text":"
    • https://developers.google.com/maps/solutions/store-locator/clothing-store-locator#findnearsql
    • https://blog.mastermaps.com/2014/08/showing-geotagged-photos-on-leaflet-map.html - Showing geotagged photos on a Leaflet map
    • https://rubenspgcavalcante.github.io/leaflet-ant-path/ - Animate polylines as ants walking in a path
    "},{"location":"developer-guide/metadata/orientation/","title":"Image Orientation","text":""},{"location":"developer-guide/metadata/orientation/#using-exiftool","title":"Using Exiftool","text":"

    Assuming you have Docker installed and want to run exiftool with Debian 12 \"Bookworm\", you can simply run this command to open a terminal:

    docker run --rm -v ${PWD}:/test -w /test -ti debian:bookworm bash\n

    This will mount the current working directory as /test. Of course, you can also specify a full path instead of ${PWD}.

    The available Ubuntu, Debian and PhotoPrism images can be found on Docker Hub:

    • https://hub.docker.com/_/ubuntu
    • https://hub.docker.com/_/debian
    • https://hub.docker.com/r/photoprism/photoprism/tags

    Now install exiftool and any other packages you need, e.g. libheif-examples to convert HEIF images to JPEG, via apt:

    apt update\napt install -y exiftool libheif-examples\n

    To view the image metadata, run exiftool -n <filename> and optionally use grep to filter the output:

    root@1ad9fb887a4f:/test# exiftool -n IMG_8437.HEIC.jpg | grep ation\nFile Modification Date/Time     : 2022:09:18 08:16:28+00:00\nOrientation                     : 6\nExposure Compensation           : 0\nroot@1ad9fb887a4f:/test# exiftool -n IMG_8437.HEIC | grep ation\nFile Modification Date/Time     : 2022:09:17 16:57:40+00:00\nOrientation                     : 6\nExposure Compensation           : 0\nHEVC Configuration Version      : 1\nMin Spatial Segmentation IDC    : 0\nRotation                        : 270\n

    Rotation and Orientation are the important values you should pay attention to and compare. The rotation is in degrees.

    "},{"location":"developer-guide/metadata/orientation/#exiftool-parameters","title":"Exiftool Parameters","text":"
    • -n displays the raw values without changes
    • -j will format the output as JSON
    • -g groups the output by metadata source
    "},{"location":"developer-guide/metadata/orientation/#exif-values","title":"Exif Values","text":"

    The numbers used to specify the image orientation are defined as follows:

    1. = 0 degrees: the correct orientation, no adjustment is required.
    2. = 0 degrees, mirrored: image has been flipped back-to-front.
    3. = 180 degrees: image is upside down.
    4. = 180 degrees, mirrored: image has been flipped back-to-front and is upside down.
    5. = 90 degrees: image has been flipped back-to-front and is on its side.
    6. = 90 degrees, mirrored: image is on its side.
    7. = 270 degrees: image has been flipped back-to-front and is on its far side.
    8. = 270 degrees, mirrored: image is on its far side.

    Learn more \u203a

    "},{"location":"developer-guide/metadata/perceptual-hashes/","title":"Perceptual Image Hashes","text":"

    Perceptual Image Hashing refers to the use of a fingerprint algorithm to generate a hash that correlates with the appearance of an image without depending on other parameters such as file size, type, name, resolution, or other metadata.

    "},{"location":"developer-guide/metadata/perceptual-hashes/#sorting-by-visual-similarity","title":"Sorting by Visual Similarity","text":"

    PhotoPrism currently generates a perceptual hash while indexing, which can be used to sort search results by visual similarity, e.g.:

    • https://demo.photoprism.app/library/browse?view=cards&order=similar

    Note, though, that it is not yet possible to automatically stack files based on this and that the hash is not 100% precise, so visually different images may have the same hash. This is because it was developed for sorting and not for stacking or finding duplicates.

    "},{"location":"developer-guide/metadata/perceptual-hashes/#stacking-of-similar-files","title":"Stacking of Similar Files","text":"

    Developing a user-friendly web interface for semi-automatic stacking of a large number of images based on their appearance is expected to require a significant amount of work.

    A simpler solution could be to add one or more additional stacking options on the library settings page and allow them to be applied to existing pictures by pressing a button or running a terminal command, but without being able to see the result in advance or perform manual changes.

    See the following issues for related feature requests:

    • Stacks: Search + manually stack similar images #28
    • Stacks: Stack compressed and original version of a photo e.g. from Google Photos #1182
    "},{"location":"developer-guide/metadata/perceptual-hashes/#related-resources","title":"Related Resources","text":""},{"location":"developer-guide/metadata/perceptual-hashes/#software-libraries-examples","title":"Software Libraries & Examples","text":"
    • corona10/goimagehash
    • Nr90/imgsim
    • azr/phash
    • ajdnik/imghash
    • burntcarrot/hashsearch
    • dsoprea/go-perceptualhash
    • 9elt/fast-dhash
    • Estella/ImageDNA
    • jenssegers/imagehash
    "},{"location":"developer-guide/metadata/perceptual-hashes/#references-algorithms-tutorials","title":"References, Algorithms & Tutorials","text":"
    • https://en.wikipedia.org/wiki/Perceptual_hashing
    • https://www.microsoft.com/en-us/photodna
    • https://news.microsoft.com/en-gb/2013/11/18/tacklingproliferatio/
    "},{"location":"developer-guide/metadata/xmp/","title":"Adobe XMP","text":"

    XMP (Extensible Metadata Platform) is the standard sidecar file format supported by Adobe Lightroom. While YAML files might be easier to understand, read and edit for humans, using the XML-based XMP format simplifies importing metadata from Lightroom and we can leverage a documented standard. Ideally, data can be kept in sync continuously between PhotoPrism and other photo management applications.

    A proof-of-concept for reading Title, Copyright, Artist and Description is implemented but full support is a lot more work, contributions welcome. One issue is proper XML parsing in Go as basic types like date and time are not supported by xml.Unmarshaler. GPS coordinates are not stored as float but as a string like 52,27.5814N.

    The original plan to build upon go-xmp didn't work out as we couldn't read many fields, so we're using pure Go for now until we find a way to get the data we need with go-xmp. It might be a bug and/or it's an issue with our specific XMP files.

    "},{"location":"developer-guide/metadata/xmp/#raw-conversion","title":"RAW Conversion","text":"

    PhotoPrism currently supports Darktable and RawTherapee as RAW image converters (as well as Sips on macOS). Darktable fully supports XMP sidecar files, RawTherapee might only partially. However, XMP is only a \"container\" format, so the fields (namespaces) used there to indicate how an image should be converted (as well as other metadata) differ between Lightroom/Photoshop, Darktable, and RawTherapee.

    In other words, just because an application generally supports XMP that doesn't mean it can use metadata created with another application or by another vendor like Adobe. If you think that's confusing, well, that's because it is. You have an open format, but you still suffer from vendor lock-in - probably not entirely unintentional on Adobe's part.

    From our experience, some basic edits done with Adobe tools - such as cropping - might be preserved when you convert the same RAW image with other software like Darktable. Advanced edits, such as lens or color corrections, will likely not be applied.

    Learn more \u203a

    "},{"location":"developer-guide/metadata/xmp/#file-samples","title":"File Samples","text":"

    We would be happy to receive more XMP files for testing, either via pull request or email.

    "},{"location":"developer-guide/metadata/xmp/#specification","title":"Specification","text":"
    • Part 1: Data and Serialization Model
    • Part 2: Standard Schemas
    • Part 3: Storage in Files
    • Adobe XMP Programmers Guide
    • Adobe XMP Files Plugin SDK
    • Adobe BSD 3-Clause License and XMP Toolkit SDK
    "},{"location":"developer-guide/metadata/xmp/#open-issues","title":"Open Issues","text":"
    • Experiment with Adobe Lightroom to see how it uses sidecar files. The new version doesn't seem to use XMP to automatically sync metadata anymore, probably because Adobe focuses on cloud storage. Needs further investigation.
    • Create a matrix showing what fields are used/supported by which application/tool (Photoshop, Lightroom, Darktable and others, see
    • Read http://www.exiv2.org/tags-xmp-crs.html (Camera Raw Schema)
    "},{"location":"developer-guide/metadata/xmp/#released-features","title":"Released Features","text":"
    • Store metadata in the filesystem #4
    • Compare the quality and XMP compatibility of different RAW converters #65
    "},{"location":"developer-guide/metadata/xmp/#external-resources","title":"External Resources","text":"
    • trimmer-io/go-xmp - A native Go SDK for the Extensible Metadata Platform (XMP)
    • XMP code in GIMP - Nothing beyond some comments. It was a code drop, we needed the feature, but unfortunately the original contributor left.
    "},{"location":"developer-guide/metadata/exif/","title":"Exif Extraction","text":"

    PhotoPrism uses the dsoprea/go-exif package to natively extract EXIF information from images. Exiftool support can be optionally enabled to support a wider range of metadata formats and extraction from video files.

    • see internal/meta/exif.go for which tags are integrated into PhotoPrism
    • see official EXIF specification for more information about EXIF tags
    "},{"location":"developer-guide/metadata/exif/#using-exiftool","title":"Using Exiftool","text":"

    Assuming you have Docker installed and want to run exiftool with Debian 12 \"Bookworm\", you can simply run this command to open a terminal:

    docker run --rm -v ${PWD}:/test -w /test -ti debian:bookworm bash\n

    This will mount the current working directory as /test. Of course, you can also specify a full path instead of ${PWD}.

    The available Ubuntu, Debian and PhotoPrism images can be found on Docker Hub:

    • https://hub.docker.com/_/ubuntu
    • https://hub.docker.com/_/debian
    • https://hub.docker.com/r/photoprism/photoprism/tags

    Now install exiftool and any other packages you need, e.g. libheif-examples to convert HEIF images to JPEG, via apt:

    apt update\napt install -y exiftool libheif-examples\n

    To view the image metadata, run exiftool -n <filename>:

    root@1ad9fb887a4f:/test# exiftool -n IMG_8437.HEIC.jpg | grep ation\nFile Modification Date/Time     : 2022:09:18 08:16:28+00:00\nOrientation                     : 6\nExposure Compensation           : 0\nroot@1ad9fb887a4f:/test# exiftool -n IMG_8437.HEIC | grep ation\nFile Modification Date/Time     : 2022:09:17 16:57:40+00:00\nOrientation                     : 6\nExposure Compensation           : 0\nHEVC Configuration Version      : 1\nMin Spatial Segmentation IDC    : 0\nRotation                        : 270\n
    "},{"location":"developer-guide/metadata/exif/#exiftool-parameters","title":"Exiftool Parameters","text":"
    • -n displays the raw values without changes
    • -j will format the output as JSON
    • -g groups the output by metadata source
    "},{"location":"developer-guide/metadata/exif/#screenshots","title":"Screenshots","text":"

    This is how other apps show metadata:

    "},{"location":"developer-guide/metadata/exif/edit/","title":"Editing Exif Data","text":"

    You can install ExifTool via homebrew, another package manager, or from the homepage:

    https://exiftool.org/

    The Exif-Read-Tool can be found on GitHub:

    dsoprea/go-exif

    "},{"location":"developer-guide/metadata/exif/edit/#show-exif-data-for-debugging","title":"Show exif data for debugging","text":""},{"location":"developer-guide/metadata/exif/edit/#show-exif-data-read-from-exiftool","title":"Show exif data read from exiftool","text":"

    exiftool -j photo.jpg

    "},{"location":"developer-guide/metadata/exif/edit/#show-exif-data-read-from-exif-read-tool","title":"Show exif data read from exif-read-tool","text":"

    exif-read-tool -f photo.jpg

    "},{"location":"developer-guide/metadata/exif/edit/#edit-exif-data-for-testing","title":"Edit EXIF data for testing","text":""},{"location":"developer-guide/metadata/exif/edit/#set-createdate","title":"Set CreateDate","text":"

    exiftool -CreateDate=\"1919:05:04 05:59:26+02:00\" dog_toshi_yellow.jpg

    "},{"location":"developer-guide/metadata/exif/edit/#remove-all-gps-data","title":"Remove all GPS data","text":"

    exiftool -GPS:all= file.jpg

    "},{"location":"developer-guide/metadata/exif/edit/#remove-all-exif-data","title":"Remove all exif data","text":"

    exiftool -all= foo.jpg

    Further examples can be found here: https://exiftool.org/examples.html

    "},{"location":"developer-guide/native-apps/","title":"Native Mobile Apps for iOS and Android","text":"

    As an addition to our platform-independent Progressive Web App (PWA), the following native apps are being maintained by other companies and individual developers:

    Name Platform Developer License Download Gallery for PhotoPrism Android Oleg Koretsky GPL 3.0 Google Play, F-Droid Photo Map Android Denny Weinberg Closed Source Google Play PhotoSync iOS, Android touchbyte GmbH Closed Source App Store, Google Play Photoflare iOS Chris Wunsch n/a TestFlight Prismatic iOS Chris Li n/a TestFlight"},{"location":"developer-guide/native-apps/#gallery-for-photoprism","title":"Gallery for PhotoPrism","text":"

    With this Android app, you can easily browse the pictures in your library and share them with other apps. The app offers a timeline view, authentication, bookmarks, and many other useful features. It can also be installed on Android TV, so you can browse your library with a remote control.

    "},{"location":"developer-guide/native-apps/#photo-map","title":"Photo Map","text":"

    Photo Map is a perfect Android app for travelers: It displays all your pictures on an interactive world map, so you never lose track of when and where you took them. With the latest update, videos are now played directly in the app. Also, the number of photos that can be displayed is no longer limited.

    "},{"location":"developer-guide/native-apps/#photosync","title":"PhotoSync","text":"

    PhotoSync lets you transfer photos and videos directly from your mobile phone. It backs up your pictures securely in the background or you can manually select files to upload into specific folders.

    "},{"location":"developer-guide/native-apps/#photoflare","title":"Photoflare","text":"

    Photoflare is an iOS app that is currently being developed by Chris Wunsch. It is at an early stage and already offers some basic functionality that can be tested via Apple TestFlight.

    Learn more \u203a

    "},{"location":"developer-guide/native-apps/#prismatic","title":"Prismatic","text":"

    Prismatic is an iOS app developed by Chris Li that allows you to display pictures from your library in customizable widgets on your phone's home screen.

    You can test this and other features by installing it via Apple TestFlight.

    Give feedback \u203a

    "},{"location":"developer-guide/security/","title":"Security Testing Guide","text":""},{"location":"developer-guide/security/#vulnerability-scanners","title":"Vulnerability Scanners","text":"

    Nuclei is a fast and customizable vulnerability scanner based on a simple YAML-based DSL. It uses community curated templates to find vulnerabilities in applications.

    On a Mac, you can install it via Homebrew by running the following command:

    brew install nuclei\n
    "},{"location":"developer-guide/security/#web-security-checklist","title":"Web Security Checklist","text":"

    The following OWASP\u00ae Web Application Security Checklist is also available as PDF or Docx for printing:

    "},{"location":"developer-guide/security/#information-gathering","title":"Information Gathering","text":"
    • Manually explore the site
    • Spider/crawl for missed or hidden content
    • Check for files that expose content, such as robots.txt, sitemap.xml, .DS_Store
    • Check the caches of major search engines for publicly accessible sites
    • Check for differences in content based on User Agent (eg, Mobile sites, access as a Search engine Crawler)
    • Perform Web Application Fingerprinting
    • Identify technologies used
    • Identify user roles
    • Identify application entry points
    • Identify client-side code
    • Identify multiple versions/channels (e.g. web, mobile web, mobile app, web services)
    • Identify co-hosted and related applications
    • Identify all hostnames and ports
    • Identify third-party hosted content
    "},{"location":"developer-guide/security/#configuration-management","title":"Configuration Management","text":"
    • Check for commonly used application and administrative URLs
    • Check for old, backup and unreferenced files
    • Check HTTP methods supported and Cross Site Tracing (XST)
    • Test file extensions handling
    • Test for security HTTP headers (e.g. CSP, X-Frame-Options, HSTS)
    • Test for policies (e.g. Flash, Silverlight, robots)
    • Test for non-production data in live environment, and vice-versa
    • Check for sensitive data in client-side code (e.g. API keys, credentials)
    "},{"location":"developer-guide/security/#secure-transmission","title":"Secure Transmission","text":"
    • Check SSL Version, Algorithms, Key length
    • Check for Digital Certificate Validity (Duration, Signature and CN)
    • Check credentials only delivered over HTTPS
    • Check that the login form is delivered over HTTPS
    • Check session tokens only delivered over HTTPS
    • Check if HTTP Strict Transport Security (HSTS) in use
    "},{"location":"developer-guide/security/#authentication","title":"Authentication","text":"
    • Test for user enumeration
    • Test for authentication bypass
    • Test for bruteforce protection
    • Test password quality rules
    • Test remember me functionality
    • Test for autocomplete on password forms/input
    • Test password reset and/or recovery
    • Test password change process
    • Test CAPTCHA
    • Test multi factor authentication
    • Test for logout functionality presence
    • Test for cache management on HTTP (eg Pragma, Expires, Max-age)
    • Test for default logins
    • Test for user-accessible authentication history
    • Test for out-of channel notification of account lockouts and successful password changes
    • Test for consistent authentication across applications with shared authentication schema / SSO
    "},{"location":"developer-guide/security/#session-management","title":"Session Management","text":"
    • Establish how session management is handled in the application (eg, tokens in cookies, token in URL)
    • Check session tokens for cookie flags (httpOnly and secure)
    • Check session cookie scope (path and domain)
    • Check session cookie duration (expires and max-age)
    • Check session termination after a maximum lifetime
    • Check session termination after relative timeout
    • Check session termination after logout
    • Test to see if users can have multiple simultaneous sessions
    • Test session cookies for randomness
    • Confirm that new session tokens are issued on login, role change and logout
    • Test for consistent session management across applications with shared session management
    • Test for session puzzling
    • Test for CSRF and clickjacking
    "},{"location":"developer-guide/security/#authorization","title":"Authorization","text":"
    • Test for path traversal
    • Test for bypassing authorization schema
    • Test for vertical Access control problems (a.k.a. Privilege Escalation)
    • Test for horizontal Access control problems (between two users at the same privilege level)
    • Test for missing authorization
    "},{"location":"developer-guide/security/#data-validation","title":"Data Validation","text":"
    • Test for Reflected Cross Site Scripting
    • Test for Stored Cross Site Scripting
    • Test for DOM based Cross Site Scripting
    • Test for Cross Site Flashing
    • Test for HTML Injection
    • Test for SQL Injection
    • Test for LDAP Injection
    • Test for ORM Injection
    • Test for XML Injection
    • Test for XXE Injection
    • Test for SSI Injection
    • Test for XPath Injection
    • Test for XQuery Injection
    • Test for IMAP/SMTP Injection
    • Test for Code Injection
    • Test for Expression Language Injection
    • Test for Command Injection
    • Test for Overflow (Stack, Heap and Integer)
    • Test for Format String
    • Test for incubated vulnerabilities
    • Test for HTTP Splitting/Smuggling
    • Test for HTTP Verb Tampering
    • Test for Open Redirection
    • Test for Local File Inclusion
    • Test for Remote File Inclusion
    • Compare client-side and server-side validation rules
    • Test for NoSQL injection
    • Test for HTTP parameter pollution
    • Test for auto-binding
    • Test for Mass Assignment
    • Test for NULL/Invalid Session Cookie
    "},{"location":"developer-guide/security/#denial-of-service","title":"Denial of Service","text":"
    • Test for anti-automation
    • Test for account lockout
    • Test for HTTP protocol DoS
    • Test for SQL wildcard DoS
    "},{"location":"developer-guide/security/#business-logic","title":"Business Logic","text":"
    • Test for feature misuse
    • Test for lack of non-repudiation
    • Test for trust relationships
    • Test for integrity of data
    • Test segregation of duties
    "},{"location":"developer-guide/security/#cryptography","title":"Cryptography","text":"
    • Check if data which should be encrypted is not
    • Check for wrong algorithms usage depending on context
    • Check for weak algorithms usage
    • Check for proper use of salting
    • Check for randomness functions
    "},{"location":"developer-guide/security/#html-5","title":"HTML 5","text":"
    • Test Web Messaging
    • Test for Web Storage SQL injection
    • Check CORS implementation
    • Check Offline Web Application
    "},{"location":"developer-guide/security/#risky-functionality","title":"Risky Functionality","text":""},{"location":"developer-guide/security/#file-uploads","title":"File Uploads","text":"
    • Test that acceptable file types are whitelisted
    • Test that file size limits, upload frequency and total file counts are defined and are enforced
    • Test that file contents match the defined file type
    • Test that all file uploads have Anti-Virus scanning in-place.
    • Test that unsafe filenames are sanitised
    • Test that uploaded files are not directly accessible within the web root
    • Test that uploaded files are not served on the same hostname/port
    • Test that files and other media are integrated with the authentication and authorisation schemas
    "},{"location":"developer-guide/security/#card-payment","title":"Card Payment","text":"
    • Test for known vulnerabilities and configuration issues on Web Server and Web Application
    • Test for default or guessable password
    • Test for non-production data in live environment, and vice-versa
    • Test for Injection vulnerabilities
    • Test for Buffer Overflows
    • Test for Insecure Cryptographic Storage
    • Test for Insufficient Transport Layer Protection
    • Test for Improper Error Handling
    • Test for all vulnerabilities with a CVSS v2 score > 4.0
    • Test for Authentication and Authorization issues
    • Test for CSRF

    Source: 0xRadi/OWASP-Web-Checklist

    "},{"location":"developer-guide/security/#openssf-security-criteria","title":"OpenSSF Security Criteria","text":"

    The Open Source Security Foundation maintains standardized security criteria and best practices for open-source projects:

    "},{"location":"developer-guide/security/go/","title":"Secure Coding in Go","text":"

    The Go Language: Web Application Secure Coding Practices is a book written for anyone who is using the Go Programming Language and aims to use it for Web development.

    You can download it in the following formats:

    • PDF
    • Mobi
    • and ePub

    This book is collaborative effort of Checkmarx Security Research Team and it follows the OWASP Secure Coding Practices - Quick Reference Guide v2 (stable) release.

    Source: OWASP/Go-SCP

    "},{"location":"developer-guide/security/policy/","title":"Vulnerability Disclosure Guidelines","text":"

    Visit photoprism.app/security-policy to learn more about our security policy, responsible disclosure, and how you can report issues as a business or organization.

    "},{"location":"developer-guide/technologies/broadway/","title":"Broadway","text":"

    Broadway is a display server for using GTK+ applications in a web browser like Chrome or Firefox. It is based on HTML5 and WebSockets. GTK (originally GIMP Toolkit) is a GUI toolkit used by many Linux desktop applications like Darktable, Gimp and RawTherapee. Obviously this technology is ideal for editing images in the browser, at least when the connection is fast enough.

    "},{"location":"developer-guide/technologies/broadway/#docker-configuration","title":"Docker Configuration","text":"
    # Configure broadwayd (HTML5 display server)\nRUN apt-get update && apt-get install libgtk-3-bin\nENV GDK_BACKEND broadway\nENV BROADWAY_DISPLAY :5\nEXPOSE 8080\nCMD broadwayd -p 8080 -a 0.0.0.0 :5\n

    When this is configured, all you need to do is open http://localhost:8080 in a browser and start any GTK application in the same container (broadwayd must be running). It should be displayed instantly, otherwise the screen is white, but you shouldn't see an error.

    "},{"location":"developer-guide/technologies/broadway/#screenshot","title":"Screenshot","text":""},{"location":"developer-guide/technologies/docker/","title":"Docker","text":"

    Docker is an Open Source container virtualization tool. It is ideal for running applications on any computer without extensive installation, configuration, or performance overhead.

    We are aware Docker is not widely used by end users despite its many advantages. For this reason, we aim to provide native binaries for common operating systems at a later time.

    "},{"location":"developer-guide/technologies/docker/#what-are-the-benefits-of-using-docker","title":"What are the benefits of using Docker?","text":"

    (1) Docker uses standard features of the Linux kernel. Containers are nothing new; Solaris Zones were released about 20 years ago and the chroot system call was introduced during development of Version 7 Unix in 1979. It is used ever since for hosting applications exposed to the public Internet. Modern Linux containers are an incremental improvement of this, based on standard functionality that is part of the kernel.

    (2) Docker saves time through simplified deployment and testing. A main advantage of Docker is that application images can be easily made available to users via Internet. It provides a common standard across most operating systems and devices, which saves our team a lot of time that we can then spend more effectively, for example, providing support and developing one of the many features that users are waiting for.

    (3) Dockerfiles are part of the source code repository. Human-readable and versioned Dockerfiles that are part of our public source code help avoid \"works for me\" moments and other unwelcome surprises by enabling us to have the exact same environment everywhere in development, staging, and production.

    (4) Running applications in containers is more secure. Last but not least, virtually all file format parsers have vulnerabilities that just haven't been discovered yet. This is a known risk that can affect you even if your computer is not directly connected to the Internet. Running apps in a container with limited host access is an easy way to improve security without compromising performance and usability.

    A virtual machine with a dedicated operating system environment provides even more security, but usually has side effects such as lower performance and more difficult handling. Using a VM, however, doesn't prevent you from running containerized apps to get the best of both worlds. This is essentially what happens when you install Docker on virtual cloud servers and operating systems other than Linux.

    "},{"location":"developer-guide/technologies/docker/#running-docker-images","title":"Running Docker Images","text":"

    Assuming you have Docker installed and want to test Debian 12 \"Bookworm\", you can simply run this command to open a terminal:

    docker run --rm -v ${PWD}:/test -w /test -ti debian:bookworm bash\n

    This will mount the current working directory as /test. Of course, you can also specify a full path instead of ${PWD}.

    The available Ubuntu, Debian and PhotoPrism images can be found on Docker Hub:

    • https://hub.docker.com/_/ubuntu
    • https://hub.docker.com/_/debian
    • https://hub.docker.com/r/photoprism/photoprism/tags

    Additional packages can be installed via apt:

    apt update\napt install -y exiftool libheif-examples\n
    "},{"location":"developer-guide/technologies/docker/#continuous-integration-deployment","title":"Continuous Integration / Deployment","text":"

    Build and push of an updated container image to Docker Hub is automatically performed by Travis CI whenever develop is merged into master and the tests are all green. For that reason, we don't use semantic versioning for our binaries and container images. A version string might look like 181112-edc7c2f-Darwin-i386-DEBUG instead. Travis CI uses the photoprism/development image for running unit and integration tests on all branches and for pull requests, see Dockerfile.

    "},{"location":"developer-guide/technologies/docker/#multi-stage-build","title":"Multi-Stage Build","text":"

    When creating new images, Docker supports so called multi-stage builds, that means you can compile an application like PhotoPrism in a container that contains all development dependencies (like source code, debugger, compiler,...) and later copy the binary to a fresh container. This way we could reduce the compressed container size from ~1 GB to less than 200 MB. Most of that is used by Darktable, TensorFlow and Ubuntu 18.04. Our photoprism binary is smaller than 20 MB.

    Example:

    FROM photoprism/development:20181112 as build\n\n# Build PhotoPrism\nWORKDIR \"/go/src/github.com/photoprism/photoprism\"\nCOPY . .\nRUN make all install DESTDIR=/opt/photoprism\n\n# Base base image as photoprism/development\nFROM ubuntu:18.04\n\nWORKDIR /opt/photoprism\n\n# Copy built binaries and assets to this image\nCOPY --from=build /usr/local/bin/photoprism /usr/local/bin/photoprism\nCOPY --from=build /opt/photoprism /opt/photoprism\n\n# Expose HTTP port\nEXPOSE 80\n\n# Start PhotoPrism server\nCMD photoprism start\n
    "},{"location":"developer-guide/technologies/docker/#kubernetes","title":"Kubernetes","text":"
    • https://forge.sh/ - Define and deploy multi-container apps in Kubernetes, from source
    • https://www.telepresence.io/ - a local development environment for a remote Kubernetes cluster
    "},{"location":"developer-guide/technologies/docker/#external-resources","title":"External Resources","text":"
    • estesp/manifest-tool
    • docker/app
    • moby/moby
    • https://hub.docker.com/r/multiarch/qemu-user-static/ - quemu for building multiarch images with Docker
    • https://github.com/opencontainers/image-spec - standard labels for Docker image metadata
    • Yelp/dumb-init - A minimal init system for Linux containers
    "},{"location":"developer-guide/technologies/external-apis/","title":"External APIs","text":""},{"location":"developer-guide/technologies/external-apis/#photo-sharing","title":"Photo Sharing","text":"
    • Instagram Developer API
    • Twitter API
    • Facebook Photo API
    • Google Photos API
    • EyeEm API
    • Nextcloud API
    "},{"location":"developer-guide/technologies/external-apis/#books-prints","title":"Books / Prints","text":"
    • Cewe Stiftung & Co. KGaA - Europe's leading online printing experts
    "},{"location":"developer-guide/technologies/external-apis/#tools","title":"Tools","text":"
    • KrakenD - API Gateway that sits between clients and services to offer tailored endpoints
    • Photobak - Back up your content from Google Photos
    "},{"location":"developer-guide/technologies/golang/","title":"Golang","text":"

    We have chosen Go because of its simplicity and performance. On top, it enables us to build a single binary for distribution.

    "},{"location":"developer-guide/technologies/golang/#learning","title":"Learning","text":"
    • https://golang.org/doc/code.html - how to write Go code
    • https://golang.org/doc/effective_go.html - gives tips for writing clear, idiomatic Go code
    • https://golangbridge.org/ - Go workshops for underrepresented communities
    • https://www.sohamkamani.com/blog/golang/2018-06-20-golang-factory-patterns/ - there are many different ways in which you can use the factory pattern to make your code cleaner and more concise
    • https://www.alexedwards.net/blog/form-validation-and-processing - Form Validation and Processing in Go
    "},{"location":"developer-guide/technologies/golang/#testing","title":"Testing","text":"
    • https://medium.com/@povilasve/go-advanced-tips-tricks-a872503ac859 - Go advanced testing tips & tricks
    • Golang version of Pact - contract testing framework for HTTP APIs and non-HTTP asynchronous messaging systems
    • Integration Tests in Go - blog post
    • Parallelize your table-driven tests - blog post
    "},{"location":"developer-guide/technologies/golang/#useful-libraries-and-frameworks","title":"Useful libraries and frameworks","text":"
    • spf13/afero - a filesystem abstraction library
    • gin-gonic/gin - HTTP web framework
    • gobuffalo/pop - a database abstraction layer
    • jinzhu/gorm - another database abstraction layer
    • appleboy/gorush - a push notification micro server for Gin
    • cavaliercoder/grab - a download manager package
    • russross/blackfriday - a library for rendering Markdown
    • sirupsen/logrus - structured, pluggable logging for Go
    • alecthomas/participle - a dead simple parser package for Go
    • tidwall/tile38 - a geospatial database and realtime geofencing server
    • melihmucuk/geocache - an in-memory cache that is suitable for geolocation based applications
    • aaronland/go-slippy-tiles - a proxy for map tiles
    • paulmach/osm - a general purpose library for reading, writing and working with OpenStreetMap data
    • @minio - Object Storage for AI
    • http://www.gorillatoolkit.org/pkg/schema - fills a struct with form values
    "},{"location":"developer-guide/technologies/golang/#jobs","title":"Jobs","text":"
    • SoundCloud is looking for backend developers (Berlin, permanent, full-time)
    "},{"location":"developer-guide/technologies/tensorflow/","title":"TensorFlow","text":""},{"location":"developer-guide/technologies/tensorflow/#using-tensorflow-with-go","title":"Using TensorFlow with Go","text":"

    For an introduction please read Understanding Tensorflow using Go.

    The TensorFlow API for Go is well suited to loading existing models and executing them within a Go application. It requires the TensorFlow C library to be installed. A full TensorFlow installation is not needed.

    It is not possible to statically link against the C library, but the issue is known and there might be a fix later this year.

    "},{"location":"developer-guide/technologies/tensorflow/#vision","title":"Vision","text":"

    Our long-term goal is to become an open platform for machine learning research based on real-world photo collections.

    "},{"location":"developer-guide/technologies/tensorflow/#external-resources","title":"External Resources","text":"
    • https://tfhub.dev/ - TensorFlow Hub is a library for reusable machine learning modules
    • https://www.tensorflow.org/install/lang_c - TensorFlow for C
    • http://www.asimovinstitute.org/neural-network-zoo/ - types of neural networks explained
    • http://playground.tensorflow.org/ - Experiment with neural networks in your browser
    • http://jupyter.org/ - open-source web application for creating and sharing documents that contain live code
    • https://developers.google.com/machine-learning/crash-course/ - Machine Learning Crash Course with TensorFlow APIs
    • https://medium.com/implodinggradients/tensorflow-or-keras-which-one-should-i-learn-5dd7fa3f9ca0
    • https://medium.com/analytics-vidhya/deploy-your-first-deep-learning-model-on-kubernetes-with-python-keras-flask-and-docker-575dc07d9e76 - Deploy Your First Deep Learning Model On Kubernetes With Python, Keras, Flask, and Docker
    • https://medium.com/mlreview/getting-inception-architectures-to-work-with-style-transfer-767d53475bf8 - Getting Inception Architectures to Work with Style Transfer
    • jdeng/goface - Face Detector based on MTCNN, tensorflow and golang
    • https://www.tensorflow.org/tutorials/representation/word2vec - Vector Representations of Words (for searching/tagging)
    • chtorr/go-tensorflow-realtime-object-detection - Real-time object detection with Go, Tensorflow, and OpenCV
    • https://ai.googleblog.com/2018/07/accelerated-training-and-inference-with.html - Accelerated Training and Inference with the Tensorflow Object Detection API
    • NanoNets/object-detection-sample-golang - NanoNets Object Detection API Example for Golang
    • https://hub.packtpub.com/object-detection-go-tensorflow/ - Implementing Object detection with Go using TensorFlow
    "},{"location":"developer-guide/technologies/yaml/","title":"Introduction to YAML","text":"

    YAML is a human-friendly format that we use for metadata exports and configuration files because of its simplicity and widespread support. The name originally meant Yet Another Markup Language. Common file extensions are .ymland .yaml.

    Values are represented in the form key: value with one entry per line:

    Type: image\nTitle: \"La Tour Eiffel \ud83c\udf08\"\nYear: 2014\n# Key-Value Collection Example:\nDetails:\n  Notes: \"Bonjour\\nla France!\" \n  Keywords: 'paris, france' # Comment\n
    "},{"location":"developer-guide/technologies/yaml/#basic-rules","title":"Basic Rules","text":"
    • Keys are case-sensitive
    • Related values must start at the same indentation level
      • We recommend using 2 spaces, but any number will work as long as it is consistent
      • Tabs are not allowed for indentation and using them may result in errors
    • Comments begin with #, can start anywhere on a line, and continue until the end of the line
    • You can generally use all Unicode characters in YAML files, including Emojis
      • To avoid ambiguity, it is recommended to enclose text strings in single ' or double quotes \", especially if they contain spaces or Boolean values like \"true\" or \"false\"
      • The difference between single and double quotes is that double quotes support escape sequences like \\t for a tab or \\n for a new line
      • Additional special characters may need to be escaped, e.g. the $ sign when working with Docker Compose
    "},{"location":"developer-guide/technologies/yaml/#multiple-values","title":"Multiple Values","text":"

    List are lines that start at the same indentation level and begin with a dash and a space as shown in the example below. They are commonly used to define service dependencies, exposed network ports, or folders shared between host and container in docker-compose.yml files:

    services:\n  photoprism:\n    depends_on:\n      - mariadb\n      - nextcloud\n    ports:\n      # \"host:container\"\n      - \"2342:2342\"\n    volumes:\n      # \"/host/folder:/container/folder\"\n      - \"/photos:/photoprism/originals\"\n
    "},{"location":"developer-guide/technologies/yaml/#key-value-pairs","title":"Key-Value Pairs","text":"

    Collections of key-value pairs are commonly used to specify the names and values of environment variables in docker-compose.yml files (see below for additional rules). Similar to lists, the keys of related values start at the same indentation level, but without a dash:

    services:\n  mariadb:\n    environment:\n      MARIADB_AUTO_UPGRADE: \"1\"\n      MARIADB_INITDB_SKIP_TZINFO: \"1\"\n      MARIADB_ROOT_PASSWORD: \"Y(&^UIk34\"\n      MARIADB_DATABASE: photoprism\n      MARIADB_USER: photoprism\n      MARIADB_PASSWORD: insecure\n
    "},{"location":"developer-guide/technologies/yaml/#docker-compose","title":"Docker Compose","text":"

    When using Docker Compose, some additional rules apply, as compose.yaml or docker-compose.yml files extend the YAML format with features such as variable interpolation.

    "},{"location":"developer-guide/technologies/yaml/#dollar-signs","title":"Dollar Signs","text":"

    If a configuration value in a compose.yaml or docker-compose.yml file contains a literal $ character, for example in a password, you must use $$ (a double dollar sign) to escape it so that e.g. \"compo$e\" becomes \"compo$$e\":

    services:\n  mariadb:\n    environment:\n      # sets password to \"compo$e\"\n      MARIADB_PASSWORD: \"compo$$e\" \n

    Values that contain a $ are otherwise interpreted as a variable. In this case, both the $VARIABLE and the ${VARIABLE} syntax are supported. Further details on the use of variables can be found in the file format reference.

    "},{"location":"developer-guide/technologies/yaml/#true-false","title":"True / False","text":"

    Boolean variable values like \"true\", \"false\", \"yes\", \"no\", \"on\", or \"off\" must be enclosed in double or single quotes so that they are passed as intended:

    services:\n  photoprism:\n    environment:\n      PHOTOPRISM_DEFAULT_TLS: \"true\"\n      PHOTOPRISM_READONLY: \"false\"\n

    If you otherwise specify true as a value without quotes, Docker Compose will pass the host variable of the same name to the container instead of setting the value to \"true\" (results in an empty string if no environment variable with the same name is set on the host):

    services:\n  photoprism:\n    environment:\n      # evaluated as \"\" (false)\n      PHOTOPRISM_READONLY: true\n
    "},{"location":"developer-guide/ui/browsers/","title":"Browsers","text":"

    We should at least support the latest Firefox and Chrome on Linux, OS X and Windows. Ideally also Safari (OS X) and Explorer/Edge (Windows).

    "},{"location":"developer-guide/ui/browsers/#testing","title":"Testing","text":"
    • BrowserStack is free for open-source projects
    "},{"location":"developer-guide/ui/components/","title":"Components","text":"

    Our frontend code is structured into reusable VueJS components. So far, it was clear enough for developers to add features and send pull requests, so it's probably not necessary to document individual components in our Developer Guide. Let us know via email or chat when you have any questions!

    Frontend contains general information about the front-end libraries we use and how to trigger a build.

    "},{"location":"developer-guide/ui/design/","title":"Design & Colors","text":"

    We strive for a user interface that is clutter-free, well-organized, and doesn't behave in unexpected ways. It should work across a wide range of devices and be easy to use for everyone.

    "},{"location":"developer-guide/ui/design/#theme-colors","title":"Theme Colors","text":"

    The colors we use should be consistent and functional, for example, provide sufficient contrast. For the included themes, the preferred primary colors are violet and cyan, but other colors can be used as well.

    To avoid distorting the visual impression of photos and videos, large background areas should generally be neutral or just slightly saturated.

    "},{"location":"developer-guide/ui/design/#context-menu","title":"Context Menu","text":"

    The context menu at the bottom right should use a color spectrum for the individual actions to reflect the spectral colors of a prism:

    "},{"location":"developer-guide/ui/design/#icon-fonts","title":"Icon Fonts","text":"

    We stick to Google's Material icons and use other icons only when absolutely necessary:

    • https://material.io/resources/icons/
    • https://jossef.github.io/material-design-icons-iconfont/
    "},{"location":"developer-guide/ui/design/#inspirational-quotes","title":"Inspirational Quotes","text":"

    Design is a funny word. Some people think design means how it looks. But of course, if you dig deeper, it's really how it works.\u2014 Steve Jobs

    Choice is the enemy of productivity. Put another way, if your solution does everything, and has no opinions about anything, then it solves nothing.\u2014 Asim Aslam

    Any fool can make something complicated. It takes a genius to make it simple.\u2014 Woody Guthrie

    "},{"location":"developer-guide/ui/design/#external-resources","title":"External Resources","text":""},{"location":"developer-guide/ui/design/#color-schemes","title":"Color Schemes","text":"
    • https://www.nordtheme.com/#palettes-modularity
    • Scheme Color Finder
      • https://www.schemecolor.com/?s=colorful
      • https://www.schemecolor.com/?s=google
      • https://www.schemecolor.com/memories-of-the-garden.php
      • https://www.schemecolor.com/double-disclosure.php
      • https://www.schemecolor.com/true-lovers-color-scheme.php
      • https://www.schemecolor.com/sunset-painting.php
      • https://www.schemecolor.com/happy-with-self.php
    "},{"location":"developer-guide/ui/design/#web-color-tools","title":"Web Color Tools","text":"
    • Adobe Color Wheel
    • Artyclick Color Name Finder
    • Coolors Color Palette Generator
    • FFFuel HEX, RGB & HSL Color Picker
    • iColorpalette
    "},{"location":"developer-guide/ui/design/#related-content","title":"Related Content","text":"
    • Screenshots - development of our user interface in time lapse \u23f1
    • The 7 pillars of design - slides by Raffaella Isidori (Codemotion Berlin 2018)
    "},{"location":"developer-guide/ui/infinite-scrolling/","title":"Infinite Scrolling","text":"

    There are two problems to solve when allowing the user to scroll through an \"infinite\" number of elements/photos:

    1. You can't load an infinite amount of data.
    2. You can't render an infinite amount of elements.

    The solution to both is to not provide an infinite amount of elements, but instead create the illusion of an infinite amount of elements by only loading and displaying what the user would actually currently be able to see.

    "},{"location":"developer-guide/ui/infinite-scrolling/#loading-data-progressively","title":"Loading Data - Progressively","text":"

    The problem of infinite data is solved by loading the actually required elements progressively, in fixed amounts (batches).

    • Load a batch of elements. If they don't fill the screen, load the next batch. repeat until the screen is filled.
    • When the user is getting close to the end of the currently loaded elements, load the next batch of data.

    In an ideal world the \"next batch\" is always loaded fast and early enough so that the user doesn't even notice that it wasn't there from the beginning. This creates the illusion of having an infinite amount of data available. There are two parameters to consider:

    1. When to start loading the next batch?
      • loading to early results in to much unnecessary data getting laoded.
      • loading to late results in the user bumping into the end of the list of elements, because the next batch hasn't finished loading yet.
    2. How large are the batches?
      • to small batches can slow down the overall loading time by resulting in overhad because of too many requests.
      • to large batches need to long to load for a single batch, so that the data may not yet be ready when it is needed.

    The best values for these parameters vary vastly depending on the current network speed, number of elements that fit on the screen and the speed the user is scrolling. Luckily these values don't need to be perfect, just good enough.

    We currently use vue-infinite-scroll to detect when the user is about to reacht the end of the currently loaded list, so we can load the next batch. The batchsize depends on the current view and is currently somewhere in the range of 50 - 300 elements.

    "},{"location":"developer-guide/ui/infinite-scrolling/#rendering-elements-virtualized","title":"Rendering elements - Virtualized","text":"

    The further a user scrolls, the more batches of elements get loaded and displayed. The more elements get displayed, the slower the browser gets and the more memory it needs. This means, if we were to just render all loaded elements, the limit how far you can scroll would be entirely determined by the available cpu and memory of the client.

    "},{"location":"developer-guide/ui/infinite-scrolling/#regular-virtualization","title":"Regular virtualization","text":"

    This problem is usually solved by virtualization:

    1. determine the scrollposition and screensize of the client.
    2. calculate what elements would be on the screen.
    3. render only those elements.

    The problem with this regular virtualization is that it requires the elements to be positioned absolutely and may prescribe how they are structured. Implementing it would therefore imply a potentialy larger rewrite and less freedom when designing the elements.

    "},{"location":"developer-guide/ui/infinite-scrolling/#pseudo-virtualization-with-placeholders","title":"Pseudo-Virtualization with placeholders","text":"

    Using the IntersectionObserver API we can efficiently determine wether something is currently visible or not. We can use this information to replace all elements that are currently not in the visible area with simple placeholders of the same size. This drastically reduces the load on the browser, because these (often single-domnode) placeholder-elements require a LOT less ressources.

    This has the huge benefit that it doesn't restrict how components are structured or positioned, while also being easier to implement. There are however two caveats:

    1. We are still rendering something for every single loaded elements
      • The load on the browser therefore technically still increases (slightly) the more elements are loaded
      • This however reduces the load so much, that this slight increase per element barely matters at all
    2. The placeholders size should be as close to the originals size as possible.
      • If the sizes don't match, scrolling might become a little janky.

    This type of virtualization more than fast enough, and because we think the benefits outweigh the downsides, we decided go this route.

    "},{"location":"developer-guide/ui/infinite-scrolling/#implementation-details","title":"Implementation details","text":"

    The setup for a component that uses the placeholder-virtualization is as follows:

    1. Add a ref to all the elements whose visibilty needs to be tracked
    2. create a single IntersectionObserver in the beforeCreate that calls a (yet to be defined) this.visibilitiesChanged
    3. add a watcher that is called when the list of elements changes. call observe on all refs from step 1
    4. define a function that takes an IntersectionObserverEntry and returns the index of the corresponding target (for example by adding a data-index-attribute to the observed element)
    5. add firstVisibleElementIndex: 0, lastVisibleElementIndex: 0 and visibleElementIndices: new Set() to the components state
    6. conditionally render elements whose index is between firstVisibleElementIndex and lastVisibleElementIndex. Render placeholders for all other elements
    7. define visibilitiesChanged. Let it call virtualizationTools.updateVisibleElementIndices. Use the result as new this.firstVisibleElementIndex and this.lastVisibileElementIndex

    We use the visibleElementIndices-Set to keep track of elements that became visible or invisible. We also use firstVisibleElementIndex and lastVisibleElementIndex for two reasons:

    1. Vue doesn't react to Set-changes (because its identity never changes), so manipulating it doesn't cause a rerender
    2. When scrolling very fast, the set may for a very brief moment contain holes (for example it has the indices 1, 2, 4, 5, 6). By implying that everything between the smallest and largest index is visible, these short-lived holes don't have any negative effect (index 3 would be rendered anyway)

    As a bonus, you can make the IntersectionObserver only observe for example every 5th element to speed up calculation of intersections. If you do so, you should add for Example -4 to firstVisibleElementIndex and +4 to lastVisibleElementIndex

    "},{"location":"developer-guide/ui/infinite-scrolling/#render-performance","title":"Render Performance","text":"

    When working with a huge amount of elements, render performance of these elements is critically important. The better the render performance, the more it actually feels like scrolling through an infinite list. It allowes the user to scroll faster without having to see placeholders and makes the application feel way snappier, especially on lower-end devices.

    Here are some tips on how to gain performance. They are ordered from most to least important and only apply to things that are rendered for every element:

    • Prefer regular HTML-Elements over vue-components
      • Rendering vue components executes a lot of JavaScript, blocking everything else. Rendering regular HTML elements is way faster
      • Example: use <button> instead of <v-btn>
    • Prefer conditional rendering over hiding/showing elements via css
      • showing/hiding via css may prevent rerenders, but it increases the amount of rendered elements
      • The less elements (and therefore domnodes) are rendered the better
    • Use less elements
      • Why use a <v-card><v-img></v-img></v-card> when performance is important and a <div></div> with some css works too?
    "},{"location":"developer-guide/ui/infinite-scrolling/#memoization","title":"Memoization","text":"

    Memoization is a technique to speed up function calls by caching results. This can have a noticable impact on render-performance, especially when function results are used for placeholders

    Example: The texts on the cards in the cards-view. There are function-calls like photo.locationInfo() and photo.getDateString(). The resulting values rarely change, but are calculated again and again on every render, resulting in ~280k calls per function when scrolling through ~2k pictures.

    We use memoize-one for much called, non-trivial functions whose parameters rarely change. The funtions in the Photo model are a prime example for that.

    For this to work the memoized function must be pure, which means its result must not depend on outside factors, but only on its parameters. Calling the same function twice with the same parameters must always return the same result.

    If you want to memoize a function that is not pure you can still do so by moving all its logic into a new, memoized, pure function and having the old funtion just call the memoized one, providing the required parameters. Example:

    // ------------------ before ------------------\nisPlayable() {\n  if (this.Type === MediaAnimated) {\n    return true;\n  } else if (!this.Files) {\n    return false;\n  }\n\n  return this.Files.some((f) => f.Video);\n}\n
    // ------------------ after ------------------\nisPlayable() {\n  return this.generateIsPlayable(this.Type, this.Files);\n}\n\ngenerateIsPlayable = memoizeOne((type, files) => {\n  if (type === MediaAnimated) {\n    return true;\n  } else if (!files) {\n    return false;\n  }\n\n  return files.some((f) => f.Video);\n})\n

    "},{"location":"developer-guide/ui/introduction/","title":"Introduction","text":"

    Open a terminal and type photoprism start to start the built-in server. It will listen on localhost:2342 by default, see docker-compose.yml and Configuration.

    "},{"location":"developer-guide/ui/introduction/#frameworks","title":"Frameworks","text":"

    Vuetify is a powerful open-source Material Design UI component framework for building modern single-page applications.

    It is based on VueJS, a JavaScript library that combines the best ideas from AngularJS (Google) and React (Facebook); development is community driven and the API fairly stable.

    Vuetify and VueJS are initialized in frontend/src/app.js. Webpack is used as a module loader / bundler. It creates single, optimized JS and CSS files in the server assets public build directory from the original source code. You can find the build configuration in frontend/webpack.config.js.

    For our docs and landing pages, we may use https://materializecss.com/ as a lightweight alternative to Vuetify.

    "},{"location":"developer-guide/ui/introduction/#components","title":"Components","text":"

    Components are reusable user-interface widgets. UI Components contains a list of custom components. Standard components like buttons or forms are well documented on vuetifyjs.com.

    "},{"location":"developer-guide/ui/introduction/#dependencies","title":"Dependencies","text":"

    The full list of dependencies can be found in frontend/package.json. You need to run npm install in the frontend directory to install them (automatically happens during installation, see Makefile). Run npm install -P [package name] to add a new package (library or framework).

    "},{"location":"developer-guide/ui/introduction/#building","title":"Building","text":"

    A build can be triggered by running npm run watch (watches for changes and re-builds when needed) or npm run build (single build) in the frontend directory. NPM is the default package manager that comes with NodeJS, a JavaScript run-time environment that executes JavaScript code outside of a browser.

    "},{"location":"developer-guide/ui/introduction/#external-resources","title":"External Resources","text":"
    • https://web.dev/progressive-web-apps/
    • vuejs-templates/pwa - a progressive web app template for VueJS
    • https://hackernoon.com/a-progressive-web-app-in-vue-tutorial-part-1-the-vue-app-f9231b032a0b
    • https://medium.com/the-web-tub/creating-your-first-vue-js-pwa-project-22f7c552fb34
    • https://developers.google.com/web/fundamentals/native-hardware/fullscreen/
    • https://webpack.js.org/configuration/optimization/
    • https://photoswipe.com/documentation/responsive-images.html
    • https://seregpie.github.io/VueWordCloud/ - Word cloud for VueJS
    • snorpey/jpg-glitch - JPEG Glitch lib for JS
    • https://stylable.io/ - scopes styles to components so they don\u2019t \u201cleak\u201d and clash with other styles (not sure if this is of any use for us)
    • Building the Google Photos Web UI - Antin Harasymiv on Medium
    • Progressive Image Grid - arrange images in a responsive, progressive-loading grid managed in JavaScript, see #85
    "},{"location":"developer-guide/ui/maps/","title":"Rendering Interactive Maps in the UI","text":"

    PhotoPrism includes four high-resolution world maps that allow you to browse photos by location. Visit try.photoprism.app/library/places to try them on our demo.

    The API keys required to use these maps are unfortunately not free for us due to the number of users we have, see FAQ.

    "},{"location":"developer-guide/ui/maps/#mapboxmaplibre-gl-js","title":"Mapbox/MapLibre GL JS","text":"

    Because Mapbox GL JS is no longer open-source, we now sponsor and use MapLibre GL JS for rendering maps in the UI. MapLibre GL is a fork from the last Mapbox GL version available under a permissive BSD license.

    Statement by former Mapbox engineer Tom MacWright:

    OSS, we hoped, was about enabling people and unlocking people\u2019s ability to collaborate. It turns out that in 2020, it\u2019s mostly helping companies and getting nothing in return. That\u2019s not a dynamic you can build a sustainable business on.

    "},{"location":"developer-guide/ui/screenshots/","title":"UI Development in Fast Motion \u23f1","text":""},{"location":"developer-guide/ui/screenshots/#august-15-2018","title":"August 15 , 2018","text":"

    The first prototype was based on Bootstrap 4.

    "},{"location":"developer-guide/ui/screenshots/#september-6-2018","title":"September 6, 2018","text":"

    We switched to the Vuetify component framework, which comes complete with everything modern Web applications need.

    "},{"location":"developer-guide/ui/screenshots/#september-7-2018","title":"September 7, 2018","text":"

    Navigation was extended.

    "},{"location":"developer-guide/ui/screenshots/#september-7-2018_1","title":"September 7, 2018","text":"

    We started to experiment with the search form.

    "},{"location":"developer-guide/ui/screenshots/#september-8-2018","title":"September 8, 2018","text":"

    Search dropdowns got populated with options.

    "},{"location":"developer-guide/ui/screenshots/#september-10-2018","title":"September 10, 2018","text":"

    New logo and speed-dial action button.

    "},{"location":"developer-guide/ui/screenshots/#september-11-2018","title":"September 11, 2018","text":""},{"location":"developer-guide/ui/screenshots/#september-19-2018","title":"September 19, 2018","text":"

    Pastel colored buttons and photo selection.

    "},{"location":"developer-guide/ui/screenshots/#september-26-2018","title":"September 26, 2018","text":"

    The detail view.

    "},{"location":"developer-guide/ui/screenshots/#july-3-2019","title":"July 3, 2019","text":"

    Improved UI with flat look, photo upload and powerful filters as well as new pages for searching places and labels.

    "},{"location":"developer-guide/ui/screenshots/#january-24-2020","title":"January 24, 2020","text":""},{"location":"developer-guide/ui/screenshots/#january-26-2020","title":"January 26, 2020","text":""},{"location":"developer-guide/ui/screenshots/#june-24-2020","title":"June 24, 2020","text":""},{"location":"developer-guide/ui/screenshots/#january-21-2021","title":"January 21, 2021","text":""},{"location":"developer-guide/ui/screenshots/#october-18-2021","title":"October 18, 2021","text":""},{"location":"developer-guide/ui/screenshots/#march-2-2022","title":"March 2, 2022","text":""},{"location":"getting-started/","title":"Setup","text":"

    PhotoPrism can be installed on all operating systems supporting Docker, as well as FreeBSD, Raspberry Pi, and many NAS devices. It is also available in the cloud on PikaPods and DigitalOcean.

    We recommend running PhotoPrism with Docker Compose when hosting it on a private server. It is available for Mac, Linux, and Windows.

    Once the initial setup is complete, our First Steps \ud83d\udc63 tutorial guides you through the user interface and settings to ensure your library is indexed according to your individual preferences.

    Our stable releases and preview builds are available as multi-arch Docker images for 64-bit AMD, Intel, and ARM processors. Experienced users can alternatively use the packages at dl.photoprism.app/pkg/linux/ to manually install PhotoPrism on compatible Linux distributions. For more installation methods, see our Getting Started FAQ.

    "},{"location":"getting-started/#system-requirements","title":"System Requirements","text":"

    You should host PhotoPrism on a server with at least 2 cores, 3 GB of physical memory,1 and a 64-bit operating system. Beyond these minimum requirements, the amount of RAM should match the number of CPU cores. Indexing large photo and video collections also benefits greatly from local SSD storage, especially for the database and cache files.

    Also ensure that your server has at least 4 GB of swap configured and avoid setting a hard memory limit as this can cause unexpected restarts when the indexer temporarily needs more memory to process large files. Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    We take no responsibility for instability or performance problems if your device does not meet the requirements.

    "},{"location":"getting-started/#databases","title":"Databases","text":"

    PhotoPrism is compatible with SQLite 3 and MariaDB 10.5.12+.2 Note that SQLite is generally not a good choice for users who require scalability and high performance, and that support for MySQL 8 has been discontinued due to low demand and missing features.3

    "},{"location":"getting-started/#browsers","title":"Browsers","text":"

    Built as a Progressive Web App (PWA), the web interface works with most modern browsers, and runs best on Chrome, Chromium, Safari, Firefox, and Edge. You can conveniently install it on the home screen of all major operating systems and mobile devices.

    Not all video and audio formats can be played with every browser. For example, AAC - the default audio codec for MPEG-4 AVC / H.264 - is supported natively in Chrome, Safari, and Edge, while it is only optionally supported by the OS in Firefox and Opera.

    "},{"location":"getting-started/#https","title":"HTTPS","text":"

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure HTTPS reverse proxy such as Traefik or Caddy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted by anyone, including your provider, hackers, and governments. Backup tools and file sync apps like FolderSync may refuse to connect as well.

    "},{"location":"getting-started/#firewall","title":"Firewall","text":"

    In order to successfully set up your installation and view location details in PhotoPrism, you must allow incoming requests as well as those to our Geocoding API and Docker if you have a firewall installed, and make sure that your Internet connection is working.

    Configure Firewall \u203a

    "},{"location":"getting-started/#maps-places","title":"Maps & Places","text":"

    As explained in our Privacy Policy, reverse geocoding and interactive world maps depend on retrieving the necessary information from us and MapTiler AG, headquartered in Switzerland. Both services are provided with a very high level of privacy and confidentiality.4

    Your use of these services is fully covered by us. Depending on your usage, this can save you much more than the cost of a PhotoPrism+ Membership, since other providers generally charge usage-based fees and often don't allow you to cache the data they provide, compromising performance and your privacy with unnecessary requests.

    View Privacy Policy \u203a View Compliance FAQ \u203a

    "},{"location":"getting-started/#roadmap","title":"Roadmap","text":"

    Our vision is to provide the most user- and privacy-friendly solution to keep your pictures organized and accessible. The project roadmap shows what tasks are in progress, what needs testing, and which features are going to be implemented next.

    Please note, however, that we have a zero-bug policy and do our best to help users when they need support or have questions. This comes at a price, as we can't give exact release dates for new features.

    "},{"location":"getting-started/#getting-support","title":"Getting Support","text":"

    If you need help installing our software at home, you are welcome to post your question in GitHub Discussions or ask in our Community Chat. Common problems can be quickly diagnosed and solved using our Troubleshooting Checklists. Silver, Gold, and Platinum members are also welcome to email us for technical support and advice.

    View Support Options \u203a Compare Memberships \u203a

    We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app. Contact us or a community member if you need help, it could be a configuration problem, or a misunderstanding in how the software works.

    1. RAW image conversion and TensorFlow are disabled on systems with 1 GB or less memory\u00a0\u21a9

    2. Our configuration examples are generally based on the current stable MariaDB version to take advantage of performance improvements. This does not mean that older versions are no longer supported and you must upgrade immediately. We recommend not using the :latest tag for the MariaDB Docker image and to upgrade manually by changing the tag once we had a chance to test a new major version.\u00a0\u21a9

    3. Oracle seems to have stopped shipping new features and enhancements. As a result, the testing effort required before each release is no longer feasible.\u00a0\u21a9

    4. Our Compliance FAQ gives answers to the most frequently asked questions about product compliance and scalability.\u00a0\u21a9

    "},{"location":"getting-started/config-options/","title":"Config Options","text":"

    Note that changes to the config options listed below always require a restart to take effect.1 Instead of using environment variables, you can alternatively use an \u21aa options.yml file to configure your instance.

    "},{"location":"getting-started/config-options/#authentication","title":"Authentication","text":"Environment CLI Flag Default Description PHOTOPRISM_AUTH_MODE --auth-mode password authentication MODE (public2, password) PHOTOPRISM_ADMIN_USER, PHOTOPRISM_ADMIN_USERNAME --admin-user admin USERNAME of the superadmin account that is created on first startup PHOTOPRISM_ADMIN_PASSWORD --admin-password initial PASSWORD of the superadmin account (8-72 characters) PHOTOPRISM_PASSWORD_LENGTH --password-length 8 minimum password LENGTH in characters\u2002plus PHOTOPRISM_OIDC_URI --oidc-uri issuer URI for single sign-on via OpenID Connect, e.g. https://accounts.google.com PHOTOPRISM_OIDC_CLIENT --oidc-client client ID for single sign-on via OpenID Connect PHOTOPRISM_OIDC_SECRET --oidc-secret client SECRET for single sign-on via OpenID Connect PHOTOPRISM_OIDC_PROVIDER --oidc-provider custom identity provider NAME, e.g. Google PHOTOPRISM_OIDC_ICON --oidc-icon custom identity provider icon URI PHOTOPRISM_OIDC_REDIRECT --oidc-redirect automatically redirect unauthenticated users to the configured identity provider PHOTOPRISM_OIDC_REGISTER --oidc-register allow new users to create an account when they sign in with OpenID Connect PHOTOPRISM_OIDC_USERNAME --oidc-username preferred_username preferred username CLAIM for new OpenID Connect users (preferred_username, name, nickname, email) PHOTOPRISM_OIDC_WEBDAV --oidc-webdav allow new OpenID Connect users to use WebDAV when they have a role that allows it PHOTOPRISM_DISABLE_OIDC --disable-oidc disable single sign-on via OpenID Connect, even if an identity provider has been configured PHOTOPRISM_SESSION_MAXAGE --session-maxage 1209600 session expiration time in SECONDS, doubled for accounts with 2FA (-1 to disable) PHOTOPRISM_SESSION_TIMEOUT --session-timeout 604800 session idle time in SECONDS, doubled for accounts with 2FA (-1 to disable) PHOTOPRISM_SESSION_CACHE --session-cache 900 session cache duration in SECONDS (60-3600)"},{"location":"getting-started/config-options/#logging","title":"Logging","text":"Environment CLI Flag Default Description PHOTOPRISM_LOG_LEVEL --log-level info log message verbosity LEVEL (trace, debug, info, warning, error, fatal, panic) PHOTOPRISM_DEBUG --debug enable debug mode, show non-essential log messages PHOTOPRISM_TRACE --trace enable trace mode, show all log messages"},{"location":"getting-started/config-options/#storage","title":"Storage","text":"Environment CLI Flag Default Description PHOTOPRISM_CONFIG_PATH --config-path config storage PATH, values in options.yml override CLI flags and environment variables if present PHOTOPRISM_DEFAULTS_YAML --defaults-yaml /etc/photoprism/defaults.yml load config defaults from FILE if exists, does not override CLI flags and environment variables PHOTOPRISM_ORIGINALS_PATH --originals-path storage PATH of your original media files (photos and videos) PHOTOPRISM_ORIGINALS_LIMIT --originals-limit 1000 maximum size of media files in MB (1-100000; -1 to disable) PHOTOPRISM_RESOLUTION_LIMIT --resolution-limit 150 maximum resolution of media files in MEGAPIXELS (1-900; -1 to disable) PHOTOPRISM_USERS_PATH --users-path users relative PATH to create base and upload subdirectories for users PHOTOPRISM_STORAGE_PATH --storage-path writable storage PATH for sidecar, cache, and database files PHOTOPRISM_IMPORT_PATH --import-path base PATH from which files can be imported to originals\u2002optional PHOTOPRISM_IMPORT_DEST --import-dest relative originals PATH to which the files should be imported by default\u2002optional PHOTOPRISM_CACHE_PATH --cache-path custom cache PATH for sessions and thumbnail files\u2002optional PHOTOPRISM_TEMP_PATH --temp-path temporary file PATH optional PHOTOPRISM_ASSETS_PATH --assets-path assets PATH containing static resources like icons, models, and translations"},{"location":"getting-started/config-options/#sidecar-files","title":"Sidecar Files","text":"Environment CLI Flag Default Description PHOTOPRISM_SIDECAR_PATH --sidecar-path custom relative or absolute sidecar PATH optional PHOTOPRISM_SIDECAR_YAML --sidecar-yaml true create YAML sidecar files to back up picture metadata"},{"location":"getting-started/config-options/#backup","title":"Backup","text":"Environment CLI Flag Default Description PHOTOPRISM_BACKUP_PATH --backup-path custom base PATH for creating and restoring backups\u2002optional PHOTOPRISM_BACKUP_SCHEDULE --backup-schedule daily backup SCHEDULE in cron format (e.g. \"0 12 * * *\" for daily at noon) or at a random time (daily, weekly) PHOTOPRISM_BACKUP_RETAIN --backup-retain 3 NUMBER of index backups to keep (-1 to keep all) PHOTOPRISM_BACKUP_DATABASE --backup-database true create regular backups based on the configured schedule PHOTOPRISM_BACKUP_ALBUMS --backup-albums true create YAML files to back up album metadata"},{"location":"getting-started/config-options/#indexing","title":"Indexing","text":"Environment CLI Flag Default Description PHOTOPRISM_INDEX_WORKERS, PHOTOPRISM_WORKERS --index-workers 4 maximum NUMBER of indexing workers, default depends on the number of physical cores PHOTOPRISM_INDEX_SCHEDULE --index-schedule indexing SCHEDULE in cron format (e.g. \"@every 3h\" for every 3 hours; \"\" to disable) PHOTOPRISM_WAKEUP_INTERVAL --wakeup-interval 15m0s TIME between facial recognition, file sync, and metadata worker runs (1-86400s) PHOTOPRISM_AUTO_INDEX --auto-index 300 delay before automatically indexing files in SECONDS when uploading via WebDAV (-1 to disable) PHOTOPRISM_AUTO_IMPORT --auto-import -1 delay before automatically importing files in SECONDS when uploading via WebDAV (-1 to disable)"},{"location":"getting-started/config-options/#feature-flags","title":"Feature Flags","text":"Environment CLI Flag Default Description PHOTOPRISM_READONLY --read-only disable features that require write permission for the originals folder PHOTOPRISM_EXPERIMENTAL --experimental enable new features currently under development PHOTOPRISM_DISABLE_SETTINGS --disable-settings disable the settings user interface and server API, e.g. in combination with public mode PHOTOPRISM_DISABLE_BACKUPS --disable-backups prevent database and album backups as well as YAML sidecar files from being created PHOTOPRISM_DISABLE_RESTART --disable-restart prevent admins from restarting the server through the user interface PHOTOPRISM_DISABLE_WEBDAV --disable-webdav prevent other apps from accessing PhotoPrism as a shared network drive PHOTOPRISM_DISABLE_PLACES --disable-places disable interactive world maps and reverse geocoding PHOTOPRISM_DISABLE_TENSORFLOW --disable-tensorflow disable features depending on TensorFlow, e.g. image classification and face recognition PHOTOPRISM_DISABLE_FACES --disable-faces disable face detection and recognition (requires TensorFlow) PHOTOPRISM_DISABLE_CLASSIFICATION --disable-classification disable image classification (requires TensorFlow) PHOTOPRISM_DISABLE_FFMPEG --disable-ffmpeg disable video transcoding and thumbnail extraction with FFmpeg PHOTOPRISM_DISABLE_EXIFTOOL --disable-exiftool disable metadata extraction with ExifTool (required for full Video, Live Photo, and XMP support) PHOTOPRISM_DISABLE_VIPS --disable-vips disable image processing and conversion with libvips PHOTOPRISM_DISABLE_SIPS --disable-sips disable file conversion using the sips command under macOS PHOTOPRISM_DISABLE_DARKTABLE --disable-darktable disable conversion of RAW images with Darktable PHOTOPRISM_DISABLE_RAWTHERAPEE --disable-rawtherapee disable conversion of RAW images with RawTherapee PHOTOPRISM_DISABLE_IMAGEMAGICK --disable-imagemagick disable conversion of image files with ImageMagick PHOTOPRISM_DISABLE_HEIFCONVERT --disable-heifconvert disable conversion of HEIC images with libheif PHOTOPRISM_DISABLE_RSVGCONVERT --disable-rsvgconvert disable conversion of SVG graphics with librsvg\u2002plus PHOTOPRISM_DISABLE_VECTORS --disable-vectors disable vector graphics support\u2002plus PHOTOPRISM_DISABLE_JPEGXL --disable-jpegxl disable JPEG XL file format support PHOTOPRISM_DISABLE_RAW --disable-raw disable indexing and conversion of RAW images PHOTOPRISM_RAW_PRESETS --raw-presets enables applying user presets when converting RAW images (reduces performance) PHOTOPRISM_EXIF_BRUTEFORCE --exif-bruteforce always perform a brute-force search if no Exif headers were found PHOTOPRISM_DETECT_NSFW --detect-nsfw flag newly added pictures as private if they might be offensive (requires TensorFlow) PHOTOPRISM_UPLOAD_NSFW --upload-nsfw allow uploads that might be offensive (detecting unsafe content requires TensorFlow)"},{"location":"getting-started/config-options/#customization","title":"Customization","text":"Environment CLI Flag Default Description PHOTOPRISM_DEFAULT_LOCALE --default-locale en default user interface language CODE PHOTOPRISM_DEFAULT_TIMEZONE --default-timezone UTC default time zone NAME, e.g. for scheduling backups PHOTOPRISM_DEFAULT_THEME --default-theme default user interface theme NAME PHOTOPRISM_APP_NAME --app-name progressive web app NAME when installed on a device PHOTOPRISM_APP_MODE --app-mode standalone progressive web app MODE (fullscreen, standalone, minimal-ui, browser) PHOTOPRISM_APP_ICON --app-icon home screen ICON (logo, app, crisp, mint, bold, square) PHOTOPRISM_APP_COLOR --app-color #000000 splash screen COLOR code PHOTOPRISM_LEGAL_INFO --legal-info legal information TEXT, displayed in the page footer PHOTOPRISM_LEGAL_URL --legal-url legal information URL PHOTOPRISM_WALLPAPER_URI --wallpaper-uri login screen background image URI"},{"location":"getting-started/config-options/#site-information","title":"Site Information","text":"Environment CLI Flag Default Description PHOTOPRISM_SITE_URL --site-url http://localhost:2342/ public site URL PHOTOPRISM_SITE_AUTHOR --site-author site OWNER, copyright, or artist PHOTOPRISM_SITE_TITLE --site-title site TITLE PHOTOPRISM_SITE_CAPTION --site-caption AI-Powered Photos App site CAPTION PHOTOPRISM_SITE_DESCRIPTION --site-description site DESCRIPTION optional PHOTOPRISM_SITE_PREVIEW --site-preview sharing preview image URL PHOTOPRISM_CDN_URL --cdn-url content delivery network URL PHOTOPRISM_CDN_VIDEO --cdn-video stream videos over the specified CDN PHOTOPRISM_CORS_ORIGIN --cors-origin origin URL from which browsers are allowed to perform cross-origin requests (leave blank to disable or use * to allow all) PHOTOPRISM_CORS_HEADERS --cors-headers Accept, Accept-Ranges, Content-Disposition, Content-Encoding, Content-Range, Location one or more HEADERS that browsers should see when performing a cross-origin request PHOTOPRISM_CORS_METHODS --cors-methods GET, HEAD, OPTIONS one or more METHODS that may be used when performing a cross-origin request"},{"location":"getting-started/config-options/#proxy-servers","title":"Proxy Servers","text":"Environment CLI Flag Default Description PHOTOPRISM_HTTPS_PROXY --https-proxy proxy server URL to be used for outgoing connections\u2002optional PHOTOPRISM_HTTPS_PROXY_INSECURE --https-proxy-insecure ignore invalid HTTPS certificates when using a proxy PHOTOPRISM_TRUSTED_PROXY --trusted-proxy 172.16.0.0/12 CIDR ranges or IPv4/v6 addresses from which reverse proxy headers can be trusted, separated by commas PHOTOPRISM_PROXY_PROTO_HEADER --proxy-proto-header X-Forwarded-Proto proxy protocol header NAME PHOTOPRISM_PROXY_PROTO_HTTPS --proxy-proto-https https forwarded HTTPS protocol NAME"},{"location":"getting-started/config-options/#web-server","title":"Web Server","text":"Environment CLI Flag Default Description PHOTOPRISM_DISABLE_TLS --disable-tls disable HTTPS/TLS even if the site URL starts with https:// and a certificate is available PHOTOPRISM_DEFAULT_TLS --default-tls default to a self-signed HTTPS/TLS certificate if no other certificate is available PHOTOPRISM_TLS_CERT --tls-cert public HTTPS certificate FILE (.crt), ignored for Unix domain sockets PHOTOPRISM_TLS_KEY --tls-key private HTTPS key FILE (.key), ignored for Unix domain sockets PHOTOPRISM_DISABLE_STS --disable-sts disable HTTP Strict-Transport-Security (STS) header PHOTOPRISM_STS_SECONDS --sts-seconds 31536000 TIME for the browser to remember that the site is to be accessed only via HTTPS (0 to disable)\u2002plus PHOTOPRISM_STS_SUBDOMAINS --sts-subdomains rule applies to all subdomains as well\u2002plus PHOTOPRISM_STS_PRELOAD --sts-preload submit to Google's HSTS preload service\u2002plus PHOTOPRISM_AUTH_LIMIT --auth-limit 60 maximum number of consecutive invalid access TOKENS from a single IP\u2002plus PHOTOPRISM_AUTH_INTERVAL --auth-interval 10s average DURATION between invalid access tokens from a single IP (0-86400s)\u2002plus PHOTOPRISM_LOGIN_LIMIT --login-limit 10 maximum number of consecutive failed LOGINS from a single IP\u2002plus PHOTOPRISM_LOGIN_INTERVAL --login-interval 1m0s average DURATION between failed logins from a single IP (0-86400s)\u2002plus PHOTOPRISM_IPS_LIMIT --ips-limit 3 maximum number of malicious request ATTEMPTS before a client IP is blocked (-1 to disable)\u2002plus PHOTOPRISM_IPS_INTERVAL --ips-interval 1h0m0s average DURATION between malicious request attempts from a single IP (0-86400s)\u2002plus PHOTOPRISM_HTTP_CSP --http-csp HTTP Content-Security-Policy (CSP) HEADER plus PHOTOPRISM_HTTP_CTO --http-cto nosniff HTTP X-Content-Type-Options HEADER plus PHOTOPRISM_HTTP_COOP --http-coop same-origin HTTP Cross-Origin-Opener-Policy (COOP) HEADER plus PHOTOPRISM_HTTP_REFERRER_POLICY --http-referrer-policy same-origin HTTP Referrer-Policy HEADER plus PHOTOPRISM_HTTP_FRAME_OPTIONS --http-frame-options DENY HTTP X-Frame-Options HEADER plus PHOTOPRISM_HTTP_XSS_PROTECTION --http-xss-protection 1; mode=block HTTP X-XSS-Protection HEADER plus PHOTOPRISM_HTTP_MODE --http-mode Web server MODE (debug, release, test) PHOTOPRISM_HTTP_COMPRESSION --http-compression Web server compression METHOD (gzip, none) PHOTOPRISM_HTTP_CACHE_PUBLIC --http-cache-public allow static content to be cached by a CDN or caching proxy PHOTOPRISM_HTTP_CACHE_MAXAGE --http-cache-maxage 2592000 time in SECONDS until cached content expires PHOTOPRISM_HTTP_VIDEO_MAXAGE --http-video-maxage 21600 time in SECONDS until cached videos expire PHOTOPRISM_HTTP_HOST --http-host 0.0.0.0 Web server IP address or Unix domain socket, e.g. unix:/var/run/photoprism.sock PHOTOPRISM_HTTP_PORT --http-port 2342 Web server port NUMBER, ignored for Unix domain sockets PHOTOPRISM_HTTP_HOSTNAME --http-hostname serve requests for this HOSTNAME only\u2002plus"},{"location":"getting-started/config-options/#database-connection","title":"Database Connection","text":"Environment CLI Flag Default Description PHOTOPRISM_DATABASE_DRIVER --database-driver sqlite database DRIVER (sqlite, mysql) PHOTOPRISM_DATABASE_DSN --database-dsn database connection DSN (sqlite file, optional for mysql) PHOTOPRISM_DATABASE_NAME --database-name photoprism database schema NAME PHOTOPRISM_DATABASE_SERVER --database-server database HOST incl. port e.g. \"mariadb:3306\" (or socket path) PHOTOPRISM_DATABASE_USER --database-user photoprism database user NAME PHOTOPRISM_DATABASE_PASSWORD --database-password database user PASSWORD PHOTOPRISM_DATABASE_TIMEOUT --database-timeout 15 timeout in SECONDS for establishing a database connection (1-60) PHOTOPRISM_DATABASE_CONNS --database-conns 0 maximum NUMBER of open database connections PHOTOPRISM_DATABASE_CONNS_IDLE --database-conns-idle 0 maximum NUMBER of idle database connections"},{"location":"getting-started/config-options/#file-conversion","title":"File Conversion","text":"Environment CLI Flag Default Description PHOTOPRISM_FFMPEG_BIN --ffmpeg-bin ffmpeg FFmpeg COMMAND for video transcoding and thumbnail extraction PHOTOPRISM_FFMPEG_ENCODER --ffmpeg-encoder libx264 FFmpeg AVC encoder NAME PHOTOPRISM_FFMPEG_SIZE --ffmpeg-size 4096 maximum video size in PIXELS (720-7680) PHOTOPRISM_FFMPEG_BITRATE --ffmpeg-bitrate 50 maximum video BITRATE in Mbit/s PHOTOPRISM_FFMPEG_MAP_VIDEO --ffmpeg-map-video 0:v:0 video STREAMS that should be transcoded PHOTOPRISM_FFMPEG_MAP_AUDIO --ffmpeg-map-audio 0:a:0? audio STREAMS that should be transcoded PHOTOPRISM_EXIFTOOL_BIN --exiftool-bin exiftool ExifTool COMMAND for extracting metadata PHOTOPRISM_SIPS_BIN --sips-bin sips Sips COMMAND for media file conversion\u2002macOS only PHOTOPRISM_SIPS_EXCLUDE, PHOTOPRISM_SIPS_BLACKLIST --sips-exclude avif, avifs, thm file EXTENSIONS not to be used with Sips\u2002macOS only PHOTOPRISM_DARKTABLE_BIN --darktable-bin darktable-cli Darktable CLI COMMAND for RAW to JPEG conversion PHOTOPRISM_DARKTABLE_EXCLUDE, PHOTOPRISM_DARKTABLE_BLACKLIST --darktable-exclude thm file EXTENSIONS not to be used with Darktable PHOTOPRISM_DARKTABLE_CACHE_PATH --darktable-cache-path custom Darktable cache PATH PHOTOPRISM_DARKTABLE_CONFIG_PATH --darktable-config-path custom Darktable config PATH PHOTOPRISM_RAWTHERAPEE_BIN --rawtherapee-bin rawtherapee-cli RawTherapee CLI COMMAND for RAW to JPEG conversion PHOTOPRISM_RAWTHERAPEE_EXCLUDE, PHOTOPRISM_RAWTHERAPEE_BLACKLIST --rawtherapee-exclude dng, thm file EXTENSIONS not to be used with RawTherapee PHOTOPRISM_IMAGEMAGICK_BIN --imagemagick-bin convert ImageMagick CLI COMMAND for image file conversion PHOTOPRISM_IMAGEMAGICK_EXCLUDE, PHOTOPRISM_IMAGEMAGICK_BLACKLIST --imagemagick-exclude heif, heic, heics, avif, avifs, jxl, thm file EXTENSIONS not to be used with ImageMagick PHOTOPRISM_HEIFCONVERT_BIN --heifconvert-bin heif-convert libheif HEIC image conversion COMMAND PHOTOPRISM_RSVGCONVERT_BIN --rsvgconvert-bin rsvg-convert librsvg SVG graphics conversion COMMAND plus"},{"location":"getting-started/config-options/#security-tokens","title":"Security Tokens","text":"Environment CLI Flag Default Description PHOTOPRISM_DOWNLOAD_TOKEN --download-token DEFAULT download URL token for originals (leave blank for a random value) PHOTOPRISM_PREVIEW_TOKEN --preview-token DEFAULT thumbnail and video streaming URL token (leave blank for a random value)"},{"location":"getting-started/config-options/#preview-images","title":"Preview Images","text":"Environment CLI Flag Default Description PHOTOPRISM_THUMB_LIBRARY --thumb-library auto image processing LIBRARY to be used for generating thumbnails (auto, imaging, vips) PHOTOPRISM_THUMB_COLOR --thumb-color auto standard color PROFILE for thumbnails (auto, preserve, srgb, none) PHOTOPRISM_THUMB_FILTER --thumb-filter auto downscaling filter NAME (imaging best to worst: blackman, lanczos, cubic, linear, nearest) PHOTOPRISM_THUMB_SIZE --thumb-size 1920 maximum size of pre-generated thumbnails in PIXELS (720-7680) PHOTOPRISM_THUMB_SIZE_UNCACHED --thumb-size-uncached 7680 maximum size of thumbnails generated on demand in PIXELS (720-7680) PHOTOPRISM_THUMB_UNCACHED --thumb-uncached generate missing thumbnails on demand (high memory and cpu usage)"},{"location":"getting-started/config-options/#image-quality","title":"Image Quality","text":"Environment CLI Flag Default Description PHOTOPRISM_JPEG_QUALITY --jpeg-quality 83 higher values increase the image QUALITY and file size (25-100) PHOTOPRISM_JPEG_SIZE --jpeg-size 7680 maximum size of generated JPEG images in PIXELS (720-30000) PHOTOPRISM_PNG_SIZE --png-size 7680 maximum size of generated PNG images in PIXELS (720-30000)"},{"location":"getting-started/config-options/#face-recognition","title":"Face Recognition","text":"

    To recognize faces, PhotoPrism first extracts crops from your images using a library based on pixel intensity comparisons. These are then fed into TensorFlow to compute 512-dimensional vectors for characterization. In the final step, the DBSCAN algorithm attempts to cluster these so-called face embeddings, so they can be matched to persons with just a few clicks. A reasonable range for the similarity distance between face embeddings is between 0.60 and 0.70, with a higher value being more aggressive and leading to larger clusters with more false positives. To cluster a smaller number of faces, you can reduce the core to 3 or 2 similar faces.

    We recommend that only advanced users change these parameters:

    Environment CLI Flag Default Description PHOTOPRISM_FACE_SIZE --face-size 50 minimum size of faces in PIXELS (20-10000) PHOTOPRISM_FACE_SCORE --face-score 9.000000 minimum face QUALITY score (1-100) PHOTOPRISM_FACE_OVERLAP --face-overlap 42 face area overlap threshold in PERCENT (1-100) PHOTOPRISM_FACE_CLUSTER_SIZE --face-cluster-size 80 minimum size of automatically clustered faces in PIXELS (20-10000) PHOTOPRISM_FACE_CLUSTER_SCORE --face-cluster-score 15 minimum QUALITY score of automatically clustered faces (1-100) PHOTOPRISM_FACE_CLUSTER_CORE --face-cluster-core 4 NUMBER of faces forming a cluster core (1-100) PHOTOPRISM_FACE_CLUSTER_DIST --face-cluster-dist 0.640000 similarity DISTANCE of faces forming a cluster core (0.1-1.5) PHOTOPRISM_FACE_MATCH_DIST --face-match-dist 0.460000 similarity OFFSET for matching faces with existing clusters (0.1-1.5)"},{"location":"getting-started/config-options/#daemon-mode","title":"Daemon Mode","text":"

    If you start the server as a daemon in the background, you can additionally specify a filename for the log and the process ID:

    Environment CLI Flag Default Description PHOTOPRISM_PID_FILENAME --pid-filename process id FILE daemon-mode only PHOTOPRISM_LOG_FILENAME --log-filename server log FILE daemon-mode only"},{"location":"getting-started/config-options/#docker-image","title":"Docker Image","text":"

    The following variables are used by our Docker images only and have no effect otherwise:

    Environment Default Description PHOTOPRISM_UID 0 run as a non-root user after initialization (supported: 0, 33, 50-99, 500-600, 900-1250, and 2000-2100) PHOTOPRISM_GID 0 run with a specific group id after initialization, can optionally be used together with PHOTOPRISM_UID (supported: 0, 33, 44, 50-99, 105, 109, 115, 116, 500-600, 900-1250, and 2000-2100) PHOTOPRISM_UMASK 0002 file-creation mode (default: u=rwx,g=rwx,o=rx) PHOTOPRISM_INIT run/install on first startup (options: update https gpu ffmpeg tensorflow davfs clitools clean) PHOTOPRISM_DISABLE_CHOWN false disable updating storage permissions via chmod and chown on startup
    1. If you are using Docker Compose, you can open a terminal, run docker compose stop, and then run docker compose up -d to restart all services.\u00a0\u21a9

    2. Enabling public mode is not recommended for instances installed on a server outside your home network, as this allows others to access your pictures without authentication.\u00a0\u21a9

    "},{"location":"getting-started/docker-compose/","title":"Setup Using Docker Compose","text":"

    With Docker Compose, you use a YAML file to configure all application services so you can easily start them with a single command. Before you proceed, make sure you have Docker installed on your system. It is available for Mac, Linux, and Windows.

    Alternatively, Podman Compose is supported as a drop-in replacement for Docker Compose on Red Hat-compatible Linux distributions like RHEL, CentOS, Fedora, AlmaLinux, and Rocky Linux.

    "},{"location":"getting-started/docker-compose/#step-1-configure","title":"Step 1: Configure","text":"LinuxPodmanRaspberry PiARMv7WindowsmacOS

    Download our docker-compose.yml example (right click and Save Link As... or use wget) to a folder of your choice, and change the configuration as needed:

    wget https://dl.photoprism.app/docker/docker-compose.yml\n

    Commands on Linux may have to be prefixed with sudo when not running as root. Note that this will point the home directory shortcut ~ to /root in the volumes: section of your config file. Kernel security modules such as AppArmor and SELinux have been reported to cause issues.

    We recommend that your server has at least 4 GB of swap configured and to avoid setting a hard memory limit, as this can lead to unexpected restarts when the indexer temporarily needs more memory to process large files. Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    Download our docker-compose.yml example (right click and Save Link As... or use wget) to a folder of your choice, and change the configuration as needed:

    wget https://dl.photoprism.app/podman/docker-compose.yml\n

    Alternatively, you can run these commands to install Podman and download the default configuration to /opt/photoprism:

    mkdir -p /opt/photoprism\ncd /opt/photoprism\ncurl -sSf https://dl.photoprism.app/podman/install.sh | bash\n

    Please keep in mind to replace the docker and docker compose commands with podman and podman-compose when following the examples in our documentation.

    We recommend that your server has at least 4 GB of swap configured and to avoid setting a hard memory limit, as this can lead to unexpected restarts when the indexer temporarily needs more memory to process large files. Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    Download our docker-compose.yml example for the Raspberry Pi and other ARM64-based devices (right click and Save Link As... or use wget) to a folder of your choice, and change the configuration as needed:

    wget https://dl.photoprism.app/docker/arm64/docker-compose.yml\n

    Mostly the same installation instructions as for regular Linux servers apply. Commands may have to be prefixed with sudo when not running as root.

    Please verify if your device meets the system requirements and has at least 4 GB of swap configured before you continue. Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    Download our docker-compose.yml example for older ARMv7-based devices (right click and Save Link As... or use wget) to a folder of your choice, and change the configuration as needed:

    wget https://dl.photoprism.app/docker/armv7/docker-compose.yml\n

    Mostly the same installation instructions as for regular Linux servers apply. Commands may have to be prefixed with sudo when not running as root.

    Please verify if your device meets the system requirements and has at least 4 GB of swap configured before you continue. Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    Download our docker-compose.yml example for Windows (right click and Save Link As...) to a folder of your choice, and change the configuration as needed:

    https://dl.photoprism.app/docker/windows/docker-compose.yml

    It is important to increase the Docker memory limit to 4 GB or more when using Hyper-V. The default of 2 GB can reduce indexing performance and cause unexpected restarts. Also make sure you configure at least 4 GB of swap space. Docker Desktop uses dynamic memory allocation with WSL 2, meaning you do not need to change any memory-related settings (depending on which version of Windows and Docker you are using).

    Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    Running the following commands will automatically download all required config files and start the server for you:

    curl.exe -o install.bat https://dl.photoprism.app/docker/windows/install.bat\ninstall.bat\n

    Before you run this, make sure you are in the directory where you want to install PhotoPrism and that Docker Desktop is installed and started on your PC.

    Download our docker-compose.yml example for macOS (right click and Save Link As...) to a folder of your choice, and change the configuration as needed:

    https://dl.photoprism.app/docker/macos/docker-compose.yml

    It is important to increase the Docker memory limit to 4 GB or more, as the default of 2 GB can reduce indexing performance and cause unexpected restarts.

    Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    When editing YAML files, please make sure that related values remain on the same indentation level and that lists start with a dash. If the value of an environment variable contains a literal $ sign, for example in a password, it must be escaped with $$ (a double dollar sign) so that e.g. \"compo$e\" becomes \"compo$$e\".

    Always change PHOTOPRISM_ADMIN_PASSWORD so that the app starts with a secure initial password. Never use easy-to-guess passwords or default values like insecure on publicly accessible servers. There is no default in case no password was provided. A minimum length of 8 characters is required.

    "},{"location":"getting-started/docker-compose/#database","title":"Database","text":"

    Our example includes a pre-configured MariaDB database server. If you remove it and provide no other database server credentials, SQLite database files will be created in the storage folder. Local SSD storage is best for databases of any kind.

    Never store database files on an unreliable device such as a USB flash drive, SD card, or shared network folder. These may also have unexpected file size limitations, which is especially problematic for databases that do not split data into smaller files.

    You cannot change the database password with MARIADB_PASSWORD after MariaDB has been started for the first time. However, choosing a secure password is not essential if you do not share the database with other applications or expose it over a network. To enable automatic schema updates when upgrading to a new major version, please make sure that MARIADB_AUTO_UPGRADE is set to a non-empty value.

    "},{"location":"getting-started/docker-compose/#volumes","title":"Volumes","text":"

    You must explicitly specify the directories you want to mount from your host, since PhotoPrism can't see files in folders that have not been shared. This is an important security feature and allows for a flexible configuration without having to change any other variables.

    It is important that all folders are mounted to persistent volumes. We recommend changing the relative paths used in our examples to absolute paths and to avoid using named or anonymous volumes in order to prevent potential data loss when the container is recreated, e.g. after an update of the Docker image.

    "},{"location":"getting-started/docker-compose/#photoprismoriginals","title":"/photoprism/originals","text":"

    The originals folder contains your original photo and video files. ~/Pictures will be mounted by default, where ~ is a shortcut for your home directory:

    services:\n  photoprism:\n    volumes:\n      - \"~/Pictures:/photoprism/originals\"\n

    We recommend that you change ~/Pictures to the directory where your existing media files are, for example:

          - \"/mnt/photos:/photoprism/originals\"\n

    Additional directories can be mounted as sub folders of /photoprism/originals (depending on overlay filesystem support):

        volumes:\n      - \"/mnt/photos:/photoprism/originals\"\n      - \"/mnt/videos:/photoprism/originals/videos\"\n

    On Windows, prefix the host path with the drive letter and use / instead of \\ as separator:

        volumes:\n      - \"D:/Example/Pictures:/photoprism/originals\"\n

    If read-only mode is enabled, all features that require write permission to the originals folder are disabled, e.g. WebDAV, uploading and deleting files. Set PHOTOPRISM_READONLY to \"true\" in docker-compose.yml for this. In addition, you can mount volumes with the :ro flag so that writes are also blocked by Docker.

    "},{"location":"getting-started/docker-compose/#photoprismstorage","title":"/photoprism/storage","text":"

    The storage folder is used to save config, cache, backup, thumbnail, and sidecar files. It must always be specified so that you do not lose these files after a restart or upgrade. If available, we recommend you put the storage folder on a local SSD drive for best performance. You can otherwise keep the default and store the files in a folder relative to the current directory:

    services:\n  photoprism:\n    volumes:\n      - \"./storage:/photoprism/storage\"\n

    Never configure the storage folder to be inside the originals folder unless the name starts with a . to indicate that it is hidden. Should you later want to move your instance to another host, the easiest and most time-saving way is to copy the entire storage folder along with your originals and database.

    "},{"location":"getting-started/docker-compose/#photoprismimport","title":"/photoprism/import","text":"

    You can optionally mount an import folder from which files can be transferred to the originals folder in a structured way that avoids duplicates, for example:

    services:\n  photoprism:\n    volumes:\n      - \"/mnt/media/usb:/photoprism/import\"\n

    Imported files receive a canonical filename and will be organized by year and month. You should never configure the import folder to be inside the originals folder, as this will cause a loop by importing already indexed files.

    Even if you don't specify an import folder, adding files via Web Upload and WebDAV remains possible unless read-only mode is enabled or the features have been disabled.

    "},{"location":"getting-started/docker-compose/#step-2-start-the-server","title":"Step 2: Start the server","text":"

    Open a terminal and change to the folder in which your config file has been saved.1 Run this command to start the application and database services in the background:

    docker compose up -d\n

    Note that our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    Now open the Web UI by navigating to http://localhost:2342/. You should see a login screen. Sign in with the user admin and the initial password configured via PHOTOPRISM_ADMIN_PASSWORD. You may change it on the account settings page. Enabling public mode will disable authentication.

    It can be helpful to keep Docker running in the foreground while debugging so that log messages are displayed directly. To do this, omit the -d parameter when restarting.

    Should the server already be running, or you see no errors, you may have started it on a different host and/or port. There could also be an issue with your browser, ad blocker, or firewall settings.

    You cannot change the password with PHOTOPRISM_ADMIN_PASSWORD after the app has been started for the first time. To change the admin password, run the docker compose exec photoprism photoprism passwd [username] command in a terminal. You can also run docker compose exec photoprism photoprism reset to delete the existing index database and start from scratch.

    The server port and other config options can be changed in docker-compose.yml at any time. Remember to restart the services for changes to take effect:

    docker compose stop\ndocker compose up -d\n
    "},{"location":"getting-started/docker-compose/#step-3-index-your-library","title":"Step 3: Index Your Library","text":"

    Our First Steps \ud83d\udc63 tutorial guides you through the user interface and settings to ensure your library is indexed according to your individual preferences.

    Easy, isn't it?

    "},{"location":"getting-started/docker-compose/#photoprism-plus","title":"PhotoPrism\u00ae Plus","text":"

    Our members can activate additional features by logging in with the admin user created during setup and then following the steps described in our activation guide. Thank you for your support, which has been and continues to be essential to the success of the project!

    Compare Memberships \u203a View Membership FAQ \u203a

    We recommend that new users install our free Community Edition before signing up for a membership.

    "},{"location":"getting-started/docker-compose/#troubleshooting","title":"Troubleshooting","text":"

    If your server runs out of memory, the index is frequently locked, or other system resources are running low:

    • Try reducing the number of workers by setting PHOTOPRISM_WORKERS to a reasonably small value in docker-compose.yml, depending on the CPU performance and number of cores
    • Ensure that your server has at least 4 GB of swap configured and avoid setting a hard memory limit as this can cause unexpected restarts when the indexer temporarily needs more memory to process large files
    • If you are using SQLite, switch to MariaDB, which is better optimized for high concurrency
    • As a last measure, you can disable the use of TensorFlow for image classification and facial recognition

    Other issues? Our troubleshooting checklists help you quickly diagnose and solve them.

    You are welcome to ask for help in our community chat. Sponsors receive direct technical support via email. Before submitting a support request, try to determine the cause of your problem.

    "},{"location":"getting-started/docker-compose/#command-line-interface","title":"Command-Line Interface","text":""},{"location":"getting-started/docker-compose/#introduction","title":"Introduction","text":"

    photoprism help lists all commands and config options available in the current version:

    docker compose exec photoprism photoprism help\n

    Use the --help flag to see a detailed command description, for example:

    docker compose exec photoprism photoprism backup --help\n

    PhotoPrism's command-line interface is also well suited for job automation using a scheduler.

    When using Docker Compose, you can prefix the commands you want to run with docker compose exec [service] to execute them in the specified service container. If this fails with no container found, please make sure that the service has been started, you have specified an existing service (usually photoprism) and you are in the folder where your config file is located.

    "},{"location":"getting-started/docker-compose/#opening-a-terminal","title":"Opening a Terminal","text":"

    To open a terminal session as the default user:

    docker compose exec photoprism bash\n

    Since the above will open the terminal as root by default, we recommend that you pass the -u flag to explicitly open a non-root session if PhotoPrism is running under a specific user account, for example:

    docker compose exec -u 1000 photoprism bash\n

    This avoids potential filesystem permission issues that can occur when a command creates new files or folders, e.g. to store thumbnails.

    "},{"location":"getting-started/docker-compose/#changing-the-user-id","title":"Changing the User ID","text":"

    Specifying a user with the -u flag is possible for all commands you run with Docker and Docker Compose. In the following examples, it is omitted for brevity. Note, however, that commands that you run without an explicit user ID might be executed as root. The currently supported user ID ranges are 0, 33, 50-99, 500-600, 900-1250, and 2000-2100.

    We recommend running the photoprism service as a non-root user by setting either the user service property or the PHOTOPRISM_UID environment variable in your config file. Don't forget to update file permissions and/or ownership with the chown command when you make changes.

    "},{"location":"getting-started/docker-compose/#examples","title":"Examples","text":"Action Command Start Services docker compose up -d Stop Services docker compose stop Download Updates docker compose pull Uninstall docker compose rm -s -v Watch Logs docker compose logs -f --tail=100 Display Config Values docker compose exec photoprism photoprism show config Show Migration Status docker compose exec photoprism photoprism migrations ls Repeat Failed Migrations docker compose exec photoprism photoprism migrations run -f Reset Database docker compose exec photoprism photoprism reset --yes Backup Database docker compose exec photoprism photoprism backup -a -i Restore Database docker compose exec photoprism photoprism restore -a -i Change Password docker compose exec photoprism photoprism passwd [username] Show User Management Commands docker compose exec photoprism photoprism users help Reset User Accounts docker compose exec photoprism photoprism users reset --yes Reset Sessions and Access Tokens docker compose exec photoprism photoprism auth reset --yes Show Face Recognition Commands docker compose exec photoprism photoprism faces help Index Faces docker compose exec photoprism photoprism faces index Reset People & Faces docker compose exec photoprism photoprism faces reset -f Transcode Videos to AVC docker compose exec photoprism photoprism convert Regenerate Thumbnails docker compose exec photoprism photoprism thumbs -f Update Index docker compose exec photoprism photoprism index --cleanup Move to Originals docker compose exec photoprism photoprism import [path] Copy to Originals docker compose exec photoprism photoprism cp [path]

    Note that our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    Complete Rescan

    docker compose exec photoprism photoprism index -f rescans all originals, including already indexed and unchanged files. This may be necessary after major upgrades and after migrations of the database schema, especially if search results are missing or incorrect. Note you can also start a rescan from the user interface by navigating to Library > Index, checking \"Complete Rescan\" and then clicking \"Start\". Manually entered information such as labels, people, titles or descriptions will not be modified when indexing, even if you perform a \"complete rescan\".

    1. The default name for Docker Compose config files is compose.yaml or docker-compose.yml. For simplicity, it does not need to be specified if you run commands in the same directory. Config files for other apps and instances should be placed in separate folders.\u00a0\u21a9

    "},{"location":"getting-started/docker/","title":"Running PhotoPrism with Docker","text":"

    We recommend using Docker Compose because it is easier and provides more convenience for running multiple services than the pure Docker command-line interface. Before you proceed, make sure you have Docker installed on your system. It is available for Mac, Linux, and Windows.

    Alternatively, Podman is supported as a drop-in replacement for Docker on Red Hat-compatible Linux distributions like RHEL, CentOS, Fedora, AlmaLinux, and Rocky Linux.

    "},{"location":"getting-started/docker/#step-1-start-the-server","title":"Step 1: Start the server","text":"LinuxPodman

    Open a terminal and run this command to start the app after replacing ~/Pictures with the folder containing your pictures:

    docker run -d \\\n  --name photoprism \\\n  --security-opt seccomp=unconfined \\\n  --security-opt apparmor=unconfined \\\n  -p 2342:2342 \\\n  -e PHOTOPRISM_UPLOAD_NSFW=\"true\" \\\n  -e PHOTOPRISM_ADMIN_PASSWORD=\"insecure\" \\\n  -v /photoprism/storage \\\n  -v ~/Pictures:/photoprism/originals \\\n  photoprism/photoprism\n

    Open a terminal and run this command to start the app after replacing ~/Pictures with the folder containing your pictures:

    podman run -d \\\n  --name photoprism \\\n  --privileged \\\n  --security-opt seccomp=unconfined \\\n  --security-opt apparmor=unconfined \\\n  -p 2342:2342 \\\n  -e PHOTOPRISM_UPLOAD_NSFW=\"true\" \\\n  -e PHOTOPRISM_ADMIN_PASSWORD=\"insecure\" \\\n  -v /photoprism/storage \\\n  -v ~/Pictures:/photoprism/originals \\\n  photoprism/photoprism\n

    Please keep in mind to replace the docker command with podman when following the examples in our documentation.

    The server port and other config options can be changed as needed. If you provide no database server credentials, SQLite database files will be created in the storage folder. Note, however, that SQLite is not a good choice for users who require scalability and high performance. We therefore do not recommend using this example to set up a production environment without modifying it, e.g. to connect it to an existing MariaDB database instance.

    Always change PHOTOPRISM_ADMIN_PASSWORD so that the app starts with a secure initial password. Never use easy-to-guess passwords or default values like insecure on publicly accessible servers. There is no default in case no password was provided. A minimum length of 8 characters is required.

    Commands on Linux may have to be prefixed with sudo when not running as root. Note that this will point the home directory shortcut ~ to /root in volume mounts. Kernel security modules such as AppArmor and SELinux have been reported to cause issues.

    When the app has been started, open the Web UI by navigating to http://localhost:2342/. You should see a login screen. Sign in with the user admin and the password configured via PHOTOPRISM_ADMIN_PASSWORD. You may change it on the account settings page. Enabling public mode will disable authentication.

    It can be helpful to keep Docker running in the foreground while debugging so that log messages are displayed directly. To do this, omit the -d parameter when restarting.

    Should the server already be running, or you see no errors, you may have started it on a different host and/or port. There could also be an issue with your browser, ad blocker, or firewall settings.

    You cannot change the password with PHOTOPRISM_ADMIN_PASSWORD after the app has been started for the first time. To change the admin password, run the docker exec -ti photoprism photoprism passwd [username] command in a terminal. You can also run docker exec -ti photoprism photoprism reset to delete the existing index database and start from scratch.

    "},{"location":"getting-started/docker/#volumes","title":"Volumes","text":"

    Since the app is running inside a container, you have to explicitly mount the host folders you want to use. PhotoPrism won't be able to see folders that have not been mounted. That's an important security feature.

    "},{"location":"getting-started/docker/#photoprismoriginals","title":"/photoprism/originals","text":"

    The originals folder contains your original photo and video files. They are mounted from ~/Pictures in the example above, where ~ is a shortcut for your home directory.

    You may mount any folder accessible from the host instead, including network drives. Additional directories can be mounted as sub folders of /photoprism/originals:

    -v ~/Example:/photoprism/originals/Example\n

    If read-only mode is enabled, all features that require write permission to the originals folder are disabled, e.g. WebDAV, uploading and deleting files. Run the app with -e PHOTOPRISM_READONLY=\"true\" for this. You can mount a folder with the :ro flag to make Docker block write operations as well.

    "},{"location":"getting-started/docker/#photoprismstorage","title":"/photoprism/storage","text":"

    SQLite, config, cache, backup, thumbnail and sidecar files are saved in the storage folder:

    • a storage folder must always be mounted so that you do not lose these files after a restart or upgrade
    • never configure the storage folder to be inside the originals folder unless the name starts with a . to indicate that it is hidden
    • we recommend placing the storage folder on a local SSD drive for best performance
    • mounting symbolic links or using them inside the storage folder is currently not supported

    Using our example, an anonymous volume is created and mounted as storage folder. You can mount a specific host folder instead, just as with originals, which is better for production environments.

    Should you later want to move your instance to another host, the easiest and most time-saving way is to copy the entire storage folder along with your originals and database.

    "},{"location":"getting-started/docker/#photoprismimport","title":"/photoprism/import","text":"

    You can optionally mount an import folder from which files can be transferred to the originals folder in a structured way that avoids duplicates:

    • imported files receive a canonical filename and will be organized by year and month
    • never configure the import folder to be inside the originals folder, as this will cause a loop by importing already indexed files

    You can safely skip this. Adding files via Web Upload and WebDAV remains possible, unless read-only mode is enabled or the features have been disabled.

    "},{"location":"getting-started/docker/#step-2-first-steps","title":"Step 2: First steps","text":"

    Our First Steps \ud83d\udc63 tutorial guides you through the user interface and settings to ensure your library is indexed according to your individual preferences.

    Easy, isn't it?

    "},{"location":"getting-started/docker/#step-3-when-youre-done","title":"Step 3: When you're done...","text":"

    You can stop PhotoPrism and start it again using the following commands:

    docker stop photoprism\ndocker start photoprism\n

    To remove the container completely:

    docker rm -f photoprism\n
    "},{"location":"getting-started/docker/#photoprism-plus","title":"PhotoPrism\u00ae Plus","text":"

    Our members can activate additional features by logging in with the admin user created during setup and then following the steps described in our activation guide. Thank you for your support, which has been and continues to be essential to the success of the project!

    Compare Memberships \u203a View Membership FAQ \u203a

    We recommend that new users install our free Community Edition before signing up for a membership.

    "},{"location":"getting-started/docker/#troubleshooting","title":"Troubleshooting","text":"

    If your server runs out of memory, the index is frequently locked, or other system resources are running low:

    • Try reducing the number of workers by setting PHOTOPRISM_WORKERS to a reasonably small value, depending on the CPU performance and number of cores
    • Ensure that your server has at least 4 GB of swap configured and avoid setting a hard memory limit as this can cause unexpected restarts when the indexer temporarily needs more memory to process large files
    • If you are using SQLite, switch to MariaDB, which is better optimized for high concurrency
    • As a last measure, you can disable the use of TensorFlow for image classification and facial recognition

    Other issues? Our troubleshooting checklists help you quickly diagnose and solve them.

    You are welcome to ask for help in our community chat. Sponsors receive direct technical support via email. Before submitting a support request, try to determine the cause of your problem.

    "},{"location":"getting-started/docker/#command-line-interface","title":"Command-Line Interface","text":""},{"location":"getting-started/docker/#introduction","title":"Introduction","text":"

    photoprism help lists all commands and config options available in the current version:

    docker exec -ti photoprism photoprism help\n

    Use the --help flag to see a detailed command description, for example:

    docker exec -ti photoprism photoprism backup --help\n

    PhotoPrism's command-line interface is also well suited for job automation using a scheduler.

    When using Docker, you can prepend commands like docker exec -ti [container] [command] to run them in a container. Should this fail with no container found, make sure the container has been started and you have specified an existing container name or id.

    "},{"location":"getting-started/docker/#opening-a-terminal","title":"Opening a Terminal","text":"

    To open a terminal session, you can run the following (replace $UID with the user ID to be used or omit the -u flag altogether to open the terminal as root):

    docker exec -ti -u $UID photoprism bash\n

    Passing the -ti flag is important for interactive commands to work, for example if you need to confirm an action.

    "},{"location":"getting-started/docker/#changing-the-user-id","title":"Changing the User ID","text":"

    Specifying a user with the -u flag is possible for all commands you run with Docker. In the following examples, it is omitted for brevity. Note, however, that commands that you run without an explicit user ID might be executed as root. The currently supported user ID ranges are 0, 33, 50-99, 500-600, 900-1250, and 2000-2100.

    "},{"location":"getting-started/docker/#examples","title":"Examples","text":"Action Command Start PhotoPrism docker start photoprism Stop PhotoPrism docker stop photoprism Download Update docker pull photoprism/photoprism:latest Uninstall docker rm -f photoprism View Logs docker logs --tail=100 -f photoprism Display Config Values docker exec -ti photoprism photoprism show config Show Migration Status docker exec -ti photoprism photoprism migrations ls Repeat Failed Migrations docker exec -ti photoprism photoprism migrations run -f Reset Database docker exec -ti photoprism photoprism reset --yes Backup Database docker exec -ti photoprism photoprism backup -a -i Restore Database docker exec -ti photoprism photoprism restore -a -i Change Password docker exec -ti photoprism photoprism passwd [username] Show User Management Commands docker exec -ti photoprism photoprism users help Reset User Accounts docker exec -ti photoprism photoprism users reset --yes Reset Sessions and Access Tokens docker exec -ti photoprism photoprism auth reset --yes Show Face Recognition Commands docker exec -ti photoprism photoprism faces help Index Faces docker exec -ti photoprism photoprism faces index Reset People & Faces docker exec -ti photoprism photoprism faces reset -f Transcode Videos to AVC docker exec -ti photoprism photoprism convert Regenerate Thumbnails docker exec -ti photoprism photoprism thumbs -f Update Index docker exec -ti photoprism photoprism index --cleanup Move to Originals docker exec -ti photoprism photoprism import [path] Copy to Originals docker exec -ti photoprism photoprism cp [path]

    You can alternatively use podman as a drop-in replacement for docker on Red Hat-compatible distributions.

    Complete Rescan

    docker exec -ti photoprism photoprism index -f rescans all originals, including already indexed and unchanged files. This may be necessary after major upgrades and after migrations of the database schema, especially if search results are missing or incorrect. Note You can also start a rescan from the user interface by navigating to Library > Index, checking \"Full Rescan\" and then clicking \"Start\". Manually entered information such as labels, people, titles or descriptions will not be modified when indexing, even if you perform a \"complete rescan\".

    "},{"location":"getting-started/faq/","title":"Frequently Asked Questions","text":""},{"location":"getting-started/faq/#what-media-file-types-are-supported","title":"What media file types are supported?","text":"

    PhotoPrism supports indexing, viewing, and converting most popular image, video and RAW formats, including JPEG, PNG, GIF, BMP, HEIF, HEIC, MP4, MOV, WebP, and WebM. TIFF is partially supported without extensions such as GeoTIFF.

    When indexing, a JPEG or PNG sidecar file is automatically created for videos and images in other formats, such as RAW or vector graphics. It is needed for thumbnail generation, image classification, and face detection. JPEG XL support is planned as soon as it is generally available and enough compatible tools exist.

    If installed, converting RAW files is possible with the following converters (our Docker image includes both):

    • Darktable (supported cameras)
    • RawTherapee (supported cameras)

    On a Mac, RAW files can also be converted with Sips (supported cameras). Our goal is to provide top-notch support for all RAW formats, regardless of camera make and model. Please let us know about any issues with a particular camera or file format.

    For maximum browser compatibility, video codecs and containers supported by FFmpeg can be transcoded to MPEG-4 AVC on demand, just as still images can be extracted for thumbnail creation.

    Make sure you have JSON sidecar files enabled if you have videos, live photos, and/or animated GIFs so that video-specific metadata such as codec, frames, and duration can be extracted, indexed, and searched.

    For a complete list of file formats and extensions, see our downloadable Feature Overview.

    In case FFmpeg is disabled or not installed, videos cannot be indexed because still images cannot be created. You should also have ExifTool enabled to extract metadata such as duration, resolution, and codec.

    "},{"location":"getting-started/faq/#what-are-sidecar-files-and-where-do-i-find-them","title":"What are sidecar files and where do I find them?","text":"

    A sidecar is a file that sits next to your main photo or video files and usually has the same name but a different extension:

    • IMG_0123.mov
    • IMG_0123.mov.jpg
    • IMG_0123.json

    New sidecar files are saved in the storage folder by default, so the originals folder can be mounted read-only.

    Even if PHOTOPRISM_DISABLE_EXIFTOOL is set to \u201ctrue\u201d or PHOTOPRISM_SIDECAR_YAML is set to \u201cfalse\u201d, the indexer will look for existing sidecar files and use them.

    "},{"location":"getting-started/faq/#what-metadata-sidecar-file-types-are-supported","title":"What metadata sidecar file types are supported?","text":"

    Currently, three types of file formats are supported:

    "},{"location":"getting-started/faq/#json","title":"JSON","text":"

    If not disabled via PHOTOPRISM_DISABLE_EXIFTOOL or --disable-exiftool, ExifTool is used to automatically create a JSON sidecar for each media file. In this way, embedded XMP and video metadata can also be indexed. Native metadata extraction is limited to common Exif headers. Note that this causes small amount of overhead when indexing for the first time.

    JSON files can also be useful for debugging, as they contain the full metadata and can be processed with common development tools and text editors.

    JSON files exported from Google Photos can be read as well. Support for more schemas may be added over time.

    "},{"location":"getting-started/faq/#yaml","title":"YAML","text":"

    Unless disabled by setting the PHOTOPRISM_SIDECAR_YAML option to \"false\" in your configuration, PhotoPrism automatically creates/updates human-friendly YAML sidecar files during indexing and after manual editing of fields such as title, date, or location. They serve as a backup in case the database (index) is lost, or when folders are synchronized with a remote instance.

    Like JSON, YAML files can be opened with common development tools and text editors. However, changes are not synchronized with the original index, as this could overwrite existing data.

    "},{"location":"getting-started/faq/#xmp","title":"XMP","text":"

    XMP (Extensible Metadata Platform) is an XML-based metadata container format developed by Adobe. It provides many more fields (as part of embedded models like Dublin Core) than Exif. This also makes it difficult - if not impossible - to provide full support. Reading title, copyright, artist, and description from XMP sidecar files is implemented as a proof-of-concept, contributions are welcome. Indexing of embedded XMP is only possible via ExifTool, see above.

    "},{"location":"getting-started/faq/#does-your-software-depend-on-any-external-services","title":"Does your software depend on any external services?","text":"

    As explained in our Privacy Policy, reverse geocoding and interactive world maps depend on retrieving the necessary information from us and MapTiler AG, headquartered in Switzerland. Both services are provided with a very high level of privacy and confidentiality.

    Your use of these services is fully covered by us. Depending on your usage, this can save you much more than the cost of a PhotoPrism+ Membership, since other providers generally charge usage-based fees and often don't allow you to cache the data they provide, compromising performance and your privacy with unnecessary requests.

    View Privacy Policy \u203a View Compliance FAQ \u203a

    In order to successfully set up your installation and view location details in PhotoPrism, you must allow incoming requests as well as those to our Geocoding API and Docker if you have a firewall installed, and make sure that your Internet connection is working:

    "},{"location":"getting-started/faq/#why-do-i-see-connection-errors-when-requesting-api-keys-at-startup","title":"Why do I see connection errors when requesting API keys at startup?","text":"

    Retrieving location data with reverse geocoding and loading the interactive world maps we provide requires a connection to external services. Please make sure that requests to these API endpoints are allowed if you have a firewall installed, and that your internet connection is working.

    Learn more \u203a

    "},{"location":"getting-started/faq/#are-the-keys-for-using-interactive-world-maps-provided-free-of-charge","title":"Are the keys for using interactive world maps provided free of charge?","text":"

    All users have access to a high-resolution vector map that we host on our own infrastructure, so no commercial API key is required. It is based on data published by OpenStreetMap (OSM).

    In addition, we automatically provide our members and business customers with an API key for MapTiler's commercial service, which includes satellite, outdoor and 3D maps. You can test these on our public demo.

    Learn more \u203a

    Although experienced users could alternatively register test accounts with a commercial provider to gain access to additional map styles instead of signing up for a membership, we believe this would not be fair. Keep in mind that we have a much larger user base than others who might encourage their users to do so, and that providers might then stop offering free test accounts, which is something we don't want to be responsible for.

    "},{"location":"getting-started/faq/#why-dont-you-use-the-free-map-tile-service-provided-by-openstreetmap","title":"Why don't you use the free map tile service provided by OpenStreetMap?","text":"

    Other free and open-source software sometimes uses the public maps that OpenStreetMap provides for development and testing. These are not intended for end-user applications like ours.

    Using their service also means that their usage and privacy policies apply, as your request data is stored and used to generate publicly available reports. This differs from our services, which ensure a high level of privacy and provide a better user experience with faster loading times.

    "},{"location":"getting-started/faq/#how-can-i-activate-my-membership","title":"How can I activate my membership?","text":"

    To connect a new instance to your membership account, you will need to log in with the super admin user that is automatically created during setup (see your compose.yaml or docker-compose.yml file or the app store documentation), and then follow the steps described in our activation guide.

    View Activation Guide \u203a

    "},{"location":"getting-started/faq/#what-are-the-advantages-of-purchasing-a-commercial-license","title":"What are the advantages of purchasing a commercial license?","text":"

    A key difference between the public license and a commercial license agreement is that you get access to additional support and configuration options, as well as the right to customize functionality to your needs without having to publicly disclose your changes. Our Compliance FAQ gives answers to the most frequently asked questions about product compliance and scalability.

    Compare Team Editions \u203a

    "},{"location":"getting-started/faq/#will-the-self-hosted-version-continue-to-be-supported","title":"Will the self-hosted version continue to be supported?","text":"

    Absolutely! We are on a mission to protect your freedom and privacy. Self-hosting is the easiest way to stay in control and protect your privacy. It also provides the best experience for advanced users who often rely on a local toolchain to select, edit, and publish their pictures.

    At the same time, we know there's a huge demand and many practical uses for a cloud-hosted app that is easy to set up. We like to give our users the choice and therefore offer a fully managed service as a deployment option. Selected hosting partners ensure that your privacy is protected as much as technically possible, even in the cloud.

    "},{"location":"getting-started/faq/#will-jpegs-be-updated-when-the-related-raw-or-xmp-files-change","title":"Will JPEGs be updated when the related RAW or XMP files change?","text":"

    JPEGs are currently not regenerated when related RAW or XMP files change. RAW files are digital negatives by design. PhotoPrism therefore assumes that their image information is immutable.

    XMP files can affect the appearance, but most of the metadata they contain, such as title and description, does not. Creating JPEGs from RAW files is a time-consuming task, and in most cases would cause a huge, unjustified amount of overhead. In addition, the rendering information in XMP files is not well standardized. For example, changes you make in Photoshop may not be compatible with Darktable.

    We recommend manually updating existing JPEG sidecar files as needed or creating additional JPEGs, so you can choose between different versions. New files and other metadata changes are detected and reflected in the index as usual when your library is scanned.

    "},{"location":"getting-started/faq/#which-folder-will-be-indexed","title":"Which folder will be indexed?","text":"

    This depends on your environment and configuration. While sub folders can be selected for indexing in the UI, changing the originals base folder requires a restart for security reasons.

    If you skip configuration and don't use one of our Docker images, PhotoPrism will attempt to find a photo library by searching a list of common folder names such as /photoprism/originals and ~/Pictures. It also searches for other resources such as external applications, classification models, and frontend assets.

    If you use our Docker Compose example without modifications, pictures will be mounted from ~/Pictures where ~ is a shortcut for your home directory:

    • \\user\\username on Windows
    • /Users/username on macOS
    • and /root or /home/username on Linux

    Since the app is running inside a container, you have to explicitly mount the host folders you want to use. PhotoPrism won't be able to see folders that have not been mounted. Multiple folders can be made accessible by mounting them as sub folders of /photoprism/originals, for example:

    volumes:\n  - \"/home/username/Pictures:/photoprism/originals\"\n  - \"/example/friends:/photoprism/originals/friends\"\n  - \"/mnt/photos:/photoprism/originals/media\"\n
    "},{"location":"getting-started/faq/#can-i-use-fat32-and-exfat-formatted-drives","title":"Can I use FAT32 and ExFAT formatted drives?","text":"

    Photos and videos can be mounted from FAT-formatted drives, such as an external SSD. Our tests have shown that PhotoPrism and MariaDB can also be started from there. However, at least on macOS, the logs may occasionally show directory access errors and you will be forced to restart if problems occur.

    "},{"location":"getting-started/faq/#i-cant-find-a-download-link-to-install-your-software-on-windows","title":"I can't find a download link to install your software on Windows?","text":"

    PhotoPrism depends on a number of other open source tools and applications, such as Darktable, RawTherapee, and FFmpeg. While you can install them directly on Windows, it's a lot of work and we don't have the capacity to test the respective Windows versions before each release.

    We therefore recommend to use Docker, so you can take advantage of our pre-built and QA-tested Docker image, which includes all the dependencies you need. It is a well-tested standard tool that also lets you run many other self-hosted apps without having to worry about the details or Windows-specific issues. To further simplify the setup for you, we offer a batch script that you can run in the directory where you want to install PhotoPrism:

    curl.exe -o install.bat https://dl.photoprism.app/docker/windows/install.bat\ninstall.bat\n

    This will automatically download all required config files and start the server for you. Before you run the script, make sure you have Docker Desktop installed on your Windows PC.

    "},{"location":"getting-started/faq/#how-can-i-install-photoprism-without-docker","title":"How can I install PhotoPrism without Docker?","text":""},{"location":"getting-started/faq/#installation-packages","title":"Installation Packages","text":"

    Experienced users can use the packages available at dl.photoprism.app/pkg/linux/ to install PhotoPrism on compatible Linux distributions, e.g. by running the following commands:

    sudo mkdir -p /opt/photoprism\ncd /opt/photoprism\nwget -c https://dl.photoprism.app/pkg/linux/amd64.tar.gz -O - | sudo tar -xz\nsudo ln -sf /opt/photoprism/bin/photoprism /usr/local/bin/photoprism\nphotoprism --version\n

    Note that these packages must be updated manually, do not come with a default configuration, and do not include the system dependencies required to make use of all the features. The minimum required glibc version is 2.35, so for example Ubuntu 22.04 and Debian Bookworm will work, but older Linux distributions may not be compatible.

    Learn more \u203a

    "},{"location":"getting-started/faq/#arch-linux-packages","title":"Arch Linux Packages","text":"

    Thomas Eizinger maintains AUR packages for installation on Arch Linux. These are based on the pre-built installation packages we provide and have a systemd integration so that PhotoPrism can be started and restarted automatically.

    Learn more \u203a

    "},{"location":"getting-started/faq/#lxc-images","title":"LXC Images","text":"

    There are currently no official LXC images available from us. However, you can use our installation packages together with the documentation we provide to set them up in a base image of your choice.

    Since Docker and LXC are pretty much the same technology, you can also convert our Docker image to the LXC format, e.g. with the following commands:

    apt update\napt install lxc umoci skopeo\nlxc-create photoprism -t oci -- --url docker://photoprism/photoprism:latest\n

    PhotoPrism can then be configured and started like any other LXC container:

    lxc-start --name=photoprism -- /opt/photoprism/bin/photoprism start\n

    Please note, though, that the network, storage and database configuration requires detailed knowledge of LXC. We therefore only recommend this approach if you can complete the setup without help from our documentation or support from our team.

    "},{"location":"getting-started/faq/#bsd-ports","title":"BSD Ports","text":"

    For FreeBSD and TrueNAS CORE (formerly FreeNAS) users, an unofficial port is available that builds PhotoPrism from source. It will also compile and install the required TensorFlow libraries for you.

    "},{"location":"getting-started/faq/#building-from-source","title":"Building From Source","text":"

    You can alternatively build and install PhotoPrism from the publicly available source code, which includes all the Community Edition features and most of the Essentials features (except additional user roles):

    git clone https://github.com/photoprism/photoprism.git\ncd photoprism\nmake all install DESTDIR=/opt/photoprism\n

    When choosing this installation method, missing build and system dependencies must be installed manually, as shown in our human-readable and versioned Dockerfiles. Since you often don't need to use the exact same versions, you can replace most packages with those available in your environment.

    Please be aware, though, that we do not have the resources to provide support and special dependencies, such as TensorFlow libraries, to private users who choose to build from source. If possible, we recommend using Docker Compose or the installation packages we provide, as they can save a lot of time creating and troubleshooting custom builds.

    PhotoPrism Plus

    If you are a Plus, Silver, Gold or Platinum member and would like to build from source, please let us know so we can give you access to our private extension repository and provide assistance.

    "},{"location":"getting-started/faq/#what-are-the-benefits-of-using-docker","title":"What are the benefits of using Docker?","text":"

    (1) Docker uses standard features of the Linux kernel. Containers are nothing new; Solaris Zones were released about 20 years ago and the chroot system call was introduced during development of Version 7 Unix in 1979. It is used ever since for hosting applications exposed to the public Internet. Modern Linux containers are an incremental improvement of this, based on standard functionality that is part of the kernel.

    (2) Docker saves time through simplified deployment and testing. A main advantage of Docker is that application images can be easily made available to users via Internet. It provides a common standard across most operating systems and devices, which saves our team a lot of time that we can then spend more effectively, for example, providing support and developing one of the many features that users are waiting for.

    (3) Dockerfiles are part of the source code repository. Human-readable and versioned Dockerfiles that are part of our public source code help avoid \"works for me\" moments and other unwelcome surprises by enabling us to have the exact same environment everywhere in development, staging, and production.

    (4) Running applications in containers is more secure. Last but not least, virtually all file format parsers have vulnerabilities that just haven't been discovered yet. This is a known risk that can affect you even if your computer is not directly connected to the Internet. Running apps in a container with limited host access is an easy way to improve security without compromising performance and usability.

    A virtual machine with a dedicated operating system environment provides even more security, but usually has side effects such as lower performance and more difficult handling. Using a VM, however, doesn't prevent you from running containerized apps to get the best of both worlds. This is essentially what happens when you install Docker on virtual cloud servers and operating systems other than Linux.

    "},{"location":"getting-started/faq/#why-does-your-docker-image-use-the-plus-license-instead-of-the-agpl","title":"Why does your Docker image use the Plus License instead of the AGPL?","text":"

    Our Plus License is used for both the extensions we provide to our members and the standard Docker images available on Docker Hub. This allows us to bundle the extensions with the compiled application, while the Community Edition remains freely available under the terms of the GNU Affero General Public License (AGPL).

    If you don't plan to use any additional features, you can alternatively use the \"ce\" tag instead of \"latest\" to get a slightly smaller Docker image distributed under the AGPL. Note that system dependencies and other third-party components included in this image are still subject to additional terms and conditions.

    View Open Source FAQ \u203a View Plus License \u203a

    "},{"location":"getting-started/faq/#should-i-use-sqlite-mariadb-or-mysql","title":"Should I use SQLite, MariaDB, or MySQL?","text":"

    PhotoPrism is compatible with SQLite 3 and MariaDB 10.5.12+. Official support for MySQL 8 has been discontinued as Oracle seems to have stopped shipping new features and enhancements.

    If you only have few pictures, concurrent users, and CPU cores, SQLite may seem faster compared to full-featured database servers like MariaDB. This changes as the index grows and the number of concurrent accesses increases. While MariaDB is optimized for high concurrency, SQLite frequently locks its index so that other operations have to wait. In the worst case, this can lead to locking errors and timeouts during indexing - especially in combination with a slow disk or network storage.

    The main advantage of SQLite is that you don't need to run a separate database server. It is therefore well suited for testing and can also be sufficient for small libraries with a few thousand files. If you are looking for scalability and high performance, it is not a good choice.

    "},{"location":"getting-started/faq/#is-database-corruption-a-common-problem-with-self-hosting","title":"Is database corruption a common problem with self-hosting?","text":"

    The likelihood of database corruption is generally very low if you follow our documentation. Our team runs many instances/databases and has never had any issues over the years.

    However, if you run MariaDB or SQLite on a network drive or an external drive/stick that e.g. has been accidentally removed, it can happen. This is why our documentation explicitly warns about the danger of using unreliable storage for database files.

    Some users also configure a named or anonymous Docker volume for the database, or mount the wrong path so that their index is lost when they recreate the database container, e.g. after an update of the Docker image.

    "},{"location":"getting-started/faq/#ive-configured-an-external-database-but-cant-connect","title":"I've configured an external database, but can't connect?","text":"

    Most often this happens when new users configure localhost or 127.0.0.1 as database server host, since these always point back to the current container or computer. So it is not possible to access an external service with such a hostname or an IP address starting with 127. It works only if it is used directly in the container or on the computer where the database server is running. Instead, you must use a hostname or IP address that is accessible from other machines and containers.

    Resolve Connection Issues \u203a

    "},{"location":"getting-started/faq/#how-can-i-determine-the-public-ip-address-of-my-home-network","title":"How can I determine the public IP address of my home network?","text":"

    You can use one of the following services to view your public IP address, i.e. the IP address that the computers in your home network use to communicate with the Internet:

    • https://api.ipify.org/
    • https://canhazip.com/
    "},{"location":"getting-started/faq/#why-is-my-configured-memory-limit-exceeded-when-indexing-even-though-photoprism-doesnt-actually-seem-to-use-that-much-memory","title":"Why is my configured memory limit exceeded when indexing, even though PhotoPrism doesn't actually seem to use that much memory?","text":"

    When indexing a media library, many files are opened and processed very quickly, which is not a typical workload compared to other containerized applications and services. Various libraries and external applications simultaneously interact with each other in complex ways, so a few spikes are inevitable. Some memory is also used by the kernel for buffered I/O to improve performance, although the extent to which caching counts towards a limit may vary.

    We therefore recommend not to set a hard memory limit, unless you are familiar with memory management and understand the implications. Instead, you should reduce the number of indexing workers and limit file size and resolution if you are low on resources or want to limit memory usage for other reasons. Also make sure you have at least 4 GB of swap configured.

    View System Requirements \u203a Get Performance Tips \u203a

    "},{"location":"getting-started/faq/#why-does-photoprism-always-consume-100-of-cpu-when-the-background-worker-is-running","title":"Why does PhotoPrism always consume 100% of CPU when the background worker is running?","text":"

    Many users reporting poor performance and high CPU load have migrated from SQLite to MariaDB so that their database schema is not optimized for performance, for example, because indexes are missing or columns have the wrong data type. The instructions for these migrations were provided by a contributor and are not part of the original software distribution. As such, they have not been officially released, recommended, or extensively tested by us.

    In some instances, users have manually changed the contents of the database. It is also possible that the database is in an inconsistent state for other reasons, e.g. due to bugs in previous versions that have been fixed in the meantime. However, we are not currently aware of any such cases.

    Due to the amount of time required to review each report, we can only offer this to eligible members and business customers, and not to users who have chosen our free community edition.

    Get Performance Tips \u203a View Database Schema \u203a

    "},{"location":"getting-started/faq/#can-you-improve-performance-when-using-older-or-otherwise-slow-hardware","title":"Can you improve performance when using older or otherwise slow hardware?","text":"

    It is a known issue that the user interface and backend operations, especially face recognition, can be slow or even crash on older hardware due to a lack of resources. Like most applications, PhotoPrism has certain requirements and our development process does not include testing on unsupported or unusual hardware.

    In many cases, performance can be improved through optimizations. Since these can prove to be very time-consuming and cost-intensive in practice, users and developers must decide on a case-by-case basis whether this provides sufficient benefit in relation to the costs or whether the use of more powerful hardware is faster and cheaper overall.

    We kindly ask you not to open a problem report on GitHub Issues for poor performance on older hardware until a full cause and feasibility analysis has been performed. GitHub Discussions or any of our other public forums and communities are great places to start a discussion.

    That being said, one of the advantages of open-source software is that users can submit pull requests with performance and other enhancements they would like to see implemented. This will result in a much faster solution than waiting for a core team member to remotely analyze your problem and then provide a fix.

    "},{"location":"getting-started/faq/#is-a-raspberry-pi-fast-enough","title":"Is a Raspberry Pi fast enough?","text":"

    This mainly depends on your expectations and the number of files you have. Most users report that PhotoPrism runs smoothly on a Raspberry Pi 4 with 4 GB of RAM.

    Note, however, that initial indexing usually takes much longer than on a regular desktop computer and that the hardware has limited video transcoding capabilities, so video file format conversion is not well supported and software transcoding is generally slow. We take no responsibility for instability or performance problems if your device does not meet the requirements.

    "},{"location":"getting-started/faq/#should-i-use-an-sd-card-or-a-usb-stick","title":"Should I use an SD card or a USB stick?","text":"

    Due to their performance and because they can lose data over time, we do not recommend using conventional SD cards, USB sticks or external USB 2 hard disk drives to store your files, except for backups.

    External Solid-State Drives (SSD) connected via USB 3 are generally reliable and fast enough to keep your originals, database, and storage folders. This way you can, for example, do the indexing on one computer, eject the drive, and then connect it to another computer to browse your pictures.

    Note, though, that database files may not be binary compatible in some cases (e.g. if the version or computer architecture does not match) and could also get corrupted when you disconnect an external drive before all changes have been written to disk. We therefore recommend that you regularly create database backups, so you can easily restore your index if necessary.

    View Backup Guide \u203a

    "},{"location":"getting-started/faq/#why-dont-you-display-animated-gifs-natively","title":"Why don't you display animated GIFs natively?","text":"

    Support for animated GIFs was added in April 2022.

    "},{"location":"getting-started/faq/#why-is-my-storage-folder-so-large-what-is-in-it","title":"Why is my storage folder so large? What is in it?","text":"

    The storage folder contains sidecar, cache, and configuration files. It may also contain index database files if you are using SQLite. Most of the space there is taken up by your thumbnails: These are high-quality, scaled-down versions of your originals. Thumbnails are necessary because web browsers are bad at resizing large images to fit the screen. Using full-resolution originals for slideshows and in search results would also consume a lot of browser memory and significantly reduce indexing performance.

    We are working to implement storage optimizations whenever there is an opportunity. It is also possible to increase the JPEG compression and/or limit the resolution if you are happy with lower quality thumbnails.

    To free up as much space as possible, the most effective way is to delete all files in the /cache/thumbnails storage folder. It is located outside the originals folder by default, depending on your configuration. Then perform a full rescan of your library or run the command photoprism thumbs -f in a terminal if you have direct server access. This command can also be used to replace existing thumbnails, for example after changing the quality settings. Higher resolution thumbnails cannot be automatically removed at this time.

    If you have a fast CPU and enough memory, you can choose to render certain thumbnails only on demand. However, storage is usually so cheap that most users opt for better quality and performance instead.

    Actual storage requirements vary and depend, among other things, on file resolutions and formats (RAW, JPEG, video,...). For highly compressed, high-resolution videos in modern formats that cannot be displayed natively by browsers, the storage folder may even be larger than the originals, since videos transcoded to AVC are not as heavily compressed.

    Change Settings \u203a

    "},{"location":"getting-started/faq/#can-i-skip-creating-thumbnails-completely","title":"Can I skip creating thumbnails completely?","text":"

    The smallest configurable size is 720px for use by the indexer to perform color detection, image classification, as well as face detection and recognition. Recreating them every time they are needed is too demanding even for the most powerful servers. Unless you have just a few small pictures, this would make the app unusable.

    Reducing the Static Size Limit of thumbnails has a significant impact on face recognition and image classification results. Simply put, it means that the indexer can no longer see properly.

    "},{"location":"getting-started/faq/#when-should-i-perform-a-complete-rescan","title":"When should I perform a complete rescan?","text":"

    We recommend performing a complete rescan after major updates to take advantage of new search filters and sorting options. Be sure to read the notes for each release to see what changes have been made and if they might affect your library, for example, because of the file types you have or because new search features have been added. If you encounter problems that you cannot solve otherwise (i.e. before reporting a bug), please also try a rescan and see if it solves the problem.

    You can start a rescan from the user interface by navigating to Library > Index, selecting \"Complete Rescan\", and then clicking \"Start\". Manually entered information such as labels, people, titles or descriptions will not be modified when indexing, even if you perform a \"complete rescan\". Be careful not to start multiple indexing processes at the same time, as this will lead to a high server load.

    "},{"location":"getting-started/faq/#how-can-i-shorten-the-startup-time-after-a-restart-or-update","title":"How can I shorten the startup time after a restart or update?","text":"

    To reduce startup time, do not set PHOTOPRISM_INIT to avoid running additional setup scripts, and set PHOTOPRISM_DISABLE_CHOWN to \"true\" to disable automatic permission updates.

    View Config Options \u203a

    If your instance doesn't start even after waiting for some time, our Troubleshooting Checklists help you quickly diagnose and solve the problem.

    "},{"location":"getting-started/faq/#why-are-files-uploaded-via-webdav-not-indexedimported-immediately","title":"Why are files uploaded via WebDAV not indexed/imported immediately?","text":"

    PHOTOPRISM_AUTO_INDEX and PHOTOPRISM_AUTO_IMPORT let you specify how long PhotoPrism should wait before indexing or importing newly uploaded files. The default setting is 300 seconds, or 5 minutes. This is a safety mechanism for users with slow uploads to avoid incomplete file sets, for example when uploading pictures with sidecar files. You can therefore reduce the delay if you have a fast connection and usually do not upload stacks of related files such as RAW images with sidecar JPEG and XMP files.

    In some cases, it is also possible that the index is already being updated, so you will have to wait until the process is complete before indexing new files.

    "},{"location":"getting-started/faq/#im-having-issues-understanding-the-difference-between-the-import-and-originals-folders","title":"I'm having issues understanding the difference between the import and originals folders?","text":"

    You may optionally mount an import folder from which files can be transferred to the originals folder in a structured way that avoids duplicates. Imported files receive a canonical filename and will be organized by year and month.

    Most users with existing photo libraries will want to index their originals folder directly without importing files, leaving the existing file and folder names unchanged. On the other hand importing is an efficient way to add files, since PhotoPrism doesn't have to search your originals folder to find new files.

    View First Steps \ud83d\udc63 \u203a

    "},{"location":"getting-started/faq/#can-i-use-photoprism-to-sort-files-into-a-configurable-folder-structure","title":"Can I use PhotoPrism to sort files into a configurable folder structure?","text":"

    You have complete freedom in how you organize your originals. If you don't like the unique names and folders used by the import function, you can resort to external batch renaming tools, for example ExifTool, PhockUp, or Photo Organizer.

    Configurable import folders may be available in a later version. This is because - depending on the specific pattern - appropriate conflict resolution is required and the patterns must be well understood and validated to avoid typos or other misconfigurations that lead to undesired results for which we do not want to be responsible.

    "},{"location":"getting-started/faq/#why-is-only-the-logo-displayed-when-i-open-the-app","title":"Why is only the logo displayed when I open the app?","text":"

    This may happen when the server cannot be reached, for example, because a proxy is misconfigured, JavaScript is disabled in your browser, an ad blocker is blocking requests, or you are using an incompatible browser.

    We recommend going through the checklist provided and to verify that your browser meets the system requirements.

    "},{"location":"getting-started/faq/#why-is-photoprism-getting-stuck-in-a-restart-loop","title":"Why is PhotoPrism getting stuck in a restart loop?","text":"

    This happens when Docker was configured to automatically restart services after failures.

    We recommend going through the checklist for fatal server errors and to verify that your computer meets the system requirements.

    "},{"location":"getting-started/faq/#can-i-install-photoprism-in-a-sub-directory-on-a-shared-domain","title":"Can I install PhotoPrism in a sub-directory on a shared domain?","text":"

    Setting up PhotoPrism behind a reverse proxy in a sub-directory on a shared domain is possible in principle. This method is experimental, however, and not generally recommended because a number of detailed issues remain to be addressed and technical expertise is required.

    "},{"location":"getting-started/faq/#i-could-not-find-a-documentation-of-config-parameters","title":"I could not find a documentation of config parameters?","text":"

    We maintain a complete list of config options in Getting Started. When you run photoprism help in a terminal, all commands and parameters available in your currently installed version are listed:

    docker compose exec photoprism photoprism help\n

    Our Docker Compose examples are continuously updated and inline documentation has been added to simplify installation.

    "},{"location":"getting-started/faq/#what-exactly-does-the-read-only-mode","title":"What exactly does the read-only mode?","text":"

    When you enable read-only mode, all features that require write permission to the originals folder are disabled, for example import, upload, and delete. Set PHOTOPRISM_READONLY to \"true\" in docker-compose.yml for this. You can mount a folder with the :ro flag to make Docker block write operations as well.

    "},{"location":"getting-started/faq/#in-which-cases-could-files-in-the-originals-folder-get-modified","title":"In which cases could files in the originals folder get modified?","text":"

    PhotoPrism generally does not write to the originals folder, with the following exceptions: (1) You rotate an image in the user interface, so its Exif header must be updated. (2) You unstack files that were stacked based on their name, so they must be renamed. (3) You add files using the import functionality or the web upload. (4) You manually delete files in the user interface. (5) You have configured the originals folder as your sidecar folder. (6) You access the originals folder with a WebDAV client to manage your files without having read-only mode enabled.

    "},{"location":"getting-started/faq/#how-can-i-uninstall-photoprism","title":"How can I uninstall PhotoPrism?","text":"

    This depends on how you installed it. If you're running PhotoPrism with Docker Compose, this command will stop and remove the Docker container:

    docker compose rm -s -v\n

    Please refer to the official Docker documentation for further details.

    "},{"location":"getting-started/faq/#how-can-i-mount-network-shares-with-docker","title":"How can I mount network shares with Docker?","text":"

    Shared folders that have already been mounted on your host under a drive letter or path can be used with Docker containers like any other directory. In addition, certain types of network storage like NFS (Unix/Linux) and CIFS (Windows/Mac) can also be mounted directly with Docker Compose.

    For more information, see the Network Storage section of our Docker Troubleshooting Guide.

    Learn more \u203a

    "},{"location":"getting-started/faq/#why-does-changing-permissions-using-chmod-not-work-for-my-network-shares","title":"Why does changing permissions using chmod not work for my network shares?","text":"

    This is a common issue with NFS shares. For security reasons, the permissions must be changed on the server for them to take effect, unless the server allows them to be changed remotely, which depends on the settings. Even then, in the worst case, the actual permissions on the server and the effective ones on the clients may be different.

    Learn more \u203a

    "},{"location":"getting-started/faq/#do-you-support-podman","title":"Do you support Podman?","text":"

    Podman works just fine both in rootless and under root. Mind the SELinux which is enabled on Red Hat compatible systems, you may hit permission error problems.

    More details on how to run PhotoPrism with Podman on CentOS in this blog post, it includes all the details including root and rootless modes, user mapping and SELinux.

    Learn more \u203a

    "},{"location":"getting-started/faq/#do-you-have-plans-to-add-support-for-ldap-or-active-directory","title":"Do you have plans to add support for LDAP or Active Directory?","text":"

    PhotoPrism offers support for secure single sign-on via OpenID Connect (OIDC). With our Pro Edition, you can also configure an LDAP or Active Directory server to authenticate users.

    Learn more \u203a

    "},{"location":"getting-started/faq/#is-it-possible-to-set-a-default-role-for-new-openid-connect-users","title":"Is it possible to set a default role for new OpenID Connect users?","text":"

    For security reasons, our Personal Editions currently default to the Guest role, which admins can then upgrade after checking the eligibility of newly registered accounts.

    Learn more \u203a

    "},{"location":"getting-started/faq/#can-i-configure-a-custom-claim-as-the-preferred-oidc-username","title":"Can I configure a custom claim as the preferred OIDC username?","text":"

    It is not possible to use a non-standard claim name such as username, as this can lead to conflicts and potential security issues, e.g. if the value is not unique or not reliably set.

    Learn more \u203a

    "},{"location":"getting-started/faq/#who-can-i-contact-if-i-have-a-complaint-about-your-software","title":"Who can I contact if I have a complaint about your software?","text":"

    Please read this documentation and determine the cause of your problem before opening invalid, duplicate and/or incomplete bug reports, starting a public \"shitstorm\" or insulting other community members in our forums and chat rooms. Not only is this annoying for everyone, but it also keeps our team from working on features and improvements that our users are waiting for.

    Learn more \u203a

    Professional Users

    The feature set and support options of our Community Edition are intended for personal use. Enterprise users are welcome to contact us for a commercial license and professional services.

    "},{"location":"getting-started/raspberry-pi/","title":"Running PhotoPrism on a Raspberry Pi","text":"

    Our stable releases and preview builds are available as multi-arch Docker images for 64-bit AMD, Intel, and ARM processors.1 As a Raspberry Pi owner, you therefore get the exact same functionality and can follow the same installation steps after going through a short list of system requirements and architecture specific notes.

    PhotoPrismPi

    The easiest way to run PhotoPrism on a Raspberry Pi2 is with PhotoPrismPi. Simply flash the image to an SD card, plug it into the Pi and boot it. After a few minutes, our latest release will be ready to use!

    "},{"location":"getting-started/raspberry-pi/#system-requirements","title":"System Requirements","text":"
    • For a good user experience, we recommend running PhotoPrism on a Raspberry Pi 4 or 5 with at least 4 GB RAM and a 64-bit operating system
    • High-resolution panoramic images may require additional swap space and/or physical memory above the recommended minimum
    • Indexing performance will benefit greatly from using SSD storage, e.g. connected via USB 3
    • Ensure that your device has at least 4 GB of swap configured and avoid setting a hard memory limit as this can cause unexpected restarts when the indexer temporarily needs more memory to process large files
    • Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum; RAW image conversion and TensorFlow are disabled on systems with 1 GB or less memory
    • You should enable HTTPS or run your server behind a secure HTTPS reverse proxy like Traefik if it is connected to a shared network or the public Internet
    • Depending on the Linux distribution, you may need to set the following security options in your docker-compose.yml:
      photoprism:\n  security_opt:\n    - seccomp:unconfined\n    - apparmor:unconfined\n
    "},{"location":"getting-started/raspberry-pi/#architecture-specific-notes","title":"Architecture Specific Notes","text":""},{"location":"getting-started/raspberry-pi/#modern-arm64-based-devices","title":"Modern ARM64-based Devices","text":"Image Name Stable Release photoprism/photoprism:latest Development Preview photoprism/photoprism:preview MariaDB arm64v8/mariadb:11

    Running 64-bit Docker images under Raspbian Linux requires a minimum of technical experience to perform the necessary configuration changes. This is because it is a 32-bit operating system with merely a 64-bit kernel to ensure compatibility with legacy software. If you don't need compatibility with 32-bit apps, we recommend choosing a standard 64-bit Linux distribution instead as it will save you time and requires less experience:

    • Raspberry Pi Debian
    • Ubuntu for Raspberry Pi
    • UbuntuDockerPi is a 64-bit Ubuntu Server with Docker pre-configured

    Other distributions that target the same use case as Raspbian, such as CoreELEC, will have similar issues and should therefore also be avoided to run modern server applications.

    "},{"location":"getting-started/raspberry-pi/#raspberry-pi-os","title":"Raspberry Pi OS","text":"

    To ensure compatibility with 64-bit Docker images, your Raspberry Pi must boot with the arm_64bit=1 flag in its config.txt file. An \"exec format\" error will occur otherwise.

    Try explicitly pulling the ARM64 version if you've booted your device with the arm_64bit=1 flag and you see the \"no matching manifest\" error on Raspberry Pi OS (Raspbian):

    docker pull --platform=arm64 photoprism/photoprism:latest\n

    It may also help to set the DOCKER_DEFAULT_PLATFORM environment variable to linux/arm64.

    In case you see Docker errors related to \"cgroups\", try adding the following parameters to /boot/firmware/cmdline.txt or /boot/cmdline.txt (file location depends on the OS in use):

    cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1\n
    "},{"location":"getting-started/raspberry-pi/#older-armv7-based-devices","title":"Older ARMv7-based Devices","text":"

    You may use the following 32-bit Docker images to run PhotoPrism and MariaDB on ARMv7-based devices (always use our ARM64 image if possible):

    Image Name Stable Release photoprism/photoprism:armv7 Development Preview photoprism/photoprism:preview-armv7 MariaDB yobasystems/alpine-mariadb:latest

    If your device meets the requirements, mostly the same installation instructions as for regular Linux servers apply. However, you should pay close attention to differences in path and environment variable names.

    Darktable is not included in the ARMv7 image because it is not 32-bit compatible. Always choose the regular 64-bit version if your device supports it.

    "},{"location":"getting-started/raspberry-pi/#is-a-raspberry-pi-fast-enough","title":"Is a Raspberry Pi fast enough?","text":"

    This mainly depends on your expectations and the number of files you have. Most users report that PhotoPrism runs smoothly on a Raspberry Pi 4 with 4 GB of RAM.

    Note, however, that initial indexing usually takes much longer than on a regular desktop computer and that the hardware has limited video transcoding capabilities, so video file format conversion is not well supported and software transcoding is generally slow. We take no responsibility for instability or performance problems if your device does not meet the requirements.

    "},{"location":"getting-started/raspberry-pi/#getting-updates","title":"Getting Updates","text":"

    Open a terminal and change to the folder where your compose.yaml or docker-compose.yml file is located.3 Now run the following commands to download the newest image from Docker Hub and restart your instance in the background:

    docker compose pull\ndocker compose stop\ndocker compose up -d\n

    Pulling a new version can take several minutes, depending on your internet connection speed.

    Advanced users can add this to a Makefile so that they only have to type a single command like make update. See Command-Line Interface to learn more about terminal commands.

    Even when you use an image with the :latest tag, Docker does not automatically download new images for you. You can either manually upgrade as shown above, or set up a service like Watchtower to get automatic updates.

    "},{"location":"getting-started/raspberry-pi/#config-examples","title":"Config Examples","text":"

    We recommend that you compare your own docker-compose.yml with our latest examples from time to time, as they may include new config options or other enhancements relevant to you.

    "},{"location":"getting-started/raspberry-pi/#mariadb-server","title":"MariaDB Server","text":"

    Our config examples are generally based on the latest stable release to take advantage of performance enhancements. This does not mean older versions are no longer supported and you have to upgrade immediately.

    If MariaDB fails to start after upgrading from an earlier version (or migrating from MySQL), the internal management schema may be outdated. See Troubleshooting MariaDB Problems for instructions on how to fix this.

    "},{"location":"getting-started/raspberry-pi/#troubleshooting","title":"Troubleshooting","text":"

    If your device runs out of memory, the index is frequently locked, or other system resources are running low:

    • Try reducing the number of workers by setting PHOTOPRISM_WORKERS to a reasonably small value in docker-compose.yml, depending on the performance of your device
    • Ensure that your device has at least 4 GB of swap configured and avoid setting a hard memory limit as this can cause unexpected restarts when the indexer temporarily needs more memory to process large files
    • If you are using SQLite, switch to MariaDB, which is better optimized for high concurrency
    • As a last measure, you can disable the use of TensorFlow for image classification and facial recognition

    Other issues? Our troubleshooting checklists help you quickly diagnose and solve them.

    You are welcome to ask for help in our community chat. Sponsors receive direct technical support via email. Before submitting a support request, try to determine the cause of your problem.

    1. Experienced users can alternatively use the packages at dl.photoprism.app/pkg/linux/ to manually install PhotoPrism on compatible Linux distributions. For more installation methods, see our Getting Started FAQ.\u00a0\u21a9

    2. Since our current MicroSD image is based on Ubuntu 22.04 LTS, it is not yet compatible with the Raspberry Pi 5, which requires Ubuntu 23.10 or later. An updated image will be provided as soon as possible.\u00a0\u21a9

    3. The default Docker Compose config filename is docker-compose.yml. For simplicity, it doesn't need to be specified when running docker compose or docker-compose in the same directory. Config files for other apps or instances should be placed in separate folders.\u00a0\u21a9

    "},{"location":"getting-started/updates/","title":"Getting Updates","text":""},{"location":"getting-started/updates/#docker-compose","title":"Docker Compose","text":"

    Open a terminal and change to the folder where your compose.yaml or docker-compose.yml file is located.1 Now run the following commands to download the newest image from Docker Hub and restart your instance in the background:

    docker compose pull\ndocker compose stop\ndocker compose up -d\n

    Note that our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    Pulling a new version can take several minutes, depending on your internet connection speed.

    Advanced users can add this to a Makefile so that they only have to type a single command like make update. See Command-Line Interface to learn more about terminal commands.

    Even when you use an image with the :latest tag, Docker does not automatically download new images for you. You can either manually upgrade as shown above, or set up a service like Watchtower to get automatic updates.

    "},{"location":"getting-started/updates/#config-examples","title":"Config Examples","text":"

    We recommend that you compare your own docker-compose.yml with our latest examples from time to time, as they may include new config options or other enhancements relevant to you.

    "},{"location":"getting-started/updates/#development-preview","title":"Development Preview","text":"

    You can test upcoming features and enhancements by changing the photoprism/photoprism image tag from :latest to :preview and then running the following commands to download the newest image from Docker Hub and restart your instance in the background:

    docker compose pull\ndocker compose stop\ndocker compose up -d\n

    Note that our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    "},{"location":"getting-started/updates/#watchtower","title":"Watchtower","text":"

    Adding Watchtower as a service to your compose.yaml or docker-compose.yml will automatically keep images up-to-date:

    services:\n  watchtower:\n    image: containrrr/watchtower\n    restart: unless-stopped\n    volumes:\n      - \"/var/run/docker.sock:/var/run/docker.sock\"\n

    Users of our DigitalOcean 1-Click App have Watchtower pre-installed.

    Danger

    Keep in mind that automatic updates can interrupt indexing and import operations, and enable Watchtower only if this is acceptable to you.

    "},{"location":"getting-started/updates/#portainer","title":"Portainer","text":"

    You can change or update the image you are using by navigating to \"Stacks\", selecting your existing PhotoPrism stack, and clicking \"Editor\":

    When you have changed the configuration to your needs or just want to get the latest image from Docker Hub, scroll down, click on \"Update the stack\", enable the \"re-pull and redeploy\" option, and then click on \"Update\":

    "},{"location":"getting-started/updates/#pure-docker","title":"Pure Docker","text":"

    Open a terminal on your server, and run the following command to pull the most recent container image:

    docker pull photoprism/photoprism:latest\n

    See Running PhotoPrism with Docker for a command reference.

    "},{"location":"getting-started/updates/#openmediavault","title":"OpenMediaVault","text":"

    To upgrade your instance, open a terminal, download our newest image from Docker Hub, and then restart the service:

    podman pull docker.io/photoprism/photoprism:latest\nsystemctl restart pod-photoprism.service\n
    "},{"location":"getting-started/updates/#complete-rescan","title":"Complete Rescan","text":"

    We recommend performing a complete rescan after major updates to take advantage of new search filters and sorting options. Be sure to read the notes for each release to see what changes have been made and if they might affect your library, for example, because of the file types you have or because new search features have been added. If you encounter problems that you cannot solve otherwise (i.e. before reporting a bug), please also try a rescan and see if it solves the problem.

    You can start a rescan from the user interface by navigating to Library > Index, selecting \"Complete Rescan\", and then clicking \"Start\". Manually entered information such as labels, people, titles or descriptions will not be modified when indexing, even if you perform a \"complete rescan\".

    Be careful not to start multiple indexing processes at the same time, as this will lead to a high server load.

    "},{"location":"getting-started/updates/#face-recognition","title":"Face Recognition","text":"

    Existing users may index faces without performing a complete rescan:

    docker compose exec photoprism photoprism faces index\n

    Remove existing people and faces for a clean start e.g. after upgrading from our development preview:

    docker compose exec photoprism photoprism faces reset -f\n
    "},{"location":"getting-started/updates/#mariadb-server","title":"MariaDB Server","text":"

    Our configuration examples are generally based on the current stable version to take advantage of performance improvements. This does not mean that older versions are no longer supported and you must upgrade immediately. We recommend not using the :latest tag for the MariaDB Docker image and to upgrade manually by changing the tag once we had a chance to test a new major version, e.g.:

    services:\n  mariadb:\n    image: mariadb:11\n    ...\n

    If MariaDB fails to start after upgrading from an earlier version (or migrating from MySQL), the internal management schema may be outdated. See Troubleshooting MariaDB Problems for instructions on how to fix this.

    "},{"location":"getting-started/updates/#raspberry-pi","title":"Raspberry Pi","text":"

    Our stable releases and preview builds are available as multi-arch Docker images for 64-bit AMD, Intel, and ARM processors. You therefore get the exact same functionality and can follow the same update instructions if your device meets the system requirements.

    Try explicitly pulling the ARM64 version if you've booted your device with the arm_64bit=1 flag and you see the \"no matching manifest\" error on Raspberry Pi OS (Raspbian):

    docker pull --platform=arm64 photoprism/photoprism:latest\n

    If you don't use legacy software, we recommend choosing a standard 64-bit Linux distribution as it requires less experience. For ARMv7-based devices, 32-bit images are provided separately.

    Darktable is not included in the ARMv7 version because it is not 32-bit compatible.

    "},{"location":"getting-started/updates/#photoprism-plus","title":"PhotoPrism\u00ae Plus","text":"

    Our members can activate additional features by logging in with the admin user created during setup and then following the steps described in our activation guide. Thank you for your support, which has been and continues to be essential to the success of the project!

    Compare Memberships \u203a View Membership FAQ \u203a

    We recommend that new users install our free Community Edition before signing up for a membership.

    "},{"location":"getting-started/updates/#how-can-i-shorten-the-startup-time-after-a-restart-or-update","title":"How can I shorten the startup time after a restart or update?","text":"

    To reduce startup time, do not set PHOTOPRISM_INIT to avoid running additional setup scripts, and set PHOTOPRISM_DISABLE_CHOWN to \"true\" to disable automatic permission updates.

    If your instance doesn't start even after waiting for some time, our Troubleshooting Checklists help you quickly diagnose and solve the problem.

    1. The default Docker Compose config filename is docker-compose.yml. For simplicity, it doesn't need to be specified when running docker compose or docker-compose in the same directory. Config files for other apps or instances should be placed in separate folders.\u00a0\u21a9

    "},{"location":"getting-started/using-a-cdn/","title":"Using a Content Delivery Network (CDN)","text":"

    A Content Delivery Network is a distributed network of servers that can deliver static content to users around the world.

    "},{"location":"getting-started/using-a-cdn/#when-to-use-a-cdn","title":"When to use a CDN?","text":"

    Large Media Files: PhotoPrism stores photos and videos that can be very large. A CDN can help speed up the delivery of these files to users.

    Global Audience: If your PhotoPrism instance is accessed from different locations around the world, a CDN can help reduce latency and improve the overall user experience by delivering content from servers that are closer to your users.

    Many Users: If your PhotoPrism instance is getting a lot of traffic, a CDN can improve application performance by reducing the load on your server.

    "},{"location":"getting-started/using-a-cdn/#config-options","title":"Config Options","text":"

    You can use the following config options to specify the URL of an external CDN and change the cache expiration time for thumbnails and other static content:

    Environment CLI Flag Default Description PHOTOPRISM_CDN_URL --cdn-url content delivery network URL PHOTOPRISM_CDN_VIDEO --cdn-video false stream videos over the specified CDN PHOTOPRISM_HTTP_CSP --http-csp HTTP Content-Security-Policy (CSP) HEADER plus PHOTOPRISM_HTTP_CACHE_PUBLIC --http-cache-public true allow static content to be cached by a CDN or caching proxy PHOTOPRISM_HTTP_CACHE_MAXAGE --http-cache-maxage 2592000 time in SECONDS until cached content expires PHOTOPRISM_HTTP_VIDEO_MAXAGE --http-video-maxage 21600 time in SECONDS until cached videos expire"},{"location":"getting-started/using-a-cdn/#cdn-providers","title":"CDN Providers","text":""},{"location":"getting-started/using-a-cdn/#bunnynet","title":"bunny.net","text":"

    If you don't have a CDN provider yet, we can recommend bunny.net. This EU-based company has a cute name, but is a reputable provider with excellent performance, a wide range of features, and more than 20,000 customers including big names like Hyundai. We also chose bunny.net for our website and public demo as they are fully compliant with the GDPR.1

    Pricing starts at $0.005/GB and there is no minimum usage or monthly fee, so you only pay for what you actually need.

    Learn more \u203a

    "},{"location":"getting-started/using-a-cdn/#cloudflare","title":"Cloudflare","text":"

    Cloudflare works similarly to a reverse proxy and allows you to make a private server publicly accessible over the Internet. This means that users accessing your instance through their service will only see a single URL as if they were connecting directly.

    You must therefore not configure a CDN URL for your site, as this may prevent the user interface from loading. Also note that their free tier does not include video streaming, so there may be problems with video playback if you are not a paying customer.

    1. We receive a $20 credit when you sign up through our link, which helps us fund the project infrastructure.\u00a0\u21a9

    "},{"location":"getting-started/using-https/","title":"Using HTTPS","text":"

    This guide shows you how to enable HTTPS/TLS, configure existing server certificates, and obtain new certificates as needed. If you have suggestions for improvement, please let us know by clicking to send a pull request.

    "},{"location":"getting-started/using-https/#why-use-encryption","title":"Why Use Encryption?","text":"

    If you install PhotoPrism on a shared server so that it is not only accessible to the local host, always secure the connection using HTTPS. Your files and passwords will otherwise be transmitted in clear text and can be intercepted by anyone, including your provider, hackers, and governments. Backup tools and file synchronization apps may also refuse to connect.

    HTTPS connections use Transport Layer Security (TLS) for encryption. TLS is a network protocol that establishes an encrypted connection to an authenticated peer over an untrusted network.

    "},{"location":"getting-started/using-https/#how-to-enable-https","title":"How To Enable HTTPS","text":"

    You have the following options to enable HTTPS/TLS when using our latest stable release. Note that after adding or updating certificates, it is required to restart PhotoPrism for the changes to take effect.

    "},{"location":"getting-started/using-https/#1-https-reverse-proxy","title":"1. HTTPS Reverse Proxy","text":"

    To run your instance behind an HTTPS reverse proxy like Traefik, we recommend that you explicitly disable TLS in PhotoPrism by setting PHOTOPRISM_DISABLE_TLS to \"true\" in your compose.yaml or docker-compose.yml configuration:

    services:\n  photoprism:\n    # ...\n    environment:\n      PHOTOPRISM_SITE_URL: \"https://www.example.com/\"\n      PHOTOPRISM_DISABLE_TLS: \"true\"\n

    Especially if your server also has other web applications installed and/or a proxy with working HTTPS is already in place, this may be the best option.

    "},{"location":"getting-started/using-https/#2-self-signed-certificate","title":"2. Self-Signed Certificate","text":"
    services:\n  photoprism:\n    # ...\n    environment:\n      PHOTOPRISM_SITE_URL: \"https://www.example.com/\"\n      PHOTOPRISM_DISABLE_TLS: \"false\"\n      PHOTOPRISM_DEFAULT_TLS: \"true\"\n      PHOTOPRISM_INIT: \"https\"\n
    "},{"location":"getting-started/using-https/#3-custom-certificate","title":"3. Custom Certificate","text":"

    To use your own certificates, you can add a custom TLS certificate and private key to the storage/config/certificates folder with the filenames www.example.com.crt and www.example.com.key, replacing www.example.com with the actual server domain. For this, you can set the same config options as when using a self-signed certificate (see above).

    Alternatively, you can specify a custom TLS certificate (*.crt) and private key (*.key) filename within the storage/config/certificates folder using the PHOTOPRISM_TLS_CERT and PHOTOPRISM_TLS_KEY environment variables in your compose.yaml or docker-compose.yml, or use the corresponding command flags:

    services:\n  photoprism:\n    # ...\n    environment:\n      PHOTOPRISM_SITE_URL: \"https://www.example.com/\"\n      PHOTOPRISM_TLS_CERT: \"site.crt\"\n      PHOTOPRISM_TLS_KEY: \"site.key\"\n      PHOTOPRISM_DISABLE_TLS: \"false\"\n      PHOTOPRISM_DEFAULT_TLS: \"true\"\n      PHOTOPRISM_INIT: \"https\"\n

    We recommend that you keep the PHOTOPRISM_DEFAULT_TLS option enabled so that you can always connect securely over HTTPS even if there is a problem with your custom certificates.

    "},{"location":"getting-started/using-https/#obtaining-certificates","title":"Obtaining Certificates","text":"

    Valid server certificates can be obtained either from a commercial Certificate Authority (CA) like ZeroSSL or free of charge from Let's Encrypt:

    "},{"location":"getting-started/using-https/#lets-encrypt","title":"Let\u2019s Encrypt","text":"

    Let's Encrypt is an automatic certificate authority that provides you with free HTTPS/TLS certificates. Many web servers and reverse proxies such as Traefik and Caddy have integrated support for obtaining single-domain certificates if your server is accessible on port 80 over the public Internet.

    The creation of certificates for servers that are not publicly reachable or that are valid for all subdomains (wildcard) is alternatively possible with the LEGO Let's Encrypt client. If you use Docker and DigitalOcean's free DNS service, the command to run will look as follows (replace the certificate path, access token, domain names, and email address with the appropriate values):

    docker run --rm -v \"/path/to/certificates:/data/\" \\\n-e DO_AUTH_TOKEN=Your_Access_Token goacme/lego -a \\\n-d \"example.com\" -d \"*.example.com\" --email=\"you@example.com\" \\\n--dns=digitalocean --dns-timeout=180 --path=/data run \n

    Note that this verification method only works if you use a supported DNS provider that LEGO can access through an API. Please refer to its documentation for details, as each provider requires different authentication credentials. If you are using DigitalOcean, you can create the required access token in your customer dashboard and replace Your_Access_Token with it.

    "},{"location":"getting-started/using-https/#zerossl","title":"ZeroSSL","text":"

    ZeroSSL is a widely trusted commercial certificate authority with more than 500,000 customers worldwide. Its headquarters are located in Vienna, Austria.

    Compared to Let's Encrypt, you can also create and revoke certificates through a user-friendly web interface, obtain certificates with a validity of more than 90 days, and choose between additional domain verification methods.1

    Learn more \u203a

    "},{"location":"getting-started/using-https/#troubleshooting","title":"Troubleshooting","text":""},{"location":"getting-started/using-https/#enabling-trace-log-mode","title":"Enabling Trace Log Mode","text":"

    A good way to troubleshoot configuration issues is to increase the log level. To enable trace log mode, set PHOTOPRISM_LOG_LEVEL to \"trace\" in the environment: section of the photoprism service (or use the --trace flag when running the photoprism command directly):

    services:\n  photoprism:\n    environment:\n      PHOTOPRISM_LOG_LEVEL: \"trace\"\n      ...\n

    Then restart all services for your changes to take effect:

    docker compose stop\ndocker compose up -d\n
    "},{"location":"getting-started/using-https/#viewing-docker-service-logs","title":"Viewing Docker Service Logs","text":"

    You can run this command to check the server logs for warnings and errors, including the last 100 messages (omit --tail=100 to see them all, and -f to output only the last logs without watching them):

    docker compose logs -f --tail=100 \n

    Learn more \u203a

    "},{"location":"getting-started/using-https/#determining-your-public-ip-address","title":"Determining Your Public IP Address","text":"

    If you have a domain name and want to set up a public host entry for your home network, you can use one of the following services to see your public IP address, i.e. the IP address that others outside your home network can use to reach you:

    • https://api.ipify.org/
    • https://canhazip.com/
    "},{"location":"getting-started/using-https/#failed-to-find-any-pem-data-in-key-input","title":"Failed to Find Any PEM Data in Key Input","text":"

    This error can indicate that your key file starts with an unexpected Byte Order Mark (BOM):

    • https://www.google.com/search?q=failed+to+find+any+pem+data+tls+golang
    • https://stackoverflow.com/questions/57596920/failed-to-find-any-pem-data-in-key

    While BOMs are not strictly forbidden, there is only one way to encode UTF-8, and so they are not needed and extremely rare. As a result, a lot of software has problems with them.

    You should be able to fix this by opening the file with a regular text or code editor (not Notepad) and then saving it again. Finally, restart all services for the changes to take effect:

    docker compose stop\ndocker compose up -d\n

    Our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    1. We may receive a credit when you sign up through our link, which helps us fund the project infrastructure.\u00a0\u21a9

    "},{"location":"getting-started/advanced/backups/","title":"Advanced Backup Guide","text":"

    At a minimum, a backup of PhotoPrism should include the files in your originals folder and a copy of the index database. We also recommend backing up the storage folder so that you don't need to recreate any thumbnail or sidecar files, and your backup includes the complete configuration.

    The easiest way to create a full backup is to first run the backup command to generate a database dump as described in our Backup Guide. Then back up your originals and storage folders using any standard file backup utility.

    "},{"location":"getting-started/advanced/backups/#scheduled-backups","title":"Scheduled Backups","text":"

    By default, PhotoPrism 240523-923ee0cf7 and newer versions automatically create daily database backups for you, with up to 3 copies being retained. The schedule, the type of backups, and the number of backups to be retained can be changed in the configuration.

    "},{"location":"getting-started/advanced/backups/#backup-command","title":"Backup Command","text":"

    You can run the following command in a terminal to manually create a new MariaDB or SQLite database backup:

    photoprism backup -i [filename]\n

    Or the following if you are using docker-compose:

    docker compose exec -T photoprism photoprism backup -i - > photoprism-db.sql\n

    As seen above, you can use - as filename to write the backup to stdout. This is done to ensure the backup resides outside of the container environment.

    If you leave the filename empty, the backup will be written to the default backup folder configured via PHOTOPRISM_BACKUP_PATH.

    If you want, you can also export your cache and thumbnails, but it can also be re-generated after restore. It will save you from re-generating thumbnails from scratch however.

    Helpful information can be found on GitHub as well.

    "},{"location":"getting-started/advanced/backups/#restore-command","title":"Restore Command","text":"

    See our regular Backup Guide to learn how to restore backups.

    "},{"location":"getting-started/advanced/backups/#sqlite-backups","title":"SQLite Backups","text":"

    If you are using a current version, you can create SQL dumps of SQLite with the photoprism backup command. Since the binary SQLite database files are located in the storage folder, they should also be automatically included in any backup.

    In order to create a dump directly with SQLite, you can alternatively run this command:

    docker compose exec -T photoprism sqlite3 /photoprism/storage/index.db .dump > photoprism-db.sql\n

    With pure docker, you can run the following (or replace docker with podman on Red Hat-based Linux distributions):

    docker exec -t PhotoPrism sqlite3 /photoprism/storage/index.db .dump > photoprism-db.sql\n

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"getting-started/advanced/caching/","title":"Optimizing Cache Performance","text":"

    While we believe this post may be helpful to advanced users, we have not yet reviewed it thoroughly. If you have suggestions for improvement, please let us know by clicking to send a pull request.

    For advanced users only. This guide is maintained by the community and may contain inaccurate or incomplete advice. You can contribute by clicking to send a pull request with your changes.

    Some users might want to place the thumbnail cache on a separate, faster file system while keeping the actual photo files on large, slow bulk storage. This should result in faster access to the thumbnails.

    To do this, we add a further volume (-v) parameter to the docker script so we use an external path (outside the container) for the cache files. You can get the internal path with photoprism config, or as a docker command in a running system (for Linux/BSD systems):

    sudo docker exec photoprism photoprism config | grep cache-path\n

    This should return a line such as:

    cache-path            /home/photoprism/.cache/photoprism\n

    for the internal path. We now know to add a line like

      -v <MYCACHE_FOLDER>:/home/photoprism/.cache/photoprism \\\n

    to the docker invocation, with your actual path to the cache folder replacing <MYCACHE_FOLDER>.

    As an example, let's assume a ZFS filesystem with two pools (\"volumes\" in classical terminology): A pool tank in a raidz2 (RAID6) configuration based on hard drives that holds the original pictures, and a pool dozer in a mirrored (RAID1) configuration based on SSD or NVMe drives to store the thumbnails. Our docker script could be:

    docker run -d \\\n  --name photoprism \\\n  -p 2342:2342 \\\n  -v /tank/photos/:/home/photoprism/Pictures/Originals \\\n  -v /dozer/cache/:/home/photoprism/.cache/photoprism \\\n  photoprism/photoprism:latest\n

    In a case like this, you will probably also want to optimize the datasets (\"file systems\") tank/photos and dozer/cache further. For instance, the original photo files will call for a larger recordsize than the smaller cache files.

    "},{"location":"getting-started/advanced/databases/","title":"Advanced Database Setup","text":"

    While we believe this contributed content may be helpful to advanced users, we have not yet thoroughly reviewed it. If you have suggestions for improvement, please let us know by clicking to send a pull request.

    "},{"location":"getting-started/advanced/databases/#compatibility","title":"Compatibility","text":"

    PhotoPrism is compatible with SQLite 3 and MariaDB 10.5.12+. Official support for MySQL 8 is discontinued as Oracle seems to have stopped shipping new features and enhancements. As a result, the testing effort required before each release is no longer feasible.

    Our configuration examples are generally based on the current stable version to take advantage of performance improvements. This does not mean that older versions are no longer supported and you must upgrade immediately. We recommend not using the :latest tag for the MariaDB Docker image and to upgrade manually by changing the tag once we had a chance to test a new major version.

    "},{"location":"getting-started/advanced/databases/#storage","title":"Storage","text":"

    Local Solid-State Drives (SSDs) are best for databases of any kind. Never store database files on an unreliable device such as a USB flash drive, SD card, or shared network folder. These may also have unexpected file size limitations, which is especially problematic for databases that do not split data into smaller files.

    Please do not use a named or anonymous Docker volume for storing MariaDB database files and check the mount path of the volume if you use a custom database image (it may not always be /var/lib/mysql), as both can lead to data loss when the database container is recreated, e.g. after an update of the Docker image.

    "},{"location":"getting-started/advanced/databases/#configuration","title":"Configuration","text":"

    When creating a new database, make sure to set the charset and collation as follows:

    CREATE DATABASE photoprism\nCHARACTER SET = 'utf8mb4'\nCOLLATE = 'utf8mb4_unicode_ci';\n

    Now create a user and grant privileges for this new database:

    CREATE USER 'photoprism'@'%' IDENTIFIED BY 'insecure';\nGRANT ALL PRIVILEGES ON photoprism.* to 'photoprism'@'%';\nFLUSH PRIVILEGES;\n

    Set the database environment variables for PhotoPrism and MariaDB as follows:

    services:\n  photoprism:\n    environment:\n      PHOTOPRISM_DATABASE_DRIVER: \"mysql\"\n      PHOTOPRISM_DATABASE_SERVER: \"mariadb:3306\"\n      PHOTOPRISM_DATABASE_NAME: \"photoprism\"\n      PHOTOPRISM_DATABASE_USER: \"photoprism\"\n      PHOTOPRISM_DATABASE_PASSWORD: \"insecure\"\n\n  mariadb:\n    environment:\n      MARIADB_AUTO_UPGRADE: \"1\"\n      MARIADB_INITDB_SKIP_TZINFO: \"1\"\n      MARIADB_DATABASE: \"photoprism\"\n      MARIADB_USER: \"photoprism\"\n      MARIADB_PASSWORD: \"insecure\"\n      MARIADB_ROOT_PASSWORD: \"insecure\"\n

    Set strong passwords if the database is exposed to an external network. Never expose your database to the public Internet in this way, for example, if it is running on a cloud server.

    "},{"location":"getting-started/advanced/databases/#schema-migrations","title":"Schema Migrations","text":"

    An index schema migration is performed automatically every time PhotoPrism is (re)started. The following instructions may be helpful in special cases, such as when a temporary problem has prevented a successful migration:

    Run Migrations \u203a

    "},{"location":"getting-started/advanced/databases/#change-database","title":"Change Database","text":"

    Migrate from SQLite to MariaDB \u203a

    Migrate from MariaDB to SQLite \u203a

    Bad Performance

    Many users reporting poor performance and high CPU usage have migrated from SQLite to MariaDB, so their database schema is no longer optimized for performance. For example, MariaDB cannot handle rows with text columns in memory and always uses temporary tables on disk if there are any.

    If this is the case, please make sure that your migrated database schema matches that of a fresh, non-migrated installation. It may help to run the migrations manually in a terminal using the migrations subcommands. However, this does not guarantee that all issues such as missing indexes are resolved.

    Due to the amount of time required to review each report, we can only offer this to eligible members and business customers, and not to users who have chosen our free community edition.

    Get Performance Tips \u203a View Database Schema \u203a

    "},{"location":"getting-started/advanced/docker-security/","title":"Docker Security Guide","text":"

    This documentation is intended for experienced users who want to enhance the security of their installation. If you have suggestions for improvement, please let us know by clicking to send a pull request.

    "},{"location":"getting-started/advanced/docker-security/#get-the-latest-security-updates","title":"Get the Latest Security Updates","text":"

    Even though PhotoPrism is developed in Go and therefore does not use many of the C libraries installed in our Docker image, external file converters like Darktable and FFmpeg as well as other tools installed as dependencies might use them. They may also be directly affected by recently discovered vulnerabilities for which updates are available.

    To automatically install these updates when the container starts for the first time, you can add PHOTOPRISM_INIT: \"update\" to the environment section of the photoprism service in your compose.yaml or docker-compose.yml:

    services:\n  photoprism:\n    environment:\n      PHOTOPRISM_INIT: \"update\"\n      ...\n    volumes:\n      - ...\n

    This can be combined with other init actions such as https, gpu and/or tensorflow, e.g. PHOTOPRISM_INIT: \"update https gpu tensorflow\". For the changes to take effect, run the following to restart the services (--force-recreate will always recreate the containers to apply available updates, even if their configuration has not been changed):

    docker compose stop\ndocker compose up -d --force-recreate\n

    We also recommend making sure that the latest Docker version and security updates are automatically installed on your host operating system.

    Note that our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    "},{"location":"getting-started/advanced/docker-security/#run-services-as-non-root-user","title":"Run Services as Non-Root User","text":"

    It is recommended that you run the photoprism service as a non-root user by setting either the user service property or the PHOTOPRISM_UID and PHOTOPRISM_GID environment variable in your compose.yaml or docker-compose.yml file:

    Environment Default Description PHOTOPRISM_UID 0 run as a non-root user after initialization (supported: 0, 33, 50-99, 500-600, 900-1250, and 2000-2100) PHOTOPRISM_GID 0 run with a specific group id after initialization, can optionally be used together with PHOTOPRISM_UID (supported: 0, 33, 44, 50-99, 105, 109, 115, 116, 500-600, 900-1250, and 2000-2100)

    If you are using hardware video transcoding, it should depend on the owner of the video device which user and group you choose so that the service has permission to access it.

    Finally, remember to update the file permissions and/or owner with the chmod and chown commands when you make changes to the UID or GID, and restart the services for your changes to take effect:

    docker compose stop\ndocker compose up -d\n
    "},{"location":"getting-started/advanced/docker-security/#remove-passwords-from-the-environment","title":"Remove Passwords From the Environment","text":"

    Passwords specified directly in a docker-compose.yml file or otherwise passed to the container environment may pose a security risk. As an alternative, they can be set in an options.yml file located in the config storage folder:

    AdminPassword: \"my super secret password\"\nDatabasePassword: \"my super secret password\"\n

    Likewise, MariaDB can be configured to use Docker secret files. For details, see the Docker Compose Documentation.

    The following is an example of the changes to your compose.yaml or docker-compose.yml file. Note that this example includes only the additional lines required to pass secret files to the MariaDB container:

    secrets:\n  # Secrets are single-line text files where the sole\n  # content is the secret. Paths in this example assume\n  # that secrets are kept in local \".secrets\" folder. \n  DB_ROOT_PWD:\n    file: .secrets/db_root_pwd.txt\n  DB_PWD:\n    file: .secrets/db_pwd.txt\n\nservices:\n  mariadb:\n    environment:\n      # Change the env variables to _FILE and point them to\n      # the file locations within the container.\n      MARIADB_PASSWORD_FILE: /run/secrets/DB_PWD\n      MARIADB_ROOT_PASSWORD_FILE: /run/secrets/DB_ROOT_PWD\n    secrets:\n      # Give the container access to the secrets to mount\n      # the files within the container.\n      - DB_ROOT_PWD\n      - DB_PWD\n
    "},{"location":"getting-started/advanced/docker-security/#rootless-docker","title":"Rootless Docker","text":"

    In addition, you can run the Docker daemon as a non-root user in rootless mode. Configuring this is beyond the scope of this guide. For more information and instructions, see the Docker Security Documentation.

    "},{"location":"getting-started/advanced/docker-volumes/","title":"Docker Volume Mounts","text":"

    When using Docker, all application services run in isolated containers, so you must explicitly mount the host folders you want to use. Be aware that PhotoPrism and MariaDB cannot see folders that have not been mounted. This is an important security feature.

    It is important that the originals, storage, and database folders are located on persistent volumes. We recommend changing the relative paths used in our examples to absolute paths and to avoid using named or anonymous Docker volumes to prevent potential data loss when the container is recreated, e.g. after an update of the Docker image.

    "},{"location":"getting-started/advanced/docker-volumes/#originals-folder","title":"Originals Folder","text":"

    The originals folder contains your original photo and video files.

    ~/Pictures will be mounted by default, where ~ is a shortcut for your home directory:

    volumes:\n  # \"/host/folder:/photoprism/folder\"  # example\n  - \"~/Pictures:/photoprism/originals\"\n

    You can mount any folder accessible from the host, including network shares. Additional directories can also be mounted as sub folders of /photoprism/originals (depending on overlay file system support):

    volumes:\n  - \"/home/username/Pictures:/photoprism/originals\"\n  - \"/example/friends:/photoprism/originals/friends\"\n  - \"/mnt/photos:/photoprism/originals/media\"\n

    On Windows, prefix the host path with the drive letter and use / instead of \\ as separator:

    volumes:\n  - \"D:/Example/Pictures:/photoprism/originals\"\n

    If read-only mode is enabled, all features that require write permission to the originals folder are disabled, e.g. WebDAV, uploading and deleting files. Set PHOTOPRISM_READONLY to \"true\" in docker-compose.yml for this. You can mount a folder with the :ro flag to make Docker block write operations as well.

    "},{"location":"getting-started/advanced/docker-volumes/#storage-folder","title":"Storage Folder","text":"

    SQLite, config, cache, backup, thumbnail and sidecar files are saved in the storage folder:

    • a storage folder mount must always be configured in your compose.yaml or docker-compose.yml file so that you do not lose these files after a restart or upgrade
    • never configure the storage folder to be inside the originals folder unless the name starts with a . to indicate that it is hidden
    • we recommend placing the storage folder on a local SSD drive for best performance
    • mounting symbolic links or using them inside the storage folder is currently not supported
    • avoid using a named or anonymous Docker volume for permanently storing files, as this can lead to data loss when the container is recreated, e.g. after an update of the Docker image

    Should you later want to move your instance to another host, the easiest and most time-saving way is to copy the entire storage folder along with your originals and database.

    "},{"location":"getting-started/advanced/docker-volumes/#import-folder","title":"Import Folder","text":"

    You can optionally mount an import folder from which files can be transferred to the originals folder in a structured way that avoids duplicates:

    • imported files receive a canonical filename and will be organized by year and month
    • never configure the import folder to be inside the originals folder, as this will cause a loop by importing already indexed files

    You can safely skip this. Adding files via Web Upload and WebDAV remains possible, unless read-only mode is enabled or the features have been disabled.

    "},{"location":"getting-started/advanced/docker-volumes/#mariadb-database","title":"MariaDB Database","text":"

    Our example includes a pre-configured MariaDB database server that stores it files in the database folder by default. If you remove it and provide no other database server credentials, SQLite database files will be created in the storage folder.

    Please do not use a named or anonymous Docker volume for storing MariaDB database files and check the mount path of the volume if you use a custom database image (it may not always be /var/lib/mysql), as both can lead to data loss when the database container is recreated, e.g. after an update of the Docker image.

    Never store database files on an unreliable device such as a USB flash drive, SD card, or shared network folder. These may also have unexpected file size limitations, which is especially problematic for databases that do not split data into smaller files. We strongly recommend using SSD storage for databases only.

    "},{"location":"getting-started/advanced/docker-volumes/#network-storage","title":"Network Storage","text":"

    Shared folders that have already been mounted on your host under a drive letter or path can be used with Docker containers like any other directory. In addition, certain types of network storage like NFS (Unix/Linux) and CIFS (Windows/Mac) can also be mounted directly with Docker Compose.

    For more information, see the Network Storage section of our Docker Troubleshooting Guide.

    Configure Network Storage \u203a

    "},{"location":"getting-started/advanced/kubernetes/","title":"Running PhotoPrism with Kubernetes","text":"

    While we believe this contributed content may be helpful to advanced users, we have not yet thoroughly reviewed it. If you have suggestions for improvement, please let us know by clicking to submit a change request.

    At a minimum, you can just define a Kubernetes Service and a StatefulSet and be up and running. For more real-world usage, you'll probably want to at least include persistent storage, and possibly some Ingress rules for exposing PhotoPrism outside your cluster.

    Before you proceed, please ensure that your server has at least 4 GB of swap configured and avoid setting a hard memory limit as this can cause unexpected restarts when the indexer temporarily needs more memory to process large files. Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    For those familiar with Helm, a PhotoPrism Helm chart is available.

    Once you've got PhotoPrism deployed, you can exec into the running container and photoprism import your photos.

    Here's an example of a YAML file that creates the following Kubernetes objects:

    • Namespace
    • Service exposing PhotoPrism on port 80
    • StatefulSet with persistent NFS volumes
    • Secret which stores the database DSN and admin password
    • Ingress rule for a Kubernetes ingress controller
    • Annotations for a Kubernetes Certificate Manager
    apiVersion: v1\nkind: Namespace\nmetadata:\n  name: photoprism\n---\napiVersion: v1\nkind: Secret\nmetadata:\n  name: photoprism-secrets\n  namespace: photoprism\nstringData:\n  PHOTOPRISM_ADMIN_PASSWORD: <your admin password here>\n  PHOTOPRISM_DATABASE_DSN: username:password@tcp(db-server-address:3306)/dbname?charset=utf8mb4,utf8&parseTime=true\n---\napiVersion: apps/v1\nkind: StatefulSet\nmetadata:\n  name: photoprism\n  namespace: photoprism\nspec:\n  selector:\n    matchLabels:\n      app: photoprism\n  serviceName: photoprism\n  replicas: 1\n  template:\n    metadata:\n      labels:\n        app: photoprism\n    spec:\n      containers:\n      - name: photoprism\n        image: photoprism/photoprism:latest\n        env:\n        - name: PHOTOPRISM_DEBUG\n          value: \"true\"\n        - name: PHOTOPRISM_DATABASE_DRIVER\n          value: mysql\n        - name: PHOTOPRISM_HTTP_HOST\n          value: 0.0.0.0\n        - name: PHOTOPRISM_HTTP_PORT\n          value: \"2342\"\n        # Load database DSN & admin password from secret\n        envFrom:\n        - secretRef:\n            name: photoprism-secrets\n            optional: false\n        ports:\n        - containerPort: 2342\n          name: http\n        volumeMounts:\n        - mountPath: /photoprism/originals\n          name: originals\n        - mountPath: /photoprism/import\n          name: import\n        - mountPath: /photoprism/storage\n          name: storage\n        readinessProbe:\n          httpGet:\n            path: /api/v1/status\n            port: http\n      volumes:\n      - name: originals\n        nfs:\n          path: /originals\n          # readOnly: true # Disables import and upload!\n          server: my.nas.host\n      - name: import\n        nfs:\n          path: /import\n          server: my.nas.host\n      - name: storage\n        nfs:\n          path: /storage\n          server: my.nas.host\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: photoprism\n  namespace: photoprism\nspec:\n  ports:\n  - name: http\n    port: 80\n    protocol: TCP\n    targetPort: http\n  selector:\n    app: photoprism\n  type: ClusterIP\n---\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  annotations:\n    # For nginx ingress controller:\n    kubernetes.io/ingress.class: nginx\n    # Default is very low so most photo uploads will fail:\n    nginx.ingress.kubernetes.io/proxy-body-size: \"512M\"\n    # If using cert-manager:\n    cert-manager.io/cluster-issuer: letsencrypt-prod\n    kubernetes.io/tls-acme: \"true\"\n  name: photoprism\n  namespace: photoprism\nspec:\n  rules:\n  - host: photoprism.my.domain\n    http:\n      paths:\n      - backend:\n          service:\n            name: photoprism\n            port:\n              name: http\n        path: /\n        pathType: Prefix\n  tls:\n  - hosts:\n    - photoprism.my.domain\n    secretName: photoprism-cert\n

    To run this locally, you can use minikube or a similar local cluster deployer.

    Once your cluster is up and running with your kubectl commands. Simply copy the above YAML markup to a file, make the necessary changes, and use the kubectl CLI command to deploy:

    kubectl create -f photoprism.yaml\n

    If you prefer to use helm, see p80n/photoprism-helm.

    "},{"location":"getting-started/advanced/nginx-proxy-setup/","title":"Advanced NGINX Proxy Setup","text":"

    While we believe this contributed content may be helpful to advanced users, we have not yet thoroughly reviewed it. If you have suggestions for improvement, please let us know by clicking to submit a change request.

    Getting Support

    Since NGINX is notoriously difficult to configure, we are unable to provide technical support for NGINX-related issues such as failed uploads, connection errors, broken thumbnails, and video playback problems. If you cannot resolve these on your own, we recommend that you ask their community for advice or use Traefik instead, which is easier to configure and more convenient to handle overall.

    Using a reverse proxy in front of PhotoPrism has various benefits:

    • Make use of HTTP/2
    • Add encryption
    • Perform traffic optimization
    • Enhance security (NGINX may block dangerous request patterns the embedded Go-based HTTP server does not know about)

    If you consider exposing your PhotoPrism instance to the evil internet, you should at least secure it with a proper HTTPS encryption.

    This guide aims for a little more advanced setup, with good security in mind.

    Trough the entire guide we use photoprism.example.com as domain. You have to change all these occurrences with your own domain / DynDNS.

    Also, this was all tested on ubuntu/20.04 - But the commands should work on a lot of other versions as well. If you use CentOS, SuSE, or any other distro, look up the commands for your package manager.

    "},{"location":"getting-started/advanced/nginx-proxy-setup/#setup","title":"Setup","text":""},{"location":"getting-started/advanced/nginx-proxy-setup/#domain-setup","title":"Domain setup","text":"

    First, we need to decide what we want to use as domain / address. We will use a proper domain in this guide, but it will technically also work with just a static IP address.

    If you're lucky enough to have your own domain, just create a subdomain and use this for PhotoPrism.

    If you want to expose your instance hosted at home, just use a DynDNS provider (just duckduckgo it, there a multiple ones that are free).

    Tip

    if you're new to DynDNS it might be good to know that most routers do support DynDNS. Therefore only minimal setup is required. Just search for your router name + DynDNS ;)

    "},{"location":"getting-started/advanced/nginx-proxy-setup/#install-nginx-on-your-machine","title":"Install NGINX on your machine","text":"

    If not already done, install the webserver on your machine which you wish to use as proxy. This also can be the same host that is running the docker container.

    apt-get update && apt-get install -y nginx apache2-utils\n

    Info

    We use apache2-utils later for the password generation of the optional extra security with http basic auth.

    "},{"location":"getting-started/advanced/nginx-proxy-setup/#acquire-a-certificate","title":"Acquire a certificate","text":""},{"location":"getting-started/advanced/nginx-proxy-setup/#install-cerbot","title":"Install cerbot","text":"

    Luckily nowadays there is a free certificate authority called Let's Encrypt. You can just use their certbot tool and acquire a new certificate in seconds.

    For Ubuntu / Debian the package is directly in the repository. Therefore, you can easily install it using this command:

    apt-get update && apt-get install -y certbot python3-certbot-nginx\n
    "},{"location":"getting-started/advanced/nginx-proxy-setup/#generate-the-certificate","title":"Generate the certificate","text":"

    Now, just request a new certificate by using this command:

    certbot -d photoprism.example.com\n

    After that you should have a new certificate in: /etc/letsencrypt/live/photoprism.example.com/

    Note

    In order for Let's Encrypt to work, the server has to be accessible from the internet on port 443

    Tip

    Please refer to Let's Encrypts documentation for automated certificate renewal. The certificates are valid for around 90 days.

    "},{"location":"getting-started/advanced/nginx-proxy-setup/#setup-nginx","title":"Setup NGINX","text":"

    Now that we have our certificate its time to setup the NGINX itself. Since it is best practice to create a configuration file per application / domain, this is exactly what we gonna do.

    Create a new file /etc/nginx/sites-available/photoprism.example.com and put the following content in it. Keep in mind to change the domain (there a multiple entries!)

    # PhotoPrism Nginx config with SSL HTTP/2 and reverse proxy\n# This file gives you an example on how to secure you PP instance with SSL\nserver {\n    listen       80;\n    listen [::]:80; # HTTP IPv6\n    # enforce https\n    return 301 https://$server_name$request_uri;\n}\nserver {\n\n    listen 443 ssl http2; # Listen on port 443 and enable ssl and HTTP/2\n    listen [::]:443 ssl http2; # Same for IPv6\n\n    # Put your domain name in here.\n    server_name  photoprism.example.com;\n\n    # - - - - - - - - - -\n    # SSL security\n    # - - - - - - - - - -\n    ssl_certificate          /etc/letsencrypt/live/photoprism.example.com/fullchain.pem;\n    ssl_certificate_key      /etc/letsencrypt/live/photoprism.example.com/privkey.pem;\n\n    # Since the PP API is also used on Android, we have to keep TLS1.2 in here for a while.\n    # A lot of the older Android devices do not support TLS1.3 yet :/\n    ssl_protocols            TLSv1.2 TLSv1.3;\n\n    # Use good and strong ciphers, disable weak and old ciphers\n    ssl_ciphers              HIGH:!RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS;\n\n    # Enable HSTS (https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security)\n    add_header Strict-Transport-Security \"max-age=172800; includeSubdomains\";\n\n    # This checks if the certificate has been invalidated by the certificate authority\n    # You can remove this section if you use self-singed certificates...\n    # Enable OCSP stapling (http://blog.mozilla.org/security/2013/07/29/ocsp-stapling-in-firefox)\n    ssl_stapling on;\n    ssl_stapling_verify on;\n    ssl_trusted_certificate /etc/letsencrypt/live/photoprism.example.com/fullchain.pem;\n\n    # DNS Servers to use for OCSP lookups\n    resolver 8.8.8.8 1.1.1.1 9.9.9.9 valid=300s;\n    resolver_timeout 5s;\n\n    # - - - - - - - - -\n    # Reverse Proxy\n    # - - - - - - - - -\n    proxy_redirect           off;\n    proxy_set_header         X-Real-IP $remote_addr;                        # Let PP know the clients real IP\n    proxy_set_header         X-Forwarded-For $proxy_add_x_forwarded_for;    # Let PP know that a proxy did forward this request\n    proxy_set_header         Host $http_host;                               # Set Proxy host info\n\n    proxy_http_version 1.1;                                                 # Required for WebSocket connection\n    proxy_set_header Upgrade $http_upgrade;                                 # Allow protocol switch to websocket\n    proxy_set_header Connection \"upgrade\";                                  # Do protocol switch\n    proxy_set_header X-Forwarded-Proto $scheme;                             # Let PP know that this connection used HTTP or HTTPS\n\n    client_max_body_size 500M;                                              # Bump the max body size, you may want to upload huge stuff via the upload GUI\n    proxy_buffering off;                                                    # Do not hold back the request while the client sends data, give the stream directly to PP\n\n    location / {\n            # Optional; additional protection with Basic Auth.\n            # Note: This breaks WebDAV without additional configuration\n            #       You also have to create a .htpasswd file using the command:\n            #       \"htpasswd -c /etc/nginx/.pp_htpasswd my_secret_user\"\n            # - - -\n            # auth_basic           \"PhotoPrism Pre Auth\";\n            # auth_basic_user_file /etc/nginx/.pp_htpasswd;\n\n            # pipes the traffic to PhotoPrism\n            # Change this to your PhotoPrisms IP / DNS\n            proxy_pass http://docker.homenet:2342;\n    }\n}\n

    Have a look at the individual comments in the configuration for a further description.

    Tip

    Don't forget to change the PhotoPrism IP / DNS on the bottom of the config... ;)

    Once you've changed everything you need, let's restart nginx:

    sudo nginx -t\nsudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/\nsystemctl reload nginx\n
    Now, you should have access to PhotoPrism.

    Warning

    Make sure to setup proper firewalling on the machine that is running PhotoPrism. Have a look at ufw for simple firewall rules.

    "},{"location":"getting-started/advanced/openid-connect/","title":"Single Sign-On via OpenID Connect","text":"

    OpenID Connect (OIDC) allows users to log in and optionally register through an external identity provider instead of manually entering a username and password:

    "},{"location":"getting-started/advanced/openid-connect/#config-options","title":"Config Options","text":"Environment CLI Flag Default Description PHOTOPRISM_OIDC_URI --oidc-uri issuer URI for single sign-on via OpenID Connect, e.g. https://accounts.google.com PHOTOPRISM_OIDC_CLIENT --oidc-client client ID for single sign-on via OpenID Connect PHOTOPRISM_OIDC_SECRET --oidc-secret client SECRET for single sign-on via OpenID Connect PHOTOPRISM_OIDC_PROVIDER --oidc-provider custom identity provider NAME, e.g. Google PHOTOPRISM_OIDC_ICON --oidc-icon custom identity provider icon URI PHOTOPRISM_OIDC_REDIRECT --oidc-redirect automatically redirect unauthenticated users to the configured identity provider PHOTOPRISM_OIDC_REGISTER --oidc-register allow new users to create an account when they sign in with OpenID Connect PHOTOPRISM_OIDC_USERNAME --oidc-username preferred_username preferred username CLAIM for new OpenID Connect users (preferred_username, name, nickname, email) PHOTOPRISM_OIDC_WEBDAV --oidc-webdav allow new OpenID Connect users to use WebDAV when they have a role that allows it PHOTOPRISM_DISABLE_OIDC --disable-oidc disable single sign-on via OpenID Connect, even if an identity provider has been configured

    Your PhotoPrism instance and the OpenID Connect Identity Provider (IdP) must be accessible via HTTPS and have valid TLS certificates configured for it. Please also make sure that the hostname in the Redirect URL configured on the IdP matches the Site URL used by PhotoPrism. Single sign-on via OIDC can otherwise not be enabled.

    "},{"location":"getting-started/advanced/openid-connect/#identity-providers","title":"Identity Providers","text":"

    To authenticate users via OIDC, you can either set up and use a self-hosted identity provider such as ZITADEL or Keycloak, or configure a public identity provider service such as those operated by Google, Microsoft, GitHub, or Amazon.

    Single sign-on can be configured automatically if the identity provider offers a standardized /.well-known/openid-configuration endpoint for service discovery, for example:

    • https://accounts.google.com/.well-known/openid-configuration
    "},{"location":"getting-started/advanced/openid-connect/#issuer-uri","title":"Issuer URI","text":"

    The Issuer URI in your configuration must match the issuer value returned by the /.well-known/openid-configuration endpoint of your OpenID Connect Identity Provider (IdP), for example https://accounts.google.com if you use Google for authentication.

    You may not modify the URI in any way, e.g. by adding or omitting slashes at the end. If the values do not match, the validation will fail and users cannot be redirected to your provider's login page. For security reasons, only a generic error message is displayed in this case.

    "},{"location":"getting-started/advanced/openid-connect/#redirect-url","title":"Redirect URL","text":"

    The Redirect URL that must be specified when registering a new client with an Identity Provider is as follows, where {hostname} must be replaced by the hostname in the Site URL, e.g. configured via PHOTOPRISM_SITE_URL:

    https://{hostname}/api/v1/oidc/redirect\n

    Note that both the Site URL configured for your instance and the Redirect URL must start with https:// and that their hostnames must match, as the use of secure connections is a strict requirement for OpenID Connect.

    "},{"location":"getting-started/advanced/openid-connect/#preferred-username","title":"Preferred Username","text":"

    When a new user signs in with OpenID Connect1, their preferred username may already be registered. In this case, a random 6-digit number is appended to resolve the conflict.

    The config option PHOTOPRISM_OIDC_USERNAME allows you to change the preferred username for new accounts from preferred_username to name, nickname, or verified email. Names are changed to lowercase handles so that, for example, \"Jens Mander\" becomes \"jens.mander\".

    "},{"location":"getting-started/advanced/openid-connect/#existing-accounts","title":"Existing Accounts","text":"

    Super admins can manually connect existing user accounts2 under Settings > Users by changing the authentication to OIDC and then setting the Subject ID to match the account identifier from the configured Identity Provider:

    The Edit Account dialog may additionally contain a text field for the Issuer URL. It does not need to be entered manually as it is set automatically after the first login.

    Alternatively, you can run the following command in a terminal to allow authentication via OIDC and set a Subject ID to connect existing accounts:

    photoprism users mod --auth=oidc --auth-id=[sub] [username]\n

    Learn more \u203a

    "},{"location":"getting-started/advanced/openid-connect/#passwords","title":"Passwords","text":"

    Changing the authentication of an account to OIDC does not remove a previously set password, so that it can still be used to log in (optionally also in combination with 2FA).

    If a local password has been set for an account, you can remove it by running the following command in a terminal:

    photoprism passwd --rm [username]\n

    Super admins can alternatively set the account password to a long random value through the Admin Web UI or CLI to effectively prevent local authentication.

    Learn more \u203a

    "},{"location":"getting-started/advanced/openid-connect/#deleting-accounts","title":"Deleting Accounts","text":"

    Deleted accounts remain linked to the Subject ID, so logging in via OIDC is no longer possible and no new account can be registered for the same Subject ID either.

    If you wish to change the connected user account or create a new account instead, you must therefore change the authentication of the old account e.g. to None before deleting it:

    photoprism users mod --auth=none [username]\n

    To restore a previously deleted account, admins can follow the same steps as for creating a new account with the same username through the Admin Web UI or the photoprism users add command. You will then be asked if you want to restore the account.

    Learn more \u203a

    "},{"location":"getting-started/advanced/openid-connect/#frequently-asked-questions","title":"Frequently Asked Questions","text":""},{"location":"getting-started/advanced/openid-connect/#is-it-possible-to-set-a-default-role-for-new-oidc-users","title":"Is it possible to set a default role for new OIDC users?","text":"

    For security reasons, our Personal Editions currently default to the Guest role, which admins can then upgrade after checking the eligibility of newly registered accounts. If you run our Pro Edition in a trusted corporate network with appropriate security measures - including for the OIDC provider - it can be configured to give new accounts a higher authorization level by default.

    Please note in this context that using an external Identity Provider for authorization, and not just for authentication, can easily lead to security issues such as the following, for which we do not want to get a CVE assigned nor do we want to be responsible for any private pictures of our users getting leaked as a result:

    • https://www.microsoft.com/en-us/security/blog/2024/07/29/ransomware-operators-exploit-esxi-hypervisor-vulnerability-for-mass-encryption/
    • https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/24505
    "},{"location":"getting-started/advanced/openid-connect/#can-i-configure-a-custom-claim-for-the-preferred-username","title":"Can I configure a custom claim for the preferred username?","text":"

    You can choose between preferred_username, name, nickname and email, where preferred_username is the default. The other options are used as a fallback if no value is returned for the configured claim.

    Note that it is not possible to use a non-standard claim such as username, as this could lead to conflicts and potential security issues, e.g. if the value is not unique or not reliably set.

    Learn more \u203a

    1. PHOTOPRISM_OIDC_REGISTER must be set to \"true\" to allow new users to create an account\u00a0\u21a9

    2. Admins cannot change the authentication of their own user account through the Admin Web UI so that they do not accidentally lock themselves out e.g. by setting it to None.\u00a0\u21a9

    "},{"location":"getting-started/advanced/scalability/","title":"Deployment in High-Availability Environments and Horizontal Scaling in the Cloud","text":"

    Since we strive to make effective use of our resources, and most users run PhotoPrism on NAS devices at home or on small cloud server instances, our public documentation and development activities generally focus on these usage scenarios.

    So while our freely available Community Edition works well even with millions of files if your server meets the requirements (vertical scaling), it is important to note that the architecture and feature set would look different in some aspects \u2013 with other trade-offs \u2013 had the focus been on high availability and horizontal scaling in the cloud instead. For example, you might then split the backend into independently scalable microservices and not use a regular file system as storage for indexing.

    As an enterprise customer with specific scalability or availability requirements, you are welcome to contact us for a free consultation to determine the feasibility and implementation options.

    "},{"location":"getting-started/advanced/transcoding/","title":"Video Transcoding","text":""},{"location":"getting-started/advanced/transcoding/#avc-encoders","title":"AVC Encoders","text":"

    The encoder used by FFmpeg can be configured with PHOTOPRISM_FFMPEG_ENCODER in your compose.yaml or docker-compose.yml config file:

    Encoder Value Software H.264 software Apple Video Toolbox apple Intel Quick Sync intel NVIDIA H.264 nvidia Raspberry Pi / Video4Linux raspberry Video Acceleration API vaapi

    It defaults to software if no value is set or hardware transcoding fails. Please refer to the FFmpeg documentation for a full list of encoders and their implementation status. We welcome contributions to support additional encoders.

    For video transcoding to work, FFmpeg must be enabled and installed. When using our Docker images, it is already pre-installed. In addition, the service must have permission to use the related video devices. This depends on your hardware and operating system, so we can only give you examples that may need to be changed to work for you.

    "},{"location":"getting-started/advanced/transcoding/#size-limit","title":"Size Limit","text":"

    The PHOTOPRISM_FFMPEG_SIZE config option allows to limit the resolution of transcoded videos. It accepts the following standard sizes, while other values are automatically adjusted to the next supported size:

    Size Usage 720 SD TV, Mobile 1280 HD TV, SXGA 1920 Full HD 2048 DCI 2K, Tablets 2560 Quad HD, Notebooks 3840 4K Ultra HD 4096 DCI 4K, Retina 4K 7680 8K Ultra HD 2

    When transcoding videos, the original aspect ratio is maintained and smaller videos will not be upscaled.

    Note that MPEG-4 AVC videos are not re-encoded if they exceed the configured resolution limit.

    "},{"location":"getting-started/advanced/transcoding/#bitrate-limit","title":"Bitrate Limit","text":"

    You can limit the bitrate of the AVC encoder with the config option PHOTOPRISM_FFMPEG_BITRATE. Keep in mind that this is a \"soft limit\", so the actual bitrate varies and depends on the encoder used as well as the specific FFmpeg parameters, which in turn depend on the encoder. It may also depend on the operating system and the GPU drivers.

    If the bitrate is significantly exceeded in your environment and you want improvements to be implemented, we recommend that you take a look at the FFmpeg documentation and the parameters in our source code so you can tell us which parameters should be changed to make it work for you.

    Note that MPEG-4 AVC videos are not re-encoded if they exceed the configured bitrate limit. To reduce the size of AVC videos, you can manually replace the original files with a smaller version or wait for a future release that offers this functionality.

    Already transcoded video files are not automatically re-transcoded when the limit is changed. To do this, you must manually remove the *.avc files in the sidecar storage folder and run the photoprism convert command in a terminal.

    "},{"location":"getting-started/advanced/transcoding/#software-transcoding","title":"Software Transcoding","text":"

    Unless you have a lot of high-resolution videos in your library, we recommend keeping the default settings to use the standard software codec for video transcoding. It has a high quality and does not require any special permissions or additional drivers.

    Since FFmpeg 7 has significant performance optimizations compared to version 6.1.1 that ships with Ubuntu 24.04, users of our Docker image can choose to install the latest FFmpeg build available at johnvansickle.com/ffmpeg by adding PHOTOPRISM_INIT: \"ffmpeg\" to the environment section of their compose.yaml or docker-compose.yml file:

    services:\n  photoprism:\n    environment:\n      PHOTOPRISM_INIT: \"ffmpeg\"\n

    Note that this version cannot be used with hardware transcoding and that it may support a different set of file formats.

    "},{"location":"getting-started/advanced/transcoding/#gpu-drivers","title":"GPU Drivers","text":"

    Depending on your hardware, it may be necessary to install additional packages for FFmpeg to use the AVC encoding device.

    One way to do this automatically is to set PHOTOPRISM_INIT to \"gpu tensorflow\" when using our Docker images. Note that this is experimental and not required for most encoders.

    See the related installation script on GitHub for details. We welcome contributions to support additional devices or update package names if needed.

    Most users can either skip PHOTOPRISM_INIT completely or just use PHOTOPRISM_INIT: \"tensorflow\" to install a special version of TensorFlow that improves indexing performance if the server CPU supports AVX, which is independent of video transcoding and the type of GPU.

    "},{"location":"getting-started/advanced/transcoding/#intel-quick-sync","title":"Intel Quick Sync","text":"

    To enable Intel Quick Sync hardware video transcoding, add the intel target to PHOTOPRISM_INIT, choose the intel encoder, and share the /dev/dri devices with the photoprism service:

    services:\n  photoprism:\n    environment:\n      PHOTOPRISM_FFMPEG_ENCODER: \"intel\"\n      PHOTOPRISM_INIT: \"intel\"\n      ...\n    devices:\n      - \"/dev/dri:/dev/dri\"\n    volumes:\n      - ...\n

    In addition, you can choose to run the photoprism service as a non-root user by setting either the user service property or the PHOTOPRISM_UID and PHOTOPRISM_GID environment variables in your compose.yaml or docker-compose.yml file:

    Environment Default Description PHOTOPRISM_UID 0 run as a non-root user after initialization (supported: 0, 33, 50-99, 500-600, 900-1250, and 2000-2100) PHOTOPRISM_GID 0 run with a specific group id after initialization, can optionally be used together with PHOTOPRISM_UID (supported: 0, 33, 44, 50-99, 105, 109, 115, 116, 500-600, 900-1250, and 2000-2100)

    Which user and group you choose should depend on the owner of the /dev/dri video device so that the service has permission to access it.

    Finally, remember to update the file permissions and/or owner with the chmod and chown commands when you make changes to the UID or GID, and restart the services for your changes to take effect:

    docker compose stop\ndocker compose up -d\n

    Older Intel hardware may not support certain video codecs and resolutions. In this case, it is not possible to use hardware transcoding for these videos. We may later add a configuration option that allows you to downscale videos.

    "},{"location":"getting-started/advanced/transcoding/#nvidia-container-toolkit","title":"NVIDIA Container Toolkit","text":"

    For hardware transcoding with an NVIDIA graphics card, the NVIDIA Container Toolkit must be installed on the host computer first. Instructions can be found in their installation guide.

    Once the toolkit is installed, choose the nvidia encoder and add a deploy section to the photoprism service:

    services:\n  photoprism:    \n    environment:\n      PHOTOPRISM_FFMPEG_ENCODER: \"nvidia\"\n      PHOTOPRISM_INIT: \"tensorflow\"\n      NVIDIA_VISIBLE_DEVICES: \"all\"\n      NVIDIA_DRIVER_CAPABILITIES: \"compute,video,utility\"\n      ...\n    volumes:\n      - ...\n    deploy:\n      resources:\n        reservations:\n          devices:\n            - driver: \"nvidia\"\n              count: 1\n              capabilities: [gpu]\n    ...\n

    Now restart the services for your changes to take effect:

    docker compose stop\ndocker compose up -d\n

    We also provide a ready-to-use docker-compose.yml example for your convenience. Note that older hardware may not support certain video codecs and resolutions.

    "},{"location":"getting-started/advanced/transcoding/#raspberry-pi","title":"Raspberry Pi","text":"

    Experimental hardware-accelerated transcoding on a Raspberry Pi (and compatible devices) can be enabled by choosing the raspberry encoder:

    PHOTOPRISM_FFMPEG_ENCODER: \"raspberry\"\n

    The Docker container must also have access to one or more video devices. For the raspberry encoder, for example, you add:

    devices:\n - \"/dev/video11:/dev/video11\"\n

    Additional advanced configuration options are available to improve stability if needed:

    PHOTOPRISM_FFMPEG_BUFFERS: \"64\" # FFmpeg capture buffers (default: 32)\n

    Now restart the services for your changes to take effect:

    docker compose stop\ndocker compose up -d\n

    Some server configurations, especially Raspberry Pi's, may experience memory allocation issues when using hardware acceleration. Carefully monitor your server's logs and increase the available GPU and/or CMA memory allocations if necessary. Note that the Raspberry Pi hardware currently only supports video resolutions up to 2160p.

    "},{"location":"getting-started/advanced/transcoding/#other-hardware","title":"Other Hardware","text":"

    If you want to use other hardware for transcoding, choose the appropriate AVC encoder and share the required devices with the photoprism service, as shown in the examples above. Then restart the services for the changes to take effect.

    Which devices need to be shared and whether additional drivers are required depends on your specific hardware. For more information, see the FFmpeg documentation.

    "},{"location":"getting-started/advanced/transcoding/#troubleshooting","title":"Troubleshooting","text":""},{"location":"getting-started/advanced/transcoding/#enabling-trace-log-mode","title":"Enabling Trace Log Mode","text":"

    A good way to troubleshoot configuration issues is to increase the log level. To enable trace log mode, set PHOTOPRISM_LOG_LEVEL to \"trace\" in the environment: section of the photoprism service (or use the --trace flag when running the photoprism command directly):

    services:\n  photoprism:\n    environment:\n      PHOTOPRISM_LOG_LEVEL: \"trace\"\n      ...\n

    Then restart all services for your changes to take effect:

    docker compose stop\ndocker compose up -d\n
    "},{"location":"getting-started/advanced/transcoding/#viewing-docker-service-logs","title":"Viewing Docker Service Logs","text":"

    You can run this command to check the server logs for warnings and errors, including the last 100 messages (omit --tail=100 to see them all, and -f to output only the last logs without watching them):

    docker compose logs -f --tail=100 \n

    Learn more \u203a

    If FFmpeg is disabled or not installed, videos cannot be indexed because still images cannot be created. You should also have ExifTool enabled to extract metadata such as duration, resolution, and codec. Note that your hardware may not support certain video codecs and resolutions. In this case, the software encoder is used automatically.

    Our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"getting-started/advanced/migrations/","title":"Performing Index Migrations","text":"

    When using Docker Compose, you can prepend commands like docker compose exec [service] [command] to run them in a service container. Should this fail with no container found, make sure the service has been started, you have specified an existing service (usually photoprism) and you are in the folder where your compose.yaml or docker-compose.yml file is located.

    "},{"location":"getting-started/advanced/migrations/#show-migration-status","title":"Show Migration Status","text":"

    Run the photoprism migrations ls command in a terminal to see a list of all migrations and their current status:

    $ photoprism migrations ls\n\n|-----------------|---------|---------------------|---------------------|--------|\n|       ID        | Dialect |     Started At      |     Finished At     | Status |\n|-----------------|---------|---------------------|---------------------|--------|\n| 20211121-094727 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20211124-120008 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-030000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-040000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-050000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-060000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-061000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-070000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-071000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-080000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-081000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-083000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-090000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-091000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220329-093000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220421-200000 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220521-000001 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220521-000002 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n| 20220521-000003 | mysql   | 2022-07-12 08:07:35 | 2022-07-12 08:07:35 | OK     |\n|-----------------|---------|---------------------|---------------------|--------|\n
    "},{"location":"getting-started/advanced/migrations/#run-specific-migrations","title":"Run Specific Migrations","text":"

    To explicitly re-run specific migrations, you can pass them as arguments to the photoprism migrations run command:

    $ photoprism migrations run 20220521-000003\n\nINFO[2022-07-12T11:45:29Z] migrate: 20220521-000003 successful [12.967654ms] \nINFO[2022-07-12T11:45:29Z] migration completed in 40.89123ms            \nINFO[2022-07-12T11:45:29Z] closed database connection \n
    "},{"location":"getting-started/advanced/migrations/#retry-failed-migrations","title":"Retry Failed Migrations","text":"

    To automatically retry previously failed migrations, pass the -f flag to the photoprism migrations run command:

    $ photoprism migrations run -f\n
    "},{"location":"getting-started/advanced/migrations/mariadb-to-sqlite/","title":"Migrating from MariaDB to SQLite","text":"

    While we believe this contributed content may be helpful to advanced users, we have not yet thoroughly reviewed it. If you have suggestions for improvement, please let us know by clicking to submit a change request.

    • Install techouse/mysql-to-sqlite3 on your host.
    • Stop Photoprism: docker compose stop photoprism
    • Add the port to the MariaDB service
    • On the host now run sudo mysql2sqlite -f <PATH_TO_STORAGE_MOUNT>/storage/index.db -d photoprism -u root --mysql-password 'insecure'
    • Shutdown your current stack fully: docker compose down
    • Edit your compose.yaml or docker-compose.yml:
    • Remove the MariaDB service.
    • Change in the Photoprism settings to use the sqlite driver and remove the other database settings
    • Start your stack again with docker compose up -d
    • If this worked you may want to delete the old mountpoint for the MariaDB database.

    Bad Performance

    Many users reporting poor performance and high CPU usage have migrated, so their database schema is no longer optimized for performance, for example, because indexes are missing or columns have the wrong data type.

    If this is the case, please make sure that your migrated database schema matches that of a fresh, non-migrated installation. It may help to run the migrations manually in a terminal using the migrations subcommands. However, this does not guarantee that all issues such as missing indexes are resolved.

    "},{"location":"getting-started/advanced/migrations/sqlite-to-mariadb/","title":"Migrating from SQLite to MariaDB","text":"

    While we believe this contributed content may be helpful to advanced users, we have not yet thoroughly reviewed it. If you have suggestions for improvement, please let us know by clicking to submit a change request.

    • Install techouse/sqlite3-to-mysql on your host. (openSUSE: zypper in python-sqlite3-to-mysql)
    • Shutdown your current stack: docker compose down
    • Add the current snippet of the MariaDB to your Sqlite Photoprism docker compose with the addition of the extra ports section where you expose port 3306 to the Host. In my case this looked like attachment 1.
    • Start the stack again: docker compose up -d
    • Stop Photoprism: docker compose stop photoprism
    • On the host now run sudo sqlite3mysql -f <PATH_TO_STORAGE_MOUNT>/storage/index.db -d photoprism -u root -p and enter the MariaDB password when prompted (default is insecure).
    • Shutdown your current stack again: docker compose down
    • Edit your compose.yaml or docker-compose.yml so it uses the MariaDB database you added before. Don't forget to remove the ports section of the MariaDB Container.
    • Start your stack again with docker compose up -d
    • If this worked you may want to delete the file index.db in the storage mount since it contains out of date information.

    Attachment 1:

    services:\n  mariadb:\n    restart: unless-stopped\n    image: mariadb:11\n    ports:\n      - 3306:3306 # Expose Port 3306 \n    security_opt:\n      - seccomp:unconfined\n      - apparmor:unconfined\n    command: --innodb-buffer-pool-size=1G >\n      --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4\n      --collation-server=utf8mb4_unicode_ci --max-connections=512\n      --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120\n    volumes:\n      - \"./database:/var/lib/mysql\" # DO NOT REMOVE\n    environment:\n      MARIADB_AUTO_UPGRADE: \"1\"\n      MARIADB_INITDB_SKIP_TZINFO: \"1\"\n      MARIADB_DATABASE: \"photoprism\"\n      MARIADB_USER: \"photoprism\"\n      MARIADB_PASSWORD: \"insecure\"\n      MARIADB_ROOT_PASSWORD: \"insecure\"\n

    Bad Performance

    Many users reporting poor performance and high CPU usage have migrated from SQLite to MariaDB, so their database schema is no longer optimized for performance. For example, MariaDB cannot handle rows with text columns in memory and always uses temporary tables on disk if there are any.

    If this is the case, please make sure that your migrated database schema matches that of a fresh, non-migrated installation. It may help to run the migrations manually in a terminal using the migrations subcommands. However, this does not guarantee that all issues such as missing indexing are resolved.

    View Database Schema \u203a

    "},{"location":"getting-started/cloud/digitalocean/","title":"Using our DigitalOcean 1-Click App","text":"

    PhotoPrism can be deployed at DigitalOcean with just a few clicks. If you have no DigitalOcean account yet, you may use this sign-up link to receive a $100, 60-day account credit:

    Sign up at DigitalOcean

    "},{"location":"getting-started/cloud/digitalocean/#install-photoprism","title":"Install PhotoPrism","text":"
    • Sign Up or Log In at DigitalOcean
    • Open the PhotoPrism listing in the marketplace
    • Click Create PhotoPrism Droplet
    "},{"location":"getting-started/cloud/digitalocean/#configure-your-droplet","title":"Configure Your Droplet","text":""},{"location":"getting-started/cloud/digitalocean/#choose-an-image","title":"Choose an Image","text":"

    The PhotoPrism image will be pre-selected

    "},{"location":"getting-started/cloud/digitalocean/#choose-a-plan","title":"Choose a Plan","text":"

    We recommend hosting PhotoPrism on a server with at least 2 cores and 3 GB of physical memory. Indexing and searching can be slow on smaller Droplets, depending on how many and what types of files you upload.

    While PhotoPrism has been reported to work on Droplets with less memory, we take no responsibility for instability or performance problems. RAW image conversion and TensorFlow are disabled on Droplets with 1 GB or less memory.

    "},{"location":"getting-started/cloud/digitalocean/#choose-a-datacenter-region","title":"Choose a Datacenter Region","text":""},{"location":"getting-started/cloud/digitalocean/#choose-an-authentication-mode","title":"Choose an Authentication Mode","text":""},{"location":"getting-started/cloud/digitalocean/#finalize-your-droplet","title":"Finalize Your Droplet","text":"

    Finalize your droplet and click Create Droplet

    Your droplet is now being created.

    "},{"location":"getting-started/cloud/digitalocean/#admin-password","title":"Admin Password","text":"
    • Click More
    • Click Access console
    • Launch the console as root
    • Within the console type cat /root/.initial-password.txt and click enter
    • Copy your initial password
    "},{"location":"getting-started/cloud/digitalocean/#open-photoprism","title":"Open PhotoPrism","text":"
    • Click Get started
    • Click Quick access

    Info

    In case you have no domain and let's encrypt set up you will see the notice \"Your connection is not private\". Click Advanced and click Open page.

    • Use username \"admin\" and your initial password to sign in
    • You may change your password using the Web UI
    "},{"location":"getting-started/cloud/digitalocean/#first-steps","title":"First Steps","text":"
    1. Configure library and advanced settings according to your needs.
    2. Choose whether you want to index your originals so that the existing file and folder names are preserved, or import them so that they are automatically organized by year and month.
    3. To add new pictures, you can either copy them to the import or originals folder e.g. using WebDAV, or upload them with a browser, which will import them automatically after upload.
    4. Start indexing or importing
    5. Finally, set up automatic syncing from your mobile phone and install the Progressive Web App (PWA) on your desktop, tablet, and phone home screens as needed.
    "},{"location":"getting-started/cloud/pikapods/","title":"PikaPods Open Source App Hosting","text":"

    Trusted Partner

    PikaPods has partnered with us to offer you this officially supported, cloud-hosted solution.1 Your personal app instance is ready to use in just a few steps and includes member features like premium themes and high-resolution world maps - at no additional cost! New customers also receive a $5 welcome credit.

    "},{"location":"getting-started/cloud/pikapods/#setup","title":"Setup","text":"

    This step-by-step guide explains how to set up a new PhotoPrism instance at PikaPods.

    "},{"location":"getting-started/cloud/pikapods/#1-create-account","title":"1. Create Account","text":"

    Sign up at www.pikapods.com/register with your contact details. You will then receive a confirmation email with an activation link that you must click to continue.

    Before proceeding, we recommend that you enter your credit card information first to avoid usage restrictions.

    "},{"location":"getting-started/cloud/pikapods/#2-start-photoprism","title":"2. Start PhotoPrism","text":"

    If it isn't already selected, go to Available Apps and select PhotoPrism, then click Run Your Own:

    Continue by entering a Pod Name and selecting a Region:

    Click ENV VARS to specify the initial password for the \"admin\" user account:

    In RESOURCES, you can configure the storage space available for photos and videos, as well as the compute resources PhotoPrism can use, such as for indexing and face recognition.

    PhotoPrism currently requires at least 2 CPUs and 8 GB of memory. We are working to lower these minimum requirements.

    The approximate price per month is shown at the bottom:

    Finally, click Add Pod to complete the setup and start your instance.

    "},{"location":"getting-started/cloud/pikapods/#3-add-your-files","title":"3. Add Your Files","text":"

    PhotoPrism is now fully set up and ready to use. To log in, click Open Pod, enter the username \"admin\" and the password you have specified:

    If you want to change your password, you can do so in Settings > Account. Our First Steps \ud83d\udc63 tutorial will guide you through the user interface and settings to ensure your library is indexed according to your individual preferences.

    1. A share of the revenue helps fund the development of PhotoPrism \u21a9

    "},{"location":"getting-started/config-files/","title":"options.yml","text":"

    As an alternative to configuring your instance with environment variables, it may be more convenient to use an options.yml file located in your config path, for example if PhotoPrism was installed through an app store or with the installation packages we provide.

    You can specify a custom config path by adding the ConfigPath option to a \u21aa defaults.yml file in the /etc/photoprism directory (requires root privileges). It is also possible to use the command flag --config-path or the environment variable PHOTOPRISM_CONFIG_PATH for this. By default, it is a subdirectory of the storage path.

    If you use a third-party integration or package, you should find the exact location of the configuration files in the corresponding documentation.

    Note that changes to the options.yml file require a restart to take effect and that config values changed through the web interface will also be saved to this file. We therefore recommend that you only edit it manually while your instance is stopped.

    "},{"location":"getting-started/config-files/#file-format","title":"File Format","text":"

    You can use any text editor to create or modify YAML config files. When specifying values, make sure that their data type matches the documentation, e.g. bool values must be either true or false (without quotes, unlike in docker-compose.yml files) and int values must be whole numbers, as shown in this example:

    Debug: false\nAdminUser: \"admin\"\nAdminPassword: \"insecure\"\nDatabaseUser: \"photoprism\"\nDatabasePassword: \"insecure\"\nDatabaseName: \"photoprism\"\nDatabaseDriver: \"mysql\"\nDatabaseServer: \"localhost:3306\"\nHttpPort: 2342\nSiteCaption: \"AI-Powered Photos App\"\nSiteDescription: \"\"\nSiteAuthor: \"\"\nSiteUrl: \"http://localhost:2342/\"\n

    To avoid ambiguity, it is recommended to enclose text strings in \" (double quotes), especially if they contain spaces, a colon, or other special characters.

    File and directory paths may be specified using ~ as a placeholder for the home directory of the current user, e.g. ~/Pictures. Relative paths can also be specified via ./pathname. If no explicit originals, import and/or assets path has been configured, a list of default directory paths will be searched and the first existing directory will be used for the respective path.

    "},{"location":"getting-started/config-files/#global-defaults","title":"Global Defaults","text":"

    Global configuration defaults, including the storage paths to be used, can optionally be specified in a \u21aa defaults.yml file.

    "},{"location":"getting-started/config-files/#current-values","title":"Current Values","text":"

    Run photoprism --help in a terminal to get an overview of the command flags and environment variables available for configuration. Their current values can be displayed with the photoprism config command.

    Below are the corresponding names of the config options that you can use in the options.yml and defaults.yml files, grouped by purpose.

    "},{"location":"getting-started/config-files/#config-options","title":"Config Options","text":""},{"location":"getting-started/config-files/#authentication","title":"Authentication","text":"Name Type CLI Flag AuthMode string --auth-mode Public bool --public AdminUser string --admin-user AdminPassword string --admin-password PasswordLength int --password-length PasswordResetUri string --password-reset-uri OIDCUri string --oidc-uri OIDCClient string --oidc-client OIDCSecret string --oidc-secret OIDCScopes string --oidc-scopes OIDCProvider string --oidc-provider OIDCIcon string --oidc-icon OIDCRedirect bool --oidc-redirect OIDCRegister bool --oidc-register OIDCUsername string --oidc-username OIDCWebDAV bool --oidc-webdav DisableOIDC bool --disable-oidc SessionMaxAge int64 --session-maxage SessionTimeout int64 --session-timeout SessionCache int64 --session-cache"},{"location":"getting-started/config-files/#logging","title":"Logging","text":"Name Type CLI Flag LogLevel string --log-level Prod bool --prod Debug bool --debug Trace bool --trace"},{"location":"getting-started/config-files/#storage","title":"Storage","text":"Name Type CLI Flag ConfigPath string --config-path OriginalsPath string --originals-path OriginalsLimit int --originals-limit ResolutionLimit int --resolution-limit UsersPath string --users-path StoragePath string --storage-path ImportPath string --import-path ImportDest string --import-dest CachePath string --cache-path TempPath string --temp-path AssetsPath string --assets-path"},{"location":"getting-started/config-files/#sidecar-files","title":"Sidecar Files","text":"Name Type CLI Flag SidecarPath string --sidecar-path SidecarYaml bool --sidecar-yaml"},{"location":"getting-started/config-files/#backup","title":"Backup","text":"Name Type CLI Flag BackupPath string --backup-path BackupSchedule string --backup-schedule BackupRetain int --backup-retain BackupDatabase bool --backup-database BackupAlbums bool --backup-albums"},{"location":"getting-started/config-files/#indexing","title":"Indexing","text":"Name Type CLI Flag IndexWorkers int --index-workers IndexSchedule string --index-schedule WakeupInterval time.Duration --wakeup-interval AutoIndex int --auto-index AutoImport int --auto-import"},{"location":"getting-started/config-files/#feature-flags","title":"Feature Flags","text":"Name Type CLI Flag ReadOnly bool --read-only Experimental bool --experimental DisableSettings bool --disable-settings DisableBackups bool --disable-backups DisableRestart bool --disable-restart DisableWebDAV bool --disable-webdav DisablePlaces bool --disable-places DisableTensorFlow bool --disable-tensorflow DisableFaces bool --disable-faces DisableClassification bool --disable-classification DisableFFmpeg bool --disable-ffmpeg DisableExifTool bool --disable-exiftool DisableVips bool --disable-vips DisableSips bool --disable-sips DisableDarktable bool --disable-darktable DisableRawTherapee bool --disable-rawtherapee DisableImageMagick bool --disable-imagemagick DisableHeifConvert bool --disable-heifconvert DisableVectors bool --disable-vectors DisableJpegXL bool --disable-jpegxl DisableRaw bool --disable-raw RawPresets bool --raw-presets ExifBruteForce bool --exif-bruteforce DetectNSFW bool --detect-nsfw UploadNSFW bool --upload-nsfw DefaultLocale string --default-locale DefaultTimezone string --default-timezone"},{"location":"getting-started/config-files/#customization","title":"Customization","text":"Name Type CLI Flag DefaultTheme string --default-theme AppName string --app-name AppMode string --app-mode AppIcon string --app-icon AppColor string --app-color LegalInfo string --legal-info LegalUrl string --legal-url WallpaperUri string --wallpaper-uri"},{"location":"getting-started/config-files/#site-information","title":"Site Information","text":"Name Type CLI Flag SiteUrl string --site-url SiteAuthor string --site-author SiteTitle string --site-title SiteCaption string --site-caption SiteDescription string --site-description SitePreview string --site-preview CdnUrl string --cdn-url CdnVideo bool --cdn-video CORSOrigin string --cors-origin CORSHeaders string --cors-headers CORSMethods string --cors-methods"},{"location":"getting-started/config-files/#web-server","title":"Web Server","text":"Name Type CLI Flag HttpsProxy string --https-proxy HttpsProxyInsecure bool --https-proxy-insecure TrustedProxies []string --trusted-proxy ProxyProtoHeaders []string --proxy-proto-header ProxyProtoHttps []string --proxy-proto-https DisableTLS bool --disable-tls DefaultTLS bool --default-tls TLSEmail string --tls-email TLSCert string --tls-cert TLSKey string --tls-key HttpMode string --http-mode HttpCompression string --http-compression HttpCachePublic bool --http-cache-public HttpCacheMaxAge int --http-cache-maxage HttpVideoMaxAge int --http-video-maxage HttpHost string --http-host HttpPort int --http-port"},{"location":"getting-started/config-files/#database-connection","title":"Database Connection","text":"Name Type CLI Flag DatabaseDriver string --database-driver DatabaseDsn string --database-dsn DatabaseName string --database-name DatabaseServer string --database-server DatabaseUser string --database-user DatabasePassword string --database-password DatabaseTimeout int --database-timeout DatabaseConns int --database-conns DatabaseConnsIdle int --database-conns-idle"},{"location":"getting-started/config-files/#file-conversion","title":"File Conversion","text":"Name Type CLI Flag FFmpegBin string --ffmpeg-bin FFmpegEncoder string --ffmpeg-encoder FFmpegSize int --ffmpeg-size FFmpegBitrate int --ffmpeg-bitrate FFmpegMapVideo string --ffmpeg-map-video FFmpegMapAudio string --ffmpeg-map-audio ExifToolBin string --exiftool-bin SipsBin string --sips-bin SipsExclude string --sips-exclude DarktableBin string --darktable-bin DarktableCachePath string --darktable-cache-path DarktableConfigPath string --darktable-config-path DarktableExclude string --darktable-exclude RawTherapeeBin string --rawtherapee-bin RawTherapeeExclude string --rawtherapee-exclude ImageMagickBin string --imagemagick-bin ImageMagickExclude string --imagemagick-exclude HeifConvertBin string --heifconvert-bin RsvgConvertBin string --rsvgconvert-bin"},{"location":"getting-started/config-files/#security-tokens","title":"Security Tokens","text":"Name Type CLI Flag DownloadToken string --download-token PreviewToken string --preview-token"},{"location":"getting-started/config-files/#preview-images","title":"Preview Images","text":"Name Type CLI Flag ThumbLibrary string --thumb-library ThumbColor string --thumb-color ThumbFilter string --thumb-filter ThumbSize int --thumb-size ThumbSizeUncached int --thumb-size-uncached ThumbUncached bool --thumb-uncached"},{"location":"getting-started/config-files/#image-quality","title":"Image Quality","text":"Name Type CLI Flag JpegQuality int --jpeg-quality JpegSize int --jpeg-size PngSize int --png-size"},{"location":"getting-started/config-files/#daemon-mode","title":"Daemon Mode","text":"

    If you start the server as a daemon in the background, you can additionally specify a filename for the log and the process ID:

    Name Type CLI Flag PIDFilename string --pid-filename LogFilename string --log-filename DetachServer bool --detach-server"},{"location":"getting-started/config-files/defaults/","title":"defaults.yml","text":"

    Global config defaults, including the config and storage paths to be used, can be defined with a defaults.yml file in the /etc/photoprism directory (requires root privileges). It is possible to use the environment variable PHOTOPRISM_DEFAULTS_YAML or the command flag --defaults-yaml to load the defaults from another file instead.

    Keep in mind that any changes to the config options, either through the UI, config files, or by setting environment variables, always require a restart to take effect.

    A defaults.yml file affects all users and should only contain options for which you want to set a global default.

    "},{"location":"getting-started/config-files/defaults/#file-format","title":"File Format","text":"

    You can use any text editor to create or modify YAML config files. When specifying values, make sure that their data type matches the documentation, e.g. bool values must be either true or false (without quotes, unlike in docker-compose.yml files) and int values must be whole numbers, as shown in this example:

    ConfigPath: \"~/.config/photoprism\"\nStoragePath: \"~/.photoprism\"\nOriginalsPath: \"~/Pictures\"\nImportPath: \"/media\"\nAdminUser: \"admin\"\nAdminPassword: \"insecure\"\nAuthMode: \"password\"\nDatabaseDriver: \"sqlite\"\nHttpHost: \"127.0.0.1\"\nHttpPort: 2342\nHttpCompression: \"gzip\"\nDisableTLS: false\nDefaultTLS: true\nExperimental: false\nDisableWebDAV: false\nDisableSettings: false\nDisableTensorFlow: false\nDisableFaces: false\nDisableClassification: false\nDisableVectors: false\nDisableRaw: false\nRawPresets: false\nJpegQuality: 85\nDetectNSFW: false\nUploadNSFW: true\n

    To avoid ambiguity, it is recommended to enclose text strings in \" (double quotes), especially if they contain spaces, a colon, or other special characters.

    File and directory paths may be specified using ~ as a placeholder for the home directory of the current user, e.g. ~/Pictures. Relative paths can also be specified via ./pathname. If no explicit originals, import and/or assets path has been configured, a list of default directory paths will be searched and the first existing directory will be used for the respective path.

    "},{"location":"getting-started/config-files/defaults/#supported-options","title":"Supported Options","text":"

    \u21aa see options.yml

    "},{"location":"getting-started/config-files/defaults/#overriding-defaults","title":"Overriding Defaults","text":"

    Defaults can be overridden by values in an options.yml file as well as by command flags and environment variables.

    To override values with an options.yml file, you can specify its path (without the file name) by adding the ConfigPath option to your defaults.yml. Alternatively, you can use the command flag --config-path or the environment variable PHOTOPRISM_CONFIG_PATH. By default, it is located in the config subdirectory of the storage path.

    The values in an options.yml file are not global by default and can be used to customize individual instances. In both files you can set any of the supported options.

    "},{"location":"getting-started/config-files/settings/","title":"settings.yml","text":"

    User interface, download, and indexing preferences are stored in a settings.yml file, located in the config path. If it does not exist yet, it will be created automatically.

    Experienced users may edit this file directly to change certain settings, as not all of them can be changed through the user interface.

    Note that changes to the settings.yml file require a restart to take effect and that settings changed through the web interface will also be saved to this file. We therefore recommend that you only edit it manually while your instance is stopped.

    "},{"location":"getting-started/config-files/settings/#file-format","title":"File Format","text":"

    You can use any text editor to create or modify YAML config files. However, it is important that related values, e.g. key-value pairs, start at the same indentation level and that spaces are used for indentation, for example:

    Index:\n  Path: \"/\"\n  Rescan: false\n

    To avoid ambiguity, it is recommended to enclose text strings in \" (double quotes), especially if they contain spaces, a colon, or other special characters.

    "},{"location":"getting-started/config-files/settings/#global-defaults","title":"Global Defaults","text":"

    User settings can optionally be initialized from a settings.yml file located in the same folder as the \u21aa defaults.yml file, e.g. /etc/photoprism.

    "},{"location":"getting-started/config-files/settings/#sections","title":"Sections","text":""},{"location":"getting-started/config-files/settings/#user-interface","title":"User Interface","text":"
    UI:\n  Scrollbar: true\n  Zoom: false\n  Theme: default\n  Language: en\n

    If you set Scrollbar to false, the browser scrollbar will be hidden regardless of which device you use and which page you are on. This is generally not recommended, but can be useful e.g. when taking screenshots.

    Setting Zoom to true allows you to enlarge the user interface with gestures on mobile devices, making the app feel more like a regular web page. This can be useful for visually impaired users so that they can magnify text and images when needed.

    Theme and Language change the theme and language of the user interface and correspond to the settings dropdowns you find when navigating to Settings > General.

    "},{"location":"getting-started/config-files/settings/#file-downloads","title":"File Downloads","text":"
    Download:\n  Name: file\n  Disabled: false\n  Originals: true\n  MediaRaw: false\n  MediaSidecar: false\n

    The Name setting determines which file names are used when downloading pictures from search results or in the photo viewer. Currently, the following options are supported:

    • file uses the actual file name in the originals folder
    • original uses the original file name if the file was imported
    • share uses a share-friendly file name based on the title and creation date

    Note that your choice will not affect the file names in ZIP archives when you download complete albums, as they always use share-friendly names. However, we may add settings for this in a future release.

    In case Originals is set to true, only the files in the originals folder will be downloaded, but not any files that were automatically created in the sidecar folder. This is the recommended default.

    If you set MediaRaw to true, RAW image files are downloaded automatically, for example when you click the download button in the photo viewer.

    Setting MediaSidecar to true will also download sidecar files as used for XMP metadata. This is generally not recommended except for some professional workflows.

    "},{"location":"getting-started/config-files/settings/#media-library","title":"Media Library","text":"
    Import:\n  Path: /\n  Move: false\nIndex:\n  Path: /\n  Convert: true\n  Rescan: false\n  SkipArchived: false\nStack:\n  UUID: true\n  Meta: true\n  Name: false\n

    These settings affect which files are stacked, as well as your preferences when indexing or importing files.

    You can also change these settings by navigating to Settings > Library and in the Library UI, so it is generally not necessary to edit them directly in the config file.

    "},{"location":"getting-started/nas/asustor/","title":"Running PhotoPrism on an Asustor NAS","text":"

    Before setting up PhotoPrism on your NAS, we recommend that you check the Asustor product database for the CPU and memory configuration of your device.

    For a good user experience, it should be a 64-bit system with at least 2 cores and 3 GB of RAM. Indexing large photo and video collections also benefits greatly from using SSD storage, especially for the database and cache files.

    Third-party integrations may not provide direct access to config files or the command line, so you might not be able to use all features and config options. Also note that RAW image conversion and TensorFlow are disabled on devices with 1 GB or less memory, and that high-resolution panoramic images may require additional swap space and/or physical memory above the recommended minimum. We take no responsibility for instability or performance problems if your device does not meet the requirements.

    "},{"location":"getting-started/nas/asustor/#setup","title":"Setup","text":"

    This step-by-step guide explains how to set up a new PhotoPrism instance through App Central, the built-in app store.

    "},{"location":"getting-started/nas/asustor/#step-1-open-app-central","title":"Step 1: Open App Central","text":"

    Log in to the user interface of your NAS. You can do this by navigating to https://asustor:7001 if you replace asustor with the actual IP address or hostname of your device and change the port depending on your configuration.

    Now open \"App Central\" on the home screen:

    "},{"location":"getting-started/nas/asustor/#step-2-install-photoprism","title":"Step 2: Install PhotoPrism","text":"

    Type PhotoPrism in the search box in the upper right corner and press Enter to start the search. PhotoPrism should then be displayed so you can click \"Install\" to start the installation:

    Next, you will be informed about dependencies like Docker that need to be installed, and you can decide whether you want your instance to be accessible from the Internet (if you have set this up for your NAS and your Internet router is compatible):

    If you want to uninstall PhotoPrism later, you can also do that in App Central.

    "},{"location":"getting-started/nas/asustor/#step-3-open-photoprism","title":"Step 3: Open PhotoPrism","text":"

    Once the installation is complete, you will find PhotoPrism on your home screen, where you can open it in a new tab with one click:

    You can also navigate to port 32770 on your device if you want to open PhotoPrism directly. When you see the login screen, enter the username \"admin\" and password \"admin321\" to sign in:

    Remember to change your password after the first login. You can do this in Settings > Account.

    "},{"location":"getting-started/nas/asustor/#step-4-add-your-files","title":"Step 4: Add Your Files","text":"

    Our First Steps \ud83d\udc63 tutorial guides you through the user interface and settings to ensure your library is indexed according to your individual preferences.

    Depending on which strategy you choose, you can add your media files to the originals or import folder located in /volume1/Docker/PhotoPrism/data:

    The storage folder, which contains configuration, cache and sidecar files, can also be found there.

    Note that the folders that PhotoPrism uses cannot be dynamically configured at the moment when using this app store version. However, we are working to make this possible.

    "},{"location":"getting-started/nas/openmediavault/","title":"Running PhotoPrism on OpenMediaVault","text":"

    Should you experience problems with the installation, we recommend that you ask the OpenMediaVault community for advice, as we cannot provide support for third-party software and services. Also note that third-party integrations may not provide direct access to config files or the command line, so you might not be able to use all features and config options.

    PhotoPrism can be conveniently installed using the OpenMediaVault plugin.

    "},{"location":"getting-started/nas/openmediavault/#getting-updates","title":"Getting Updates","text":"

    To upgrade your instance, open a terminal, download our newest image from Docker Hub, and then restart the service:

    podman pull docker.io/photoprism/photoprism:latest\nsystemctl restart pod-photoprism.service\n

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"getting-started/nas/qnap/","title":"Setting Up PhotoPrism on QNAP","text":"

    Before setting up PhotoPrism on your NAS, we recommend that you check the QNAP product database for the CPU and memory configuration of your device.

    For a good user experience, it should be a 64-bit system with at least 2 cores and 3 GB of RAM. Indexing large photo and video collections also benefits greatly from using SSD storage, especially for the database and cache files.

    Should you experience problems with the installation, we recommend that you ask the QNAP community for advice, as we cannot provide support for third-party software and services. Also note that RAW image conversion and TensorFlow are disabled on devices with 1 GB or less memory, and that high-resolution panoramic images may require additional swap space and/or physical memory above the recommended minimum.

    "},{"location":"getting-started/nas/qnap/#setup","title":"Setup","text":"

    You can follow this tutorial to install PhotoPrism on QNAP:

    \u21aa https://safjan.com/install-photoprism-on-qnap-nas-using-docker-compose/

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"getting-started/nas/synology/","title":"Running PhotoPrism on a Synology NAS","text":"

    Before setting up PhotoPrism on your NAS, we recommend that you check the Synology Knowledge Base for the CPU and memory configuration of your device.

    For a good user experience, it should be a 64-bit system with at least 2 cores and 3 GB of RAM. Indexing large photo and video collections also benefits greatly from using SSD storage, especially for the database and cache files.

    Should you experience problems with the installation, we recommend that you ask the Synology community for advice, as we cannot provide support for third-party software and services. Also note that RAW image conversion and TensorFlow are disabled on devices with 1 GB or less memory, and that high-resolution panoramic images may require additional swap space and/or physical memory above the recommended minimum.

    "},{"location":"getting-started/nas/synology/#will-my-device-be-fast-enough","title":"Will my device be fast enough?","text":"

    This largely depends on your expectations and the number of files you have. Most users report that PhotoPrism runs well on their Synology NAS. However, you should keep in mind:

    • initial indexing may take longer than on standard desktop computers
    • the hardware has no video transcoding support and software transcoding is generally slow
    "},{"location":"getting-started/nas/synology/#setup","title":"Setup","text":""},{"location":"getting-started/nas/synology/#setup-using-portainer","title":"Setup using Portainer","text":"

    A step-by-step guide to install PhotoPrism with Portainer can be found here.

    "},{"location":"getting-started/nas/synology/#setup-using-synology-container-manager","title":"Setup using Synology Container Manager","text":"

    To install PhotoPrism with the Synology Container Manager, we recommend following this beginner-friendly tutorial on LinuxLinks.

    "},{"location":"getting-started/nas/synology/#first-steps","title":"First Steps","text":"

    Our First Steps \ud83d\udc63 tutorial guides you through the user interface and settings to ensure your library is indexed according to your individual preferences.

    "},{"location":"getting-started/nas/synology/#troubleshooting","title":"Troubleshooting","text":"

    If your device runs out of memory, the index is frequently locked, or other system resources are running low:

    • Try reducing the number of workers by setting PHOTOPRISM_WORKERS to a reasonably small value in docker-compose.yml, depending on the performance of your device
    • Make sure your device has at least 4 GB of swap space so that indexing doesn't cause restarts when memory usage spikes; RAW image conversion and video transcoding are especially demanding
    • If you are using SQLite, switch to MariaDB, which is better optimized for high concurrency
    • As a last measure, you can disable the use of TensorFlow for image classification and facial recognition

    Other issues? Our troubleshooting checklists help you quickly diagnose and solve them.

    "},{"location":"getting-started/nas/unraid/","title":"Setting Up PhotoPrism on Unraid","text":"

    Should you experience problems with the installation, we recommend that you ask the Unraid community for advice, as we cannot provide support for third-party software and services. Also note that third-party integrations may not provide direct access to config files or the command line, so you might not be able to use all features and config options.

    SQLite is not a good choice for users who require scalability and high performance. If it is used in a configuration template, we recommend changing it to use MariaDB instead.

    We recommend you follow this tutorial provided by IBRACORP to install PhotoPrism on Unraid:

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"getting-started/portainer/","title":"Portainer Setup Guide","text":"

    Portainer can be used to manage Docker containers through a web interface. On many NAS devices, it either comes pre-installed or you can simply install it from the vendor's app store. If you are installing PhotoPrism on a regular home or cloud server, you may instead want to follow our Docker Compose Setup Guide, which only uses standard Docker tools and commands.

    "},{"location":"getting-started/portainer/#step-1-create-stack","title":"Step 1: Create Stack","text":"

    Navigate to \"Stacks\", click \"Add stack\" and paste the contents of our stack.yml config template (opens in a new tab) into the Web editor so that you can change the storage folder locations in the volumes sections as needed:

    When using the Web editor, please make sure that related values remain on the same indentation level and that lists start with a dash as shown in our template.

    "},{"location":"getting-started/portainer/#volumes","title":"Volumes","text":"

    You need to explicitly specify the directories you want to use on your NAS device, since PhotoPrism can't see files in folders that have not been shared. This is an important security feature and allows for a flexible configuration without having to change any other variables.

    It is important that all folders specified in the \"volumes\" sections are located on a persistent volume on your device. We recommend changing the relative paths used in our example to absolute paths in order to avoid potential data loss, e.g. if the default application folder managed by Portainer changes or is reset after an update.

    The volume mount paths to configure depend on your NAS device and its settings. As on most operating systems, a dot followed by a slash ./ can be used to specify a path relative to the current directory. If you keep the defaults, all files will be located in the internal application folder that Portainer automatically creates when you add a new stack.

    "},{"location":"getting-started/portainer/#database","title":"Database","text":"

    Our stack template includes a pre-configured MariaDB database server that stores its data in the Portainer application folder by default:

    services:\n  mariadb:\n    volumes:\n      - \"./database:/var/lib/mysql\"\n

    If your NAS device has a mixed drive configuration with solid-state drives (SSDs) and traditional hard disks, we recommend that you change ./database to an absolute path located on an SSD as this significantly improves performance, for example:

          - \"/mnt/ssd/database:/var/lib/mysql\"\n

    Database files should never be located on an unreliable device such as a USB flash drive, SD card, or network folder.

    "},{"location":"getting-started/portainer/#photoprismoriginals","title":"/photoprism/originals","text":"

    The originals folder contains your original photo and video files:

    services:\n  photoprism:\n    volumes:\n      - \"./originals:/photoprism/originals\"\n

    We recommend that you change ./originals to the directory on your NAS where your existing media files are, for example:

          - \"/mnt/photos:/photoprism/originals\"\n

    Additional directories can be mounted as sub folders of /photoprism/originals (depending on overlay filesystem support):

        volumes:\n      - \"/mnt/photos:/photoprism/originals\"\n      - \"/mnt/videos:/photoprism/originals/videos\"\n

    If you want to start with an empty library, you can mount any directory that has enough free space for your needs.

    "},{"location":"getting-started/portainer/#photoprismstorage","title":"/photoprism/storage","text":"

    The storage folder is used to save config, cache, backup, thumbnail, and sidecar files. It must always be specified so that you do not lose these files after a restart or upgrade. If available, we recommend that you put the storage folder on a local SSD drive for best performance. You can otherwise keep the default to store the files in the internal application folder:

    services:\n  photoprism:\n    volumes:\n      - \"./storage:/photoprism/storage\"\n

    Never configure the storage folder to be inside the originals folder unless the name starts with a . to indicate that it is hidden. Should you later want to move your instance to another NAS, the easiest and most time-saving way is to copy the entire storage folder along with your originals and database.

    "},{"location":"getting-started/portainer/#photoprismimport","title":"/photoprism/import","text":"

    You can optionally mount an import folder from which files can be transferred to the originals folder in a structured way that avoids duplicates, for example:

    services:\n  photoprism:\n    volumes:\n      - \"/mnt/media/usb:/photoprism/import\"\n

    Imported files receive a canonical filename and will be organized by year and month. You should never configure the import folder to be inside the originals folder, as this will cause a loop by importing already indexed files.

    Even if you don't specify an import folder, adding files via Web Upload and WebDAV remains possible unless read-only mode is enabled or the features have been disabled.

    "},{"location":"getting-started/portainer/#step-2-finalize-setup","title":"Step 2: Finalize Setup","text":"

    To complete the setup, download the stack.env file from our server (right click and Save Link As...), click \"Load variables from .env file\", upload it to Portainer, and then change the values according to your needs:

    Always change PHOTOPRISM_ADMIN_PASSWORD so that the app starts with a secure initial password. Never use easy-to-guess passwords or default values like insecure on publicly accessible instances. There is no default in case no password was provided. A minimum length of 8 characters is required.

    You cannot change the database password with MARIADB_PASSWORD after MariaDB has been started for the first time. However, choosing a secure password is not essential if you do not share the database with other applications or expose it over a network. To enable automatic schema updates when upgrading to a new major version, please make sure that MARIADB_AUTO_UPGRADE is set to a non-empty value.

    When you're done, scroll down and click \"Deploy the stack\" without changing any of the other options:

    After waiting a few moments, you should be able to log in as admin with the password specified in PHOTOPRISM_ADMIN_PASSWORD when you navigate to http://localhost:2342/.

    If you have modified the server hostname, port, or protocol in your configuration, the URL to use changes accordingly.

    "},{"location":"getting-started/portainer/#step-3-index-your-library","title":"Step 3: Index Your Library","text":"

    Our First Steps \ud83d\udc63 tutorial guides you through the user interface and settings to ensure your library is indexed according to your individual preferences.

    The config options and container image you want to use can be changed at any time by navigating to \"Stacks\", selecting your existing PhotoPrism stack, clicking \"Editor\", updating the configuration to your needs, and then clicking \"Update the stack\" to apply the changes.

    "},{"location":"getting-started/portainer/#photoprism-plus","title":"PhotoPrism\u00ae Plus","text":"

    Our members can activate additional features by logging in with the admin user created during setup and then following the steps described in our activation guide. Thank you for your support, which has been and continues to be essential to the success of the project!

    Compare Memberships \u203a View Membership FAQ \u203a

    We recommend that new users install our free Community Edition before signing up for a membership.

    "},{"location":"getting-started/portainer/#troubleshooting","title":"Troubleshooting","text":"

    If your device runs out of memory, the index is frequently locked, or other system resources are running low:

    • Try reducing the number of workers by setting PHOTOPRISM_WORKERS to a reasonably small value, depending on the CPU performance and number of cores
    • Make sure your device has at least 4 GB of swap space so that indexing doesn't cause restarts when memory usage spikes; RAW image conversion and video transcoding are especially demanding
    • If you are using SQLite, switch to MariaDB, which is better optimized for high concurrency
    • As a last measure, you can disable the use of TensorFlow for image classification and facial recognition

    Other issues? Our troubleshooting checklists help you quickly diagnose and solve them.

    You are welcome to ask for help in our community chat. Sponsors receive direct technical support via email. Before submitting a support request, try to determine the cause of your problem.

    "},{"location":"getting-started/portainer/#command-line-interface","title":"Command-Line Interface","text":""},{"location":"getting-started/portainer/#opening-a-terminal","title":"Opening a Terminal","text":"

    Navigate to \"Stacks\", select the PhotoPrism stack and scroll down to the list of containers:

    Now click the button belonging to the photoprism-photoprism-1 container and accept the default settings to open a terminal:

    Running photoprism help lists all commands and options available in the current version:

    photoprism help\n

    Use the --help flag to see a detailed command description, for example:

    photoprism backup --help\n

    The command-line interface is also well suited for job automation using a scheduler.

    "},{"location":"getting-started/portainer/#examples","title":"Examples","text":"Action Command Display Config Values photoprism show config Show Migration Status photoprism migrations ls Repeat Failed Migrations photoprism migrations run -f Reset Database photoprism reset --yes Backup Database photoprism backup -a -i Restore Database photoprism restore -a -i Change Password photoprism passwd [username] Show User Management Commands photoprism users help Reset Users photoprism users reset --yes Show Face Recognition Commands photoprism faces help Index Faces photoprism faces index Reset People & Faces photoprism faces reset -f Transcode Videos to AVC photoprism convert Regenerate Thumbnails photoprism thumbs -f Update Index photoprism index --cleanup Move to Originals photoprism import [path] Copy to Originals photoprism cp [path]"},{"location":"getting-started/ports/freebsd/","title":"Running PhotoPrism On FreeBSD","text":"

    Please note that third-party apps may not provide access to the compose.yaml or docker-compose.yml file or the command line, and therefore you may not be able to use all of PhotoPrism's features and config options.

    Should you experience problems with the installation, we recommend that you ask the FreeBSD community for advice, as we cannot provide support for third-party software and services. You can contribute by clicking to send a pull request with your changes.

    For FreeBSD and TrueNAS CORE (formerly FreeNAS) users, an unofficial port is available that builds PhotoPrism from source. It will also compile and install the required TensorFlow libraries for you.

    1. Clone or download the port:

    git clone https://github.com/huo-ju/photoprism-freebsd-port\n

    2. Build TensorFlow and PhotoPrism from source, then install:

    cd photoprism-freebsd-port\nmake config\nmake && make install\n

    When running the make config command, a CPU feature options dialog will be presented, and the default option is NONE.

    3. Add entries to rc.conf:

    photoprism_enable=\"YES\"\nphotoprism_assetspath=\"/var/photoprism/assets\"\nphotoprism_storagepath=\"/var/photoprism/storage\"\n

    You can add more command line parameters into photoprism_flags=\"\" in the rc.conf

    photoprism config shows all config parameters.

    4. Start the service:

    service photoprism start\n

    Done!

    "},{"location":"getting-started/ports/freebsd/#when-should-i-perform-a-complete-rescan","title":"When should I perform a complete rescan?","text":"

    We recommend performing a complete rescan after major updates to take advantage of new search filters and sorting options. Be sure to read the notes for each release to see what changes have been made and if they might affect your library, for example, because of the file types you have or because new search features have been added. If you encounter problems that you cannot solve otherwise (i.e. before reporting a bug), please also try a rescan and see if it solves the problem.

    You can start a rescan from the user interface by navigating to Library > Index, selecting \"Complete Rescan\", and then clicking \"Start\".

    Manually entered information such as labels, people, titles or descriptions will not be modified when indexing, even if you perform a \"complete rescan\". Be careful not to start multiple indexing processes at the same time, as this will lead to a high server load.

    "},{"location":"getting-started/proxies/apache-2/","title":"Using Apache 2.4 as Reverse Proxy","text":"

    Should you experience problems with Apache, we recommend that you ask the Apache community for advice, as we cannot provide support for third-party software and services.

    Example

    ProxyPass /api/v1/ws ws://photoprism.lxd:2342/api/v1/ws\nProxyPassReverse /api/v1/ws ws://photoprism.lxd:2342/api/v1/ws\nProxyPass / http://photoprism:2342/\nProxyPassReverse / http://photoprism:2342/\nProxyRequests off\n

    The official documentation explains in detail, how to configure Apache Web Server 2.4 to reverse proxy WebSockets.

    "},{"location":"getting-started/proxies/apache-2/#why-use-a-proxy","title":"Why Use a Proxy?","text":"

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure HTTPS reverse proxy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted by anyone, including your provider, hackers, and governments. Backup tools and file sync apps may refuse to connect as well.

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"getting-started/proxies/caddy-1/","title":"Using Caddy 1 as Reverse Proxy","text":"

    Should you experience problems with Caddy, we recommend that you ask the Caddy community for advice, as we cannot provide support for third-party software and services.

    For PhotoPrism to work properly, you need to enable websockets and transparent proxying:

    Example

    example.com {\n    proxy / photoprism:2342 {\n        websocket\n        transparent\n    }\n}\n

    Please refer to the official documentation for further details.

    "},{"location":"getting-started/proxies/caddy-1/#why-use-a-proxy","title":"Why Use a Proxy?","text":"

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure HTTPS reverse proxy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted by anyone, including your provider, hackers, and governments. Backup tools and file sync apps may refuse to connect as well.

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"getting-started/proxies/caddy-2/","title":"Using Caddy 2 as Reverse Proxy","text":"

    Should you experience problems with Caddy, we recommend that you ask the Caddy community for advice, as we cannot provide support for third-party software and services.

    WebSocket proxying automatically works in Caddy 2. There is no need to enable this as necessary for Caddy 1, Apache, and NGINX. In addition, Caddy 2 may automatically create and update Let's Encrypt HTTPS certificates.

    Example

    example.com {\n    reverse_proxy photoprism:2342\n}\n

    Please refer to the official documentation for further details.

    "},{"location":"getting-started/proxies/caddy-2/#why-use-a-proxy","title":"Why Use a Proxy?","text":"

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure HTTPS reverse proxy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted by anyone, including your provider, hackers, and governments. Backup tools and file sync apps may refuse to connect as well.

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"getting-started/proxies/haproxy/","title":"Using HAPROXY as Reverse Proxy","text":"

    Should you experience problems with Haproxy, we recommend that you ask the Haproxy community for advice, as we cannot provide support for third-party software and services.

    defaults\n    #Defaults used in frontend and backends\n    #Defined here to avoid repitition\n    #Can be overwritten in frontends and/or backends\n    log global\n    option logasap\n    mode http\n    timeout connect 30000ms\n    timeout client 30000ms\n    timeout server 30000ms\n    timeout tunnel 120000ms\n    timeout queue 5000ms\n\n##########################################################\n\n#Frontend config\nfrontend fe-photoprism\n    #'photo' is the name of the subdomain\n    #TLS certs should be referenced here, maybe created by dehydrated, certbot, ...\n    bind *:443 ssl crt /etc/ssl/localcerts/wildcard.example.com.pem\n\n    #SNI-Detection\n    #Can be removed, if not needed\n    acl sni_photo hdr(host) -i photo.example.com\n    #Use Backend if domain (acl is set) detected\n    use_backend be-photoprism if sni_photo\n\n    #Every unflagged request goes here, may target to another backend as well\n    default_backend be-photoprism\n\n##########################################################\n\n#Backend config\n#be-photoprism is the name of the backend referenced in frontend\nbackend be-photoprism\n    retries 3\n    option forwardfor\n    no option httpclose\n\n    #Local PhotoPrism-Instance\n    server photo 127.0.0.1:2342\n
    "},{"location":"getting-started/proxies/haproxy/#why-use-a-proxy","title":"Why Use a Proxy?","text":"

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure HTTPS reverse proxy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted by anyone, including your provider, hackers, and governments. Backup tools and file sync apps may refuse to connect as well.

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"getting-started/proxies/nginx/","title":"Using NGINX as Reverse Proxy","text":"

    Getting Support

    Since NGINX is notoriously difficult to configure, we are unable to provide technical support for NGINX-related issues such as failed uploads, connection errors, broken thumbnails, and video playback problems. If you cannot resolve these on your own, we recommend that you ask their community for advice or use Traefik instead, which is easier to configure and more convenient to handle overall.

    This tutorial explains, how to configure NGINX WebSocket connections between your client and backend services.

    Example

    http {\n  server {\n    listen 80 ssl;\n    listen [::]:80 ssl;\n    server_name example.com;\n    client_max_body_size 500M;\n\n    # With SSL via Let's Encrypt\n    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot\n    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot\n    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot\n    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot\n\n    location / {\n      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n      proxy_set_header Host $host;\n\n      proxy_pass http://photoprism:2342;\n\n      proxy_buffering off;\n      proxy_http_version 1.1;\n      proxy_set_header Upgrade $http_upgrade;\n      proxy_set_header Connection \"upgrade\";\n\n      client_max_body_size 500M;\n    }\n  }\n}\n

    At the very least you will need to adapt server_name and the ssl_certificate/ssl_certificate_key paths to match your setup. Please refer to their official documentation for further details.

    View \"Pitfalls and Common Mistakes\" \u203a

    "},{"location":"getting-started/proxies/nginx/#why-use-a-proxy","title":"Why Use a Proxy?","text":"

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure HTTPS reverse proxy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted by anyone, including your provider, hackers, and governments. Backup tools and file sync apps may refuse to connect as well.

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"getting-started/proxies/swag/","title":"Using SWAG as Reverse Proxy","text":"

    Should you experience problems with Swag, we recommend that you ask the Swag community for advice, as we cannot provide support for third-party software and services.

    To simplify the setup of a reverse HTTPS proxy, Linuxserver.io developed SWAG.

    SWAG - Secure Web Application Gateway (formerly known as letsencrypt, no relation to Let's Encrypt\u2122) sets up a Nginx web server and reverse proxy with PHP support and a built-in certbot client that automates free SSL server certificate generation and renewal processes (Let's Encrypt and ZeroSSL). It also contains fail2ban for intrusion prevention.

    "},{"location":"getting-started/proxies/swag/#setup","title":"Setup","text":""},{"location":"getting-started/proxies/swag/#step-1-get-a-domain","title":"Step 1: Get a domain","text":"

    The first step is to grab a dynamic DNS if you don't have your own subdomain already. You can get this from for example DuckDNS.

    "},{"location":"getting-started/proxies/swag/#step-2-set-up-swag","title":"Step 2: Set-up SWAG","text":"

    Then you will need to set up SWAG, the variables of the docker compose are explained on the Github page of SWAG. This is an example of how to set it up using duckdns and docker-compose.

    compose.yaml

    version: \"2.1\"\nservices:\n swag:\n image: ghcr.io/linuxserver/swag\n container_name: swag\n        cap_add:\n        - NET_ADMIN\n    environment:\n        - PUID=1000\n        - PGID=1000\n        - TZ=Europe/Brussels\n        - URL=<mydomain.duckdns>\n        - SUBDOMAINS=wildcard\n        - VALIDATION=duckdns\n        - CERTPROVIDER= #optional\n        - DNSPLUGIN= #optional\n        - DUCKDNSTOKEN=<duckdnstoken> \n        - EMAIL=<e-mail> #optional\n        - ONLY_SUBDOMAINS=false #optional\n        - EXTRA_DOMAINS=<extradomains> #optional\n        - STAGING=false #optional\n        volumes:\n        - /etc/config/swag:/config\n        ports:\n        - 443:443\n        restart: unless-stopped\n

    Don't forget to change the mydomain.duckdns into your personal domain and the duckdnstoken into your token and remove the brackets.

    "},{"location":"getting-started/proxies/swag/#step-3-change-the-config-files","title":"Step 3: Change the config files","text":"

    Navigate to the config folder of SWAG and head to proxy-confs. If you used the example above, you should navigate to: /etc/config/swag/nginx/proxy-confs/ . There are a lot of preconfigured files to use for different apps such as radarr,sonarr,overseerr,...

    To use the bundled configuration file, simply rename photoprism.subdomain.conf.sample in the proxy-confs folder to photoprism.subdomain.conf. Alternatively, you can create a new file photoprism.subdomain.conf in proxy-confs with the following configuration:

    photoprism.subdomain.conf

    server {\n    listen 443 ssl http2;\n    listen [::]:443 ssl http2;\n\n    server_name photoprism.*;\n\n    include /config/nginx/ssl.conf;\n\n    client_max_body_size 0;\n\n    location / {\n        include /config/nginx/proxy.conf;\n        resolver 127.0.0.11 valid=30s;\n        set $upstream_app photoprism;\n        set $upstream_port 2342;\n        set $upstream_proto http;\n        proxy_pass $upstream_proto://$upstream_app:$upstream_port;\n        }\n\n}   \n
    "},{"location":"getting-started/proxies/swag/#step-4-port-forward-port-443","title":"Step 4: Port-forward port 443","text":"

    Since SWAG allows you to set up a secure connection, you will need to open port 443 on your router for encrypted traffic. This is way more secure than port 80 for http.

    "},{"location":"getting-started/proxies/swag/#step-5-restart-swag","title":"Step 5: Restart SWAG","text":"

    When you change anything in the config of Nginx, you will need to restart the container using docker restart swag. If everything went well, you can now access photoprism on the subdomain you configured: photoprism.mydomain.duckdns.org

    Attention

    The docker-container of photoprism won't be named \"photoprism\", it will be \"name_photoprism\". To check this, execute docker ps and check wether it is named \"photoprism\". If it's not, go to your docker-compose.yml file and add the following line to photoprism below 'image' container_name=photoprism. Restart swag afterwards. Keep in mind to not have two photoprism containers with the same name! You could also change the config file of Swag with the right name in the proxy-confs directory.

    "},{"location":"getting-started/proxies/swag/#why-use-a-proxy","title":"Why Use a Proxy?","text":"

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure HTTPS reverse proxy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted by anyone, including your provider, hackers, and governments. Backup tools and file sync apps may refuse to connect as well.

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"getting-started/proxies/traefik/","title":"Using Traefik as Reverse Proxy","text":"

    Best Choice

    • No special settings required in combination with modern web applications
    • WebSocket proxying automatically works
    • Traefik can create and update Let's Encrypt HTTPS certificates for you

    To run PhotoPrism behind Traefik, create a traefik.yaml configuration and then add a traefik service to your compose.yaml or docker-compose.yml file, as shown in the following example:

    compose.yaml

    services:\n  traefik:\n    image: traefik:v3.1\n    restart: unless-stopped\n    ports:\n      - \"80:80\"\n      - \"443:443\"\n    volumes:\n      - \"./traefik.yaml:/etc/traefik/traefik.yaml\"\n      - \"./traefik/data:/data\"\n      - \"/var/run/docker.sock:/var/run/docker.sock\"\n\n  photoprism:\n    image: photoprism/photoprism:latest\n    restart: unless-stopped\n    labels:\n      - \"traefik.enable=true\"\n      - \"traefik.http.routers.photoprism.rule=Host(`example.com`)\"\n      - \"traefik.http.routers.photoprism.tls=true\"\n      - \"traefik.http.routers.photoprism.tls.certresolver=myresolver\" \n    volumes:\n      - \"./originals:/photoprism/originals\"\n      - \"./storage:/photoprism/storage\"\n    environment:\n        PHOTOPRISM_SITE_URL: \"https://example.com/\"\n        PHOTOPRISM_DISABLE_TLS: \"true\"\n

    traefik.yaml

    log:\n  level: INFO\n\nglobal:\n  sendAnonymousUsage: false\n\nentryPoints:\n  web:\n    address: \":80\"\n    http:\n      redirections:\n        entryPoint:\n          to: websecure\n          scheme: https\n  websecure:\n    address: \":443\"\n\nproviders:\n  docker:\n    exposedByDefault: false\n    watch: true\n\napi:\n  insecure: false\n  dashboard: false\n  debug: false\n\ncertificatesResolvers:\n  myresolver:\n    acme:\n      email: ssl-admin@example.com\n      storage: /data/certs.json\n      httpChallenge:\n        entryPoint: web\n

    Note that you must disable HTTPS/TLS in PhotoPrism by setting PHOTOPRISM_DISABLE_TLS to \"true\" as Traefik handles HTTPS connections, and that all settings and config options not related to Traefik have been omitted for brevity.

    Further traefik.yaml examples and a detailed description of the Traefik configuration can be found in the corresponding documentation.

    "},{"location":"getting-started/proxies/traefik/#why-use-a-proxy","title":"Why Use a Proxy?","text":"

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure HTTPS reverse proxy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted by anyone, including your provider, hackers, and governments. Backup tools and file sync apps may refuse to connect as well.

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"getting-started/raspberry-pi/microsd-image/","title":"MicroSD Image for the Raspberry Pi","text":"

    The easiest way to run PhotoPrism on a Raspberry Pi is with PhotoPrismPi.1 Simply flash the image to an SD card and boot your device with it.

    We recommend using a fast MicroSD card with at least 64 GB so that you don't run out of storage space later on. These are usually sold with an adapter that fits into normal SD card slots.

    Raspberry Pi 5

    Since our current MicroSD image is based on Ubuntu 22.04 LTS, it is not yet compatible with the Raspberry Pi 5, which requires Ubuntu 23.10 or later. As an alternative, you can download Ubuntu 24.04, install Docker, and then follow our regular Setup Guide.

    "},{"location":"getting-started/raspberry-pi/microsd-image/#step-1-install-balenaetcher","title":"Step 1: Install balenaEtcher","text":"

    Etcher is a powerful OS image flasher that makes flashing an SD card a pleasant and safe experience. You can download it from the official website or directly from GitHub.

    • Homepage: https://www.balena.io/etcher/
    • Intel / AMD: https://github.com/balena-io/etcher/releases
    • Apple Silicon: https://github.com/Augmentedjs/balena-io-etcher-builds/releases
    "},{"location":"getting-started/raspberry-pi/microsd-image/#step-2-flash-from-url","title":"Step 2: Flash from URL","text":"

    Open balenaEtcher and enter the following URL as image source:

    https://dl.photoprism.app/dist/photoprismpi/latest.zip\n

    When you have selected the image and inserted a suitable card into your computer, press Flash!.

    "},{"location":"getting-started/raspberry-pi/microsd-image/#step-3-boot-your-device","title":"Step 3: Boot Your Device","text":"

    Insert the MicroSD card into the Pi, make sure your device is connected to a wired network, and turn it on. After a few minutes,2 our latest release should be ready to use when you navigate to http://photoprismpi.local/!3

    "},{"location":"getting-started/raspberry-pi/microsd-image/#user-accounts","title":"User Accounts","text":"

    When you first log in to PhotoPrism, the username for the initial super admin account is admin and the password is photoprismpi.

    You can also connect to the server via SSH with the username ubuntu and password ubuntu.

    Danger

    Since they can be easily guessed, both passwords should be changed immediately. This is especially important if your device is connected to the Internet or any other shared network.

    "},{"location":"getting-started/raspberry-pi/microsd-image/#storage-folders","title":"Storage Folders","text":"

    Uploads, sidecar and cache files are stored in /opt/photoprism. External drives can be connected via USB and accessed as folders /mnt/a to /mnt/d without further configuration.

    Should you want to make changes to the default settings, you can find your compose.yaml or docker-compose.yml file in /boot/firmware/docker-compose/photoprism. After connecting via SSH with the credentials provided above, you can obtain root privileges by running sudo -i.

    "},{"location":"getting-started/raspberry-pi/microsd-image/#https-proxy","title":"HTTPS Proxy","text":"

    Caddy is installed as a reverse proxy that can be configured in /etc/caddy/Caddyfile. By default, the automatically generated certificates are not recognized as valid by browsers, so you will see a warning when connecting over HTTPS.

    1. PhotoPrismPi is based on Ubuntu Server and CustomPiOS. Special thanks to Guy Sheffer who helped us build this!\u00a0\u21a9

    2. Download and installation time depends on the speed of your Internet connection.\u00a0\u21a9

    3. If you can't connect, try using the existing hostname or IP address instead.\u00a0\u21a9

    "},{"location":"getting-started/troubleshooting/","title":"Troubleshooting Checklists","text":"

    You are welcome to ask for help in our community chat. Sponsors receive direct technical support via email. Before submitting a support request, try to determine the cause of your problem.

    "},{"location":"getting-started/troubleshooting/#connection-fails","title":"Connection Fails","text":"

    If your browser cannot connect to the Web UI even after waiting a few minutes, run this command to watch the logs including the last 100 messages (omit --tail=100 to see them all, and -f to output only the last logs without watching them):

    docker compose logs -f --tail=100\n

    Before reporting a bug:

    • Check the logs for messages like disk full, disk quota exceeded, no space left on device, read-only file system, error creating path, wrong permissions, no route to host, connection failed, and killed:
      • If a service has been \"killed\" or otherwise automatically terminated, this points to a memory problem (add swap and/or memory; remove or increase usage limits)
      • In case the logs show \"disk full\", \"quota exceeded\", or \"no space left\" errors, either the disk containing the storage folder is full (add storage) or a disk usage limit is configured (remove or increase it)
      • Errors such as \"read-only file system\", \"error creating path\", \"failed to create folder\", \"permission denied\", or \"wrong permissions\" indicate a filesystem permission problem
      • It may help to add the :z mount flag to volumes when using SELinux (Red Hat/Fedora)
      • Log messages that contain \"no route to host\" indicate a problem with the database or Docker network configuration (follow our examples)
    • Make sure you are using the correct protocol (default is http), port (default is 2342), and host (default is localhost):
      • Check if the server port you try to use has been exposed and no firewall is blocking it
      • Only use localhost or 127.0.0.1 if the server is running on the same computer (host)
      • Avoid using IP addresses other than 127.0.0.1 directly, as they can change
      • We recommend configuring a local hostname to access other hosts on your network
    • If you use a firewall, ensure that it is configured correctly and that outgoing connections to our geocoding API are allowed
    • Note that HTTP security headers will prevent the app from loading in a frame (override them)
    • Verify your computer meets the system requirements
    • Go through the checklist for fatal server errors
    "},{"location":"getting-started/troubleshooting/#mariadb","title":"MariaDB","text":"

    Should MariaDB get stuck in a restart loop and PhotoPrism cannot connect to it, this indicates a memory, filesystem, or other permission issue:

    mariadb: mysqld: ready for connections.\nmariadb: mysqld (initiated by: unknown): Normal shutdown\nphotoprism: dial tcp 172.18.0.2:3306: connect: no route to host\nmariadb: mysqld: Shutdown complete\n

    Learn more \u203a

    "},{"location":"getting-started/troubleshooting/#firewall","title":"Firewall","text":"

    Maps & Places: As explained in our Privacy Policy, reverse geocoding and interactive world maps depend on retrieving the necessary information from us and MapTiler AG, headquartered in Switzerland. You therefore need allow requests to these API endpoints if you have a firewall installed and make sure your Internet connection is working.

    Learn more \u203a

    IPTables: On Linux, Docker manipulates the iptables rules to provide network isolation. This does have some implications for what you need to do if you want to have your own policies in addition to the rules Docker manages.

    Learn more \u203a

    "},{"location":"getting-started/troubleshooting/#debug-mode","title":"Debug Mode","text":"

    To enable debug mode, set PHOTOPRISM_LOG_LEVEL to \"debug\" in the environment: section of the photoprism service (or use the --debug flag when running the photoprism command directly):

    services:\n  photoprism:\n    environment:\n      PHOTOPRISM_LOG_LEVEL: \"debug\"\n

    If you need even more detailed logs for debugging, you can enable trace log mode by setting PHOTOPRISM_LOG_LEVEL to \u201ctrace\u201d in the environment: section of the photoprism service (or use the --trace flag when running the photoprism command directly):

    services:\n  photoprism:\n    environment:\n      PHOTOPRISM_LOG_LEVEL: \"trace\"\n

    Then restart all services for the changes to take effect. It can be helpful to keep Docker running in the foreground while debugging so that log messages are displayed directly. To do this, omit the -d parameter when restarting:

    docker compose stop\ndocker compose up \n

    If you see no errors or no logs at all, you may have started the server on a different host and/or port. There could also be an issue with your browser, browser plugins, firewall settings, or other tools you may have installed.

    The default Docker Compose config filename is docker-compose.yml. For simplicity, it doesn't need to be specified when running docker compose or docker-compose in the same directory. Config files for other apps or instances should be placed in separate folders.

    "},{"location":"getting-started/troubleshooting/#docker-doesnt-work","title":"Docker Doesn't Work","text":"

    Make sure you have Docker or Docker Desktop installed, started, and properly configured on your system. It is available for Mac, Linux, and Windows. On Red Hat-compatible Linux distributions like RHEL, CentOS, Fedora, AlmaLinux, and Rocky Linux, you can use Podman and Podman Compose as direct replacements for Docker and Docker Compose.

    \u21aa Getting Docker Up and Running

    "},{"location":"getting-started/troubleshooting/#bad-performance","title":"Bad Performance","text":"

    \u21aa Performance Tips

    \u21aa Solving Windows-Specific Issues

    "},{"location":"getting-started/troubleshooting/#fatal-server-errors","title":"Fatal Server Errors","text":"

    Fatal errors are often caused by one of the following conditions:

    • Your (virtual) server disk is full (add storage)
    • You have accidentally mounted the wrong folders (update config and restart)
    • There is disk space left, but a usage or the inode limit has been reached (change it)
    • You are using a filesystem or network drive with a file size limit (change settings or storage)
    • The storage folder is not writable or mounted read-only (change permissions)
    • Symbolic links were mounted or used within a storage folder (replace with actual paths)
    • The server is low on memory (add memory)
    • You didn't configure at least 4 GB of swap space (add swap)
    • RAW images and/or high-resolution panoramas require additional memory above the recommended minimum (add more swap or memory)
    • The server CPU is overheating (improve cooling)
    • The server has an outdated operating system that is not fully compatible (update)
    • The server hardware is defective and causes random panics (test on another server)
    • The database server is not running, incompatible, or misconfigured (start, upgrade, or fix it)
    • You've upgraded the MariaDB server without running mariadb-upgrade
    • Files are stored on an unreliable device such as a USB flash drive or a shared network folder
    • There are network problems caused by a bad configuration, firewall, or unstable connection
    • Kernel security modules such as AppArmor and SELinux are blocking permissions
    • Your Raspberry Pi has not been configured according to our recommendations

    We recommend checking your Docker Logs for messages like disk full, disk quota exceeded, no space left on device, read-only file system, error creating path, wrong permissions, no route to host, connection failed, and killed:

    • If a service has been \"killed\" or otherwise automatically terminated, this points to a memory problem (add swap and/or memory; remove or increase usage limits)
    • In case the logs show \"disk full\", \"quota exceeded\", or \"no space left\" errors, either the disk containing the storage folder is full (add storage) or a disk usage limit is configured (remove or increase it)
    • Errors such as \"read-only file system\", \"error creating path\", \"failed to create folder\", \"permission denied\", or \"wrong permissions\" indicate a filesystem permission problem
    • Log messages that contain \"no route to host\" indicate a problem with the database or network configuration (follow our examples)

    Start a full rescan if necessary, for example, if it looks like thumbnails or pictures are missing.

    "},{"location":"getting-started/troubleshooting/#app-not-loading","title":"App Not Loading","text":"

    If the app doesn't load in your browser when you navigate to the server URL, you can check the browser console for helpful errors and warnings. Sometimes you just need to wait a moment, for example, if you are using a slow wireless connection or the server was started only a few seconds ago. In case this does not help:

    • You are using an incompatible browser (try another browser)
    • JavaScript is disabled in your browser settings, so you only see the splash screen (enable it)
    • JavaScript was disabled by a browser plugin (disable it or add an exception)
    • Your browser cannot communicate properly with the server, e.g. because a reverse proxy, VPN, or CDN is configured incorrectly (check its configuration and try without)
    • HTTP security headers prevent the app from loading in a frame (override them)
    • An ad blocker or other plugins block requests (disable them or add an exception)
    • There is a problem with your network connection (test if other sites work)
    • You are connected to the wrong server, VPN, CDN, or a DNS record has not been updated yet
    "},{"location":"getting-started/troubleshooting/#cannot-log-in","title":"Cannot Log In","text":"

    If password authentication is enabled and the user interface loads, but you cannot log in with what you assume is the correct password:

    • There is a problem with the integrity, stability or connection of the database that you should be able to diagnose by watching the logs for errors and warnings
    • You had too many failed login attempts, therefore another attempt from your computer is temporarily not possible
    • Caps Lock is enabled on your keyboard, your computer has the wrong input locale set, or somebody else might have changed the password without telling you
    • PHOTOPRISM_ADMIN_PASSWORD does not have a minimum length of 8 characters, so PhotoPrism has been started without a password since there is no default
    • Your password contains one or more $ signs that were not properly escaped in your compose.yaml or docker-compose.yml file (escape them and reset your database or manually set a new password)
    • The password may be correct, but the username is wrong and does not match PHOTOPRISM_ADMIN_USER
    • There is a problem with the schema or data in the auth_sessions database table that can be resolved by running the photoprism auth reset --yes command in a terminal to reset it to a clean state and force a re-login of all users (this will also delete all client access tokens and app passwords users may have created)
    • You upgraded from an early test or preview build and might need to run the photoprism users reset --yes command in a terminal after the upgrade, see Known Issues for details (this resets the auth_users table to a clean state and requires accounts to be recreated)
    • Your browser cannot communicate properly with the server, e.g. because a reverse proxy, VPN, or CDN is configured incorrectly (check its configuration and try without)
    • You are connected to the wrong server, VPN, CDN, or a DNS record has not been updated yet
    • Remember that the initial admin username and password cannot be changed after PhotoPrism has been started for the first time

    We also recommend checking your Docker Logs for messages like disk full, disk quota exceeded, no space left on device, read-only file system, error creating path, wrong permissions, no route to host, connection failed, and killed:

    • If a service has been \"killed\" or otherwise automatically terminated, this points to a memory problem (add swap and/or memory; remove or increase usage limits)
    • In case the logs show \"disk full\", \"quota exceeded\", or \"no space left\" errors, either the disk containing the storage folder is full (add storage) or a disk usage limit is configured (remove or increase it)
    • Errors such as \"read-only file system\", \"error creating path\", \"failed to create folder\", \"permission denied\", or \"wrong permissions\" indicate a filesystem permission problem
    • Log messages that contain \"no route to host\" indicate a problem with the database or network configuration (follow our examples)

    To see which user accounts exist on your instance, open a terminal and run photoprism users ls. A new password can be set with photoprism passwd [username]. You can then try to log in again. Upgrade to the latest release, restart the server, and check the logs for errors and warnings if it still doesn't work.

    "},{"location":"getting-started/troubleshooting/#no-webdav-access","title":"No WebDAV Access","text":"

    If you followed our step-by-step guide and still have trouble connecting via WebDAV:

    • WebDAV has been disabled for all users in the advanced settings
    • WebDAV is unavailable because your instance is running in public mode (disable it)
    • You are trying to connect to an invalid path, try /originals/ without omitting the / at the end, and read our notes on installing PhotoPrism in a subdirectory on a shared domain
    • Your user account role is not permitted to use WebDAV (try as User or Admin)
    • WebDAV access has not been enabled for your user account (enable it)
    • You are experiencing a general authentication problem, see Cannot Log In
    • Your WebDAV client requires a secure connection (connect via HTTPS)
    • Your instance or reverse proxy uses an invalid HTTPS/TLS certificate
    • As a Windows user, you may need to change the basic authentication level
    • Your browser cannot communicate properly with the server, e.g. because a reverse proxy, VPN, or CDN is configured incorrectly (check its configuration and try without)
    • You are connected to the wrong server, VPN, or a DNS record has not been updated yet
    "},{"location":"getting-started/troubleshooting/#missing-pictures","title":"Missing Pictures","text":"

    If you have indexed your library and some images or videos are missing, first check Library > Errors for errors and warnings. In case the application logs do not contain anything helpful:

    • The files exceed the size limit in megabyte or the resolution limit in megapixels
    • The files have bad filesystem permissions or the wrong owner, so they cannot be opened
    • The pictures are in Review due to low quality or incomplete metadata
    • The file type is generally unsupported
    • The file type is generally supported, but a specific feature or codec is not
    • The indexer has skipped the files because they are exact duplicates
    • The indexer has skipped the files because they have an incorrect extension that does not match their actual format, e.g. JPEG images with a .heic extension
    • The files are ignored based on pattern in a .ppignore file
    • They are in Library > Hidden because thumbnails could not be created:
      • Preview Images are disabled under Settings > Library (enable them)
      • FFmpeg and/or RAW converters are disabled under Settings > Advanced
      • The file is broken, e.g. because of short Huffman data (try to fix it)
      • Your storage folder is full, or a quota/inode limit has been reached (increase it)
      • Your storage folder is not writable or mounted read-only (change permissions)
    • Multiple files were stacked based on their metadata or file names
    • The private or archived status was restored from a backup
    • The NSFW filter is enabled, so they were marked as private
    • You are not signed in as admin, so you cannot see everything
    • You try to index a shared drive on a remote server, but the server is offline
    • Somebody has deleted files without telling you
    • Your server does not have at least 4 GB of swap or a hard memory limit is configured, which may cause unexpected restarts when the indexer temporarily needs more memory to process large files
      • Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum
    • You are connected to the wrong server, VPN, CDN, or a DNS record has not been updated yet

    Depending on the cause of the problem, you may need to perform a full rescan once the issue is resolved.

    "},{"location":"getting-started/troubleshooting/#zip-archives","title":"Zip Archives","text":"

    When you have tried to download multiple pictures or albums and found that some files are missing in the resulting zip archive or you got the error message \"No files available for download\":

    • Your index may be out of sync with the file system (reindex your library and wait until the operation has been completed)
    • In order to include RAW, XMP and/or generated sidecar files, your download preferences may need to be changed under Settings > General
    • If this didn't help, you may need to perform a complete rescan of your library, e.g. after upgrading to a new release or restoring your index from a backup

    Also make sure that there is enough free disk space available, since the server creates a temporary zip file when multiple pictures are selected for download. Complete albums are compressed while downloading without needing temporary storage.

    "},{"location":"getting-started/troubleshooting/#file-downloads","title":"File Downloads","text":"

    Follow the steps to resolve zip download issues if you are having problems downloading selected pictures, individual files, or stacks of files that belong to a single photo.

    If this didn't help, the problems might be caused by your browser settings, e.g. insufficient permissions to download multiple files, browser plugins, a firewall, VPN, CDN or proxy that you use together with PhotoPrism.

    "},{"location":"getting-started/troubleshooting/#wrong-search-results","title":"Wrong Search Results","text":"

    If search results are incorrect, for example, in the wrong order or not filtered properly:

    • Indexing is still in progress and has not been completed yet
    • You need to re-index your pictures, for example after updating PhotoPrism
    • Previously failed migrations must be re-run to update the index schema
    • The database server is incompatible or needs to be updated

    It may be a bug if you cannot find any other reasons, such as a local configuration problem or a misunderstanding in how the software works. Please note that reports must be reproducible in order for us to provide a solution.

    "},{"location":"getting-started/troubleshooting/#broken-thumbnails","title":"Broken Thumbnails","text":"

    If some pictures have broken or missing thumbnails, first check Library > Errors for errors and warnings. In case the application logs do not contain anything helpful:

    • The issue can be resolved by reloading the page or clearing the browser cache
    • You browse non-JPEG files under Library > Originals which have an icon but no preview
    • Preview Images are disabled under Settings > Library (enable them)
    • Dynamic Previews are disabled under Settings > Advanced or your server is not powerful enough
    • The sizes in Settings > Advanced have been changed so the request can't be fulfilled
    • FFmpeg and/or RAW converters are disabled under Settings > Advanced (enable them)
    • Your storage folder is full, or a quota/inode limit has been reached (increase it)
    • Your storage folder is not writable or mounted read-only (change permissions)
    • Your cache storage folder is not accessible, has been renamed, or was not mounted on a permanent volume, so the cached thumbnails have been lost after a restart (run the photoprism thumbs command in a terminal to regenerate them after fixing this)
    • Originals or thumbnail files were deleted manually, for example to free up disk space
    • Files cannot be opened, e.g. because the file system permissions have been changed
    • Files are stored on an unreliable device such as a USB flash drive or a shared network folder
    • Some thumbnails could not be created because you didn't configure at least 4 GB of swap
    • Your browser cannot communicate properly with the server, e.g. because a reverse proxy, VPN, or CDN is configured incorrectly (check its configuration and try without)
    • Your proxy, router, or firewall has a request rate limit, so some requests fail
    • There are other network problems caused by a firewall, router, or unstable connection
    • An ad blocker or other plugins block requests (disable them or add an exception)
    • You are connected to the wrong server, VPN, CDN, or a DNS record has not been updated yet

    We also recommend checking your Docker Logs for messages like disk full, disk quota exceeded, no space left on device, read-only file system, error creating path, wrong permissions, and killed:

    • If a service has been \"killed\" or otherwise automatically terminated, this points to a memory problem
    • In case the logs show \"disk full\", \"quota exceeded\", or \"no space left\" errors, either the disk containing the storage folder is full (add storage) or a disk usage limit is configured (remove or increase it)
    • Errors such as \"read-only file system\", \"error creating path\", \"failed to create folder\", \"permission denied\", or \"wrong permissions\" indicate a filesystem permission problem

    Depending on the cause of the problem, you may need to perform a full rescan once the issue is resolved.

    "},{"location":"getting-started/troubleshooting/#videos-dont-play","title":"Videos Don't Play","text":"

    In case FFmpeg is disabled or not installed, videos cannot be indexed because still images cannot be created. You should also have ExifTool enabled to extract metadata such as duration, resolution, and codec.

    If videos do not play and/or you only see a white/black area when you open a video:

    • You are using an incompatible browser, e.g. without AVC support (try another browser)
    • AVC support or related JavaScript features have been disabled in your browser (check the settings and try another browser)
    • It is a large non-AVC video that needs to be transcoded first (wait or run photoprism convert to pre-transcode videos)
    • An ad blocker or other plugins block requests (disable them or add an exception)
    • Your (virtual) server disk is full, or a quota/inode limit has been reached (increase it)
    • The storage folder is not writable or mounted read-only (change permissions)
    • Files are stored on an unreliable device such as a USB flash drive or a shared network folder (check if the files are accessible)
    • Your browser cannot communicate properly with the server, e.g. because a reverse proxy, VPN, or CDN is configured incorrectly (check its configuration and try without)
    • There are other network problems caused by a proxy, firewall, or unstable connection (try a direct connection)
    • You are connected to the wrong server, VPN, CDN, or a DNS record has not been updated yet

    We recommend that you check your Docker Logs and the browser console for messages related to HTTP requests, permissions, security, FFmpeg, videos, and file conversion.

    Please note:

    1. Not all video and audio formats can be played with every browser. For example, AAC - the default audio codec for MPEG-4 AVC / H.264 - is supported natively in Chrome, Safari, and Edge, while it is only optionally supported by the OS in Firefox and Opera.
    2. HEVC/H.265 video files can have a .mp4 file extension too, which is often associated with AVC only. This is because MP4 is a container format, meaning that the actual video content may be compressed with H.264, H.265, or something else. The file extension doesn't really tell you anything other than that it's probably a video file.
    3. MPEG-4 AVC videos are not re-encoded if they exceed the configured bitrate limit. To reduce the size of AVC videos, you can manually replace the original files with a smaller version or wait for a future release that offers this functionality.

    We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app. Ask for technical support if you need help, it could be a local configuration problem, or a misunderstanding in how the software works.

    "},{"location":"getting-started/troubleshooting/browsers/","title":"Diagnosing Frontend Issues","text":"

    Problems with the user interface can be caused by a bug or an incompatible browser:

    • some features may not be supported by non-standard browsers, as well as nightly, unofficial, or outdated versions
    • not all video and audio formats can be played with every browser, device, and operating system
    • for example, AAC - the default audio codec for MPEG-4 AVC / H.264 - is supported natively in Chrome, Safari, and Edge, while it is only optionally supported by the OS in Firefox and Opera

    If the user interface doesn't load at all, our App Not Loading checklist helps you identify and resolve the cause.

    "},{"location":"getting-started/troubleshooting/browsers/#try-another-browser","title":"Try Another Browser","text":"

    To test if you have a general problem that is not browser-specific, open the Web UI in other browsers:

    • if you are using Firefox Nightly, try the stable version and Chrome or Chromium
    • if you have browser plugins installed, try disabling them to see if this makes a difference
    • when the problem disappears, you know that the issue is browser-dependent or caused by a plugin
    • otherwise, the issue may not be specific to the browser version
    • make a note in which browsers the problem occurs, as this will be helpful when submitting a support request

    The user interface works with most modern browsers, and runs best on Chrome, Chromium, Safari, Firefox, and Edge. Opera and Samsung Internet have been reported to be compatible as well. Due to limited resources, we can not test every release with all browser types and versions.

    "},{"location":"getting-started/troubleshooting/browsers/#getting-error-details","title":"Getting Error Details","text":"

    If possible, please also include the error type, error message, and URL of the affected resource when submitting a support request. For this purpose, check the browser console for warnings and errors as described below. It is perfectly fine to take screenshots instead of writing down the details.

    In case you don't see any log messages, try reloading the page, as the problem may occur while the page is loading.

    Chrome, Chromium, and EdgeFirefoxSafariMobile Safari
    • press \u2318+Option+J (Mac) or Ctrl+Shift+J (Windows, Linux, Chrome OS) to go directly to the Developer Tools
    • or, navigate to More tools > Developer tools in the browser menu and open the Console tab
    • press \u2318+Option+K (Mac) or Ctrl+Shift+K (Windows) to go directly to the Firefox Web Console panel
    • or, navigate to Web Development > Web Console in the menu and open the Console panel

    Before you can access the console in Safari on MacOS, you first need to enable the Develop menu:

    1. Choose Safari Menu > Preferences and select the Advanced Tab
    2. Select \"Show Develop menu in menu bar\"

    Once the Develop menu is enabled:

    • press Option+\u2318+C to go directly to the Javascript Console
    • or, navigate to Develop > Show Javascript Console in the browser menu

    Browser logs on Apple mobile devices running iOS or iPadOS can be viewed when you connect them to a Mac. Before you can connect your device to a Mac, you must allow your device to be inspected:

    1. Open the Settings app
    2. Go to Safari
    3. Scroll down to Advanced
    4. Enable the Web Inspector toggle

    If you now connect the device to your Mac with a cable, websites opened in Safari on iOS and iPadOS will appear in a submenu for the connected device in the Develop menu of the Safari desktop browser. Note that when prompted, you may need to confirm that you trust the Mac you are connecting your device to.

    Web pages (and other content) are separated by app, making it easier for you to find the web page you are looking for. Once you have found and selected the site you want to inspect, a Web Inspector window will open. See Apple's Developer Guide for additional help and information.

    We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app. Ask for technical support if you need help, it could be a local configuration problem, or a misunderstanding in how the software works.

    "},{"location":"getting-started/troubleshooting/docker/","title":"Getting Docker Up and Running","text":"

    You are welcome to ask for help in our community chat. Sponsors receive direct technical support via email. Before submitting a support request, try to determine the cause of your problem.

    "},{"location":"getting-started/troubleshooting/docker/#installation","title":"Installation","text":"

    If you cannot use the docker and docker compose (or docker-compose) commands, make sure Docker is running on the host you are connected to and your current user has permission to use it. The following instructions explain how to install Docker:

    • Ubuntu, Mint, Debian, and Arch Linux
    • Microsoft Windows
    • Apple macOS

    Alternatively, Podman is supported as a drop-in replacement for Docker on Red Hat-compatible Linux distributions like RHEL, CentOS, Fedora, AlmaLinux, and Rocky Linux.

    "},{"location":"getting-started/troubleshooting/docker/#ubuntu-linux","title":"Ubuntu Linux","text":"

    If you are using Ubuntu Linux, you can run this script to install the latest Docker version, including the Compose Plugin, on your server in one step:

    bash <(curl -s https://setup.photoprism.app/ubuntu/install-docker.sh)\n
    "},{"location":"getting-started/troubleshooting/docker/#docker-compose","title":"Docker Compose","text":"

    The examples in our guides now use the new docker compose command by default. However, if your Docker version does not yet support the Compose Plugin, you can still use the standalone docker-compose command.

    On some Linux distributions, you may need to install an additional package. To do so, you can use a graphical software package manager or run the following command in a terminal to install the Compose Plugin for Docker on Ubuntu and Debian:

    sudo apt update\nsudo apt install docker-compose-plugin\n

    If that does not work, this will install the legacy docker-compose command:

    sudo apt update\nsudo apt install docker-compose\n

    Running the following commands will add a docker-compose alias for the new Compose plugin so that older scripts don't break:

    echo 'docker compose \"$@\"' | sudo tee /bin/docker-compose\nsudo chmod +x /bin/docker-compose\n
    "},{"location":"getting-started/troubleshooting/docker/#podman-compose","title":"Podman Compose","text":"

    On Red Hat-compatible Linux distributions like RHEL, CentOS, Fedora, AlmaLinux, and Rocky Linux, you can use Podman and Podman Compose as direct replacements for Docker and Docker Compose. The following installs the podman and podman-compose commands if they are not already installed:

    sudo dnf update -y\nsudo dnf install epel-release -y\nsudo dnf install netavark aardvark-dns podman podman-docker podman-compose -y\nsudo systemctl start podman\nsudo systemctl enable podman\npodman --version\n

    We also provide a setup script that conveniently installs Podman and downloads the default configuration to a directory of your choice:

    mkdir -p /opt/photoprism\ncd /opt/photoprism\ncurl -sSf https://dl.photoprism.app/podman/install.sh | bash\n

    Please keep in mind to replace the docker and docker compose commands with podman and podman-compose when following the examples in our documentation.

    "},{"location":"getting-started/troubleshooting/docker/#using-docker","title":"Using Docker","text":""},{"location":"getting-started/troubleshooting/docker/#cannot-connect","title":"Cannot Connect","text":"

    If you see the error message \"Cannot connect to the Docker daemon\", it means that Docker is not installed or not running yet. Before you try anything else, it may help to simply restart your computer.

    On many Linux distributions, this command will start the Docker daemon manually if needed:

    sudo systemctl start docker.service\n

    On other operating systems, start Docker Desktop and enable the \"Start Docker Desktop when you log in\" option in its settings.

    "},{"location":"getting-started/troubleshooting/docker/#connection-aborted","title":"Connection Aborted","text":"

    If you see the error message \"Connection aborted\" or \"Connection denied\", it usually means that your current user does not have permission to use Docker.

    On Linux, this command grants permission by adding a user to the docker group (relogin for changes to take effect):

    sudo usermod -aG docker [username]\n

    Alternatively, you can prefix the docker and docker-compose commands with sudo when not running as root, for example:

    sudo docker compose stop\nsudo docker compose up -d\n

    Note that this will point the home directory shortcut ~ to /root in the volumes: section of your compose.yaml or docker-compose.yml.

    "},{"location":"getting-started/troubleshooting/docker/#iptables-firewall","title":"IPTables Firewall","text":"

    On Linux, Docker manipulates the iptables rules to provide network isolation. This does have some implications for what you need to do if you want to have your own policies in addition to the rules Docker manages.

    Learn more \u203a

    "},{"location":"getting-started/troubleshooting/docker/#wrong-mtu-size","title":"Wrong MTU Size","text":"

    If you use Docker on your server or on a virtual machine, technical limitations of the local network or your internet provider can sometimes make it impossible to reach external services such as the Reverse Geocoding API that we operate for our users. In particular, the network cards of virtual machines often do not have the standard Maximum Transmission Unit (MTU) of 1500, but a smaller size like 1492 or 1454.

    In this case, you must configure the virtual network cards of your Docker containers so that they have an MTU size that is less than or equal to that of the outgoing network, for example by adding the following to your compose.yaml (or docker-compose.yml) config files:

    networks:\n  default:\n    driver: bridge\n    driver_opts:\n      com.docker.network.driver.mtu: 1450\n

    Learn more \u203a

    All network configuration changes require a restart of the affected services and/or the Docker daemon to take effect.

    "},{"location":"getting-started/troubleshooting/docker/#viewing-logs","title":"Viewing Logs","text":"

    You can run this command to watch the Docker service logs, including the last 100 messages (omit --tail=100 to see them all, and -f to output only the last logs without watching them):

    docker compose logs -f --tail=100 \n

    A good way to troubleshoot configuration issues is to increase the log level. To enable trace log mode, set PHOTOPRISM_LOG_LEVEL to \"trace\" in the environment: section of the photoprism service (or use the --trace flag when running the photoprism command directly):

    services:\n  photoprism:\n    environment:\n      PHOTOPRISM_LOG_LEVEL: \"trace\"\n      ...\n

    Now restart all services for your changes to take effect:

    docker compose stop\ndocker compose up -d\n

    It can also be helpful to keep Docker running in the foreground while debugging, so that log messages are displayed directly. To do this, omit the -d parameter when (re)starting:

    docker compose stop\ndocker compose up\n

    If you see no errors or no logs at all, you may have started the server on a different host and/or port. There could also be an issue with your browser, browser plugins, firewall settings, or other tools you may have installed.

    The default Docker Compose config filename is docker-compose.yml. For simplicity, it doesn't need to be specified when running docker compose or docker-compose in the same directory. Config files for other apps or instances should be placed in separate folders.

    "},{"location":"getting-started/troubleshooting/docker/#adding-swap","title":"Adding Swap","text":"

    Note that indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory above the recommended minimum. We recommend not to set a hard memory limit, unless you are familiar with memory management and understand the implications.

    "},{"location":"getting-started/troubleshooting/docker/#linux","title":"Linux","text":"

    Open a terminal and run this command to check if your server has swap configured.

    swapon --show\n

    Example output:

    NAME      TYPE SIZE USED PRIO\n/swapfile file  64G  88M   -2\n

    This means you have 64 GB of swap and don't need to add more. Learn how much you need.

    Otherwise, run these commands to permanently add 4 GB of swap (or more depending on how much physical memory you have):

    sudo -i\nfallocate -l 4G /swapfile\nchmod 600 /swapfile\nmkswap /swapfile\nswapon /swapfile\necho '/swapfile none swap sw 0 0' | tee -a /etc/fstab\n

    You can skip sudo -i if you are already logged in as root.

    "},{"location":"getting-started/troubleshooting/docker/#raspbian","title":"Raspbian","text":"

    Open a terminal on your Raspberry Pi and run the following command to verify if it has swap configured:

    swapon --show\n

    Example output:

    NAME      TYPE SIZE USED PRIO\n/swapfile file  100M  0B   -2\n

    If no swap has been configured or the command only shows 100 MB, open /etc/dphys-swapfile with a text editor, search for CONF_SWAPSIZE=100 and increase the value to 2048 if your device has 4 GB of physical memory, and 4096 otherwise:

    sudo nano /etc/dphys-swapfile\n

    Then restart for the changes to take effect:

    sudo reboot\n

    In addition, you can reduce memory usage and improve stability by setting PHOTOPRISM_WORKERS to 1 in your compose.yaml or docker-compose.yml file to limit the number of indexing workers.

    "},{"location":"getting-started/troubleshooting/docker/#windows","title":"Windows","text":"

    It is important to increase the Docker memory limit to 4 GB or more when using Hyper-V. The default of 2 GB can reduce indexing performance and cause unexpected restarts. Also make sure you configure at least 4 GB of swap space. Docker Desktop uses dynamic memory allocation with WSL 2, meaning you do not need to change any memory-related settings (depending on which version of Windows and Docker you are using).

    "},{"location":"getting-started/troubleshooting/docker/#macos","title":"macOS","text":"

    It is important to increase the Docker memory limit to 4 GB or more, as the default of 2 GB can reduce indexing performance and cause unexpected restarts. Also, ensure that you configure at least 4 GB of swap space.

    "},{"location":"getting-started/troubleshooting/docker/#kernel-security","title":"Kernel Security","text":"

    We recommend disabling Linux kernel security modules like SELinux (Red Hat/Fedora) on private servers, especially if you have no experience configuring them.

    If you have working configuration rules for a particular Linux distribution, feel free to share the instructions with the community so that less experienced users can harden their installation without running into problems.

    "},{"location":"getting-started/troubleshooting/docker/#file-permissions","title":"File Permissions","text":"

    Errors such as \"read-only file system\", \"error creating path\", \"failed to create folder\", \"permission denied\", or \"wrong permissions\" indicate a filesystem permission problem:

    • Use a file manager, or the commands ls -alh, chmod, and chown on Unix-like operating systems, to check and change filesystem permissions so all files and folders are accessible
    • The app and database storage folders must be writable as well: Verify that the services have write permissions and that you have not mounted the folders read-only on your host or via Docker using the :ro flag
    • If you have configured specific user and group IDs for a service, make sure they match
    • If symbolic links are mounted or used within storage folders, replace them with actual paths
    • It may help to add the :z mount flag to volumes when using SELinux (Red Hat/Fedora)
    • When mounting folders that only root has access to, you may have to prefix the docker and docker-compose commands with sudo on Linux if you are not already logged in as root

    An easy way to test for missing permissions is to (temporarily) remove restrictions and make the entire folder accessible to everyone:

    sudo chmod -R a+rwX [folder]\n
    Start a full rescan once all issues have been resolved, especially if it looks like thumbnails or pictures are missing.

    Be very careful when changing permissions in shared hosting environments. If you are using PhotoPrism on corporate or university servers, we recommend that you ask your IT help desk for advice.

    "},{"location":"getting-started/troubleshooting/docker/#overlay-volumes","title":"Overlay Volumes","text":"

    Depending on overlay file system support, it is possible to mount additional host folders as sub folders of /photoprism/originals (or other storage folders), for example:

    volumes:\n  - \"/home/username/Pictures:/photoprism/originals\"\n  - \"/example/friends:/photoprism/originals/friends\"\n  - \"/mnt/photos:/photoprism/originals/media\"\n

    For this to work, you should have the cgroupfs-mount package installed, as shown in the installation script we provide. You may otherwise find that files added to the mounted folders are not visible on the host, and data loss may occur.

    We recommend that you start with a simple configuration without overlay volume mounts or path placeholders like ~, and only move on to a more complex setup once this works.

    "},{"location":"getting-started/troubleshooting/docker/#disk-space","title":"Disk Space","text":"

    In case the logs show \"disk full\", \"quota exceeded\", or \"no space left\" errors, either the disk containing the storage folder is full (get a new one or use a different disk) or a disk usage limit is configured, for example in the Docker, Kubernetes, or Virtual Machine configuration (remove or increase it):

    • on Linux and other Unix-like operating systems, the available disk space can be viewed by running df -h in a terminal
    • if you are using Kubernetes, Docker Desktop, Hyper-V, or a Virtual Machine, they have their own settings to adjust the size of storage, RAM, and swap
    • for details, refer to the corresponding documentation

    Start a full rescan if necessary, for example, if it looks like thumbnails or pictures are missing.

    "},{"location":"getting-started/troubleshooting/docker/#network-storage","title":"Network Storage","text":"

    Shared folders that have already been mounted on your host under a drive letter or path can be used with Docker containers like any other directory. As shown below, certain types of network storage can alternatively be mounted directly with Docker Compose.

    Please note that the required system dependencies must be installed on your computer in order to mount NFS (Unix/Linux) and/or CIFS shares (Windows/Mac), e.g. the nfs-client and cifs-utils packages on Ubuntu Linux. Also make sure that your Docker version and operating system are up-to-date, and that the latest Subsystem for Linux (WSL) is installed if you have a Windows PC.

    Never store database files, e.g. used by MariaDB or SQLite, on an unreliable device like a USB stick, SD card or network drive as this leads to poor performance and can also result in data loss.

    "},{"location":"getting-started/troubleshooting/docker/#unix-nfs","title":"Unix / NFS","text":"

    Follow this docker-compose.yml example to mount Network File System (NFS) shares e.g. from Unix servers or NAS devices:

    services:\n  photoprism:\n    # ...\n    volumes:\n      # Map named volume \"originals\"\n      # to \"/photoprism/originals\":\n      - \"originals:/photoprism/originals\"     \n  mariadb:\n    # ...\n\n# Specify named volumes:\nvolumes:\n  originals:\n    driver_opts:\n      type: nfs\n      # Authentication and other mounting options:\n      o: \"addr=1.2.3.4,username=user,password=secret,soft,rw,nfsvers=4.1\"\n      # Mount this path:\n      device: \":/mnt/example\"\n

    device should contain the path to the share on the NFS server, note the : at the beginning. In the above example, the share can be mounted as the named volume originals. You can also choose another name as long as it is consistent.

    Driver-specific options can be set after the server address in o, see the nfs manual page. Here are some examples of commonly used options:

    • nfsvers=3, nfsvers=4, or nfsvers=4.1 to specify the NFS version
    • nolock (optional): Remote applications on the NFS server are not affected by lock files inside the Docker container (only other processes inside the container are affected by locks)
    • timeo=n (optional, default 600): The NFS client waits n tenths of a second before retrying an NFS request
    • soft (optional): The NFS client aborts an NFS request after retrans=n unsuccessful retries, otherwise it retries indefinitely
    • retrans=n (optional, default 2): Sets the number of retries for NFS requests, only relevant when using soft

    When you are done, please restart all services for the changes to take effect.

    Because some operating environments and file systems do not enforce character set encodings, NFS v4.1 supports the fs_charset_cap attribute, which indicates the UTF-8 capabilities to the client.

    "},{"location":"getting-started/troubleshooting/docker/#smb-cifs","title":"SMB / CIFS","text":"

    Follow this docker-compose.yml example to mount CIFS network shares, e.g. from Windows, NAS devices or Linux servers with Samba:

    services:\n  photoprism:\n    # ...\n    volumes:\n      # Map named volume \"originals\"\n      # to \"/photoprism/originals\":\n      - \"originals:/photoprism/originals\"     \n  mariadb:\n    # ...\n\n# Specify named volumes:\nvolumes:\n  originals:\n    driver_opts:\n      type: cifs\n      o: \"iocharset=utf8,username=user,password=secret,rw\"\n      device: \"//host/folder\"\n

    Then restart all services for the changes to take effect. Note that related values must start at the same indentation level in YAML and that tabs are not allowed for indentation. We recommend using 2 spaces, but any number will do as long as it is consistent.

    We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app. Ask for technical support if you need help, it could be a local configuration problem, or a misunderstanding in how the software works.

    "},{"location":"getting-started/troubleshooting/firewall/","title":"Configuring Your Firewall","text":"

    You are welcome to ask for help in our community chat. Sponsors receive direct technical support via email. Before submitting a support request, try to determine the cause of your problem.

    "},{"location":"getting-started/troubleshooting/firewall/#incoming-requests","title":"Incoming Requests","text":"

    Unless you have changed the default configuration, PhotoPrism is reachable via port 2342 on all network devices. If you are using a firewall, please ensure that this port can be accessed from other computers on your network, or that your instance can be accessed through a reverse proxy:

    "},{"location":"getting-started/troubleshooting/firewall/#outgoing-connections","title":"Outgoing Connections","text":"

    As explained in our Privacy Policy, reverse geocoding and interactive world maps depend on retrieving the necessary information from us and MapTiler AG, headquartered in Switzerland. Both services are provided with a very high level of privacy and confidentiality.

    View Privacy Policy \u203a View Compliance FAQ \u203a

    In order to successfully set up your installation and view location details in PhotoPrism, you must allow requests to the following hosts if you have a firewall installed, and make sure that your Internet connection is working:

    • dl.photoprism.app
    • my.photoprism.app
    • cdn.photoprism.app
    • maps.photoprism.app
    • places.photoprism.app
    • places.photoprism.xyz

    In addition, the following API endpoints should be allowed so that public Docker images can be pulled from Docker Hub:

    • auth.docker.io
    • registry-1.docker.io
    • index.docker.io
    • dseasb33srnrn.cloudfront.net
    • production.cloudflare.docker.com
    "},{"location":"getting-started/troubleshooting/firewall/#iptables-and-docker","title":"IPTables and Docker","text":"

    On Linux, Docker manipulates the iptables rules to provide network isolation. This does have some implications for what you need to do if you want to have your own policies in addition to the rules Docker manages.

    Learn more \u203a

    "},{"location":"getting-started/troubleshooting/firewall/#docker-mtu-size","title":"Docker MTU Size","text":"

    If you use Docker on your server or on a virtual machine, technical limitations of the local network or your internet provider can also make it impossible to reach external services. In particular, the network cards of virtual machines often do not have the standard Maximum Transmission Unit (MTU) of 1500, but a smaller size like 1492 or 1454.

    In this case, you must configure the virtual network cards of your Docker containers so that they have an MTU size that is less than or equal to that of the outgoing network, for example by adding the following to your compose.yaml (or docker-compose.yml) config files:

    networks:\n  default:\n    driver: bridge\n    driver_opts:\n      com.docker.network.driver.mtu: 1450\n

    Learn more \u203a

    All network configuration changes require a restart of the affected services and/or the Docker daemon to take effect.

    "},{"location":"getting-started/troubleshooting/logs/","title":"Collecting Debug Information","text":"Web AppBrowserDocker Logs

    Make sure Logs is enabled under Settings > General so you can see log messages in the Web UI.

    Live Logs

    The continuously updated live logs in Library > Logs are especially useful for diagnosing indexing and import issues, but also display other types of logs (depending on the log level):

    1. Navigate to Library
    2. Open the Logs tab

    Only a limited number of messages are visible in the Web App to reduce memory usage. You can see all messages in the Docker Logs. This may be more convenient if you are looking for information on a specific file or want to attach your full logs to a support request.

    Errors and Warnings

    1. Expand the main navigation
    2. Open the Library sub navigation
    3. Navigate to Library > Errors

    If you have a frontend issue, it is often helpful to check the browser console for errors and warnings. A console is available in all modern browsers and can be activated via keyboard shortcuts or the browser menu.

    Problems with the user interface can be caused by a bug or an incompatible browser: Some features may not be supported by non-standard browsers, as well as nightly, unofficial, or outdated versions.

    In case you don't see any log messages, try reloading the page, as the problem may occur while the page is loading.

    Chrome, Chromium, and Edge

    • press \u2318+Option+J (Mac) or Ctrl+Shift+J (Windows, Linux, Chrome OS) to go directly to the Developer Tools
    • or, navigate to More tools > Developer tools in the browser menu and open the Console tab

    Firefox

    • press \u2318+Option+K (Mac) or Ctrl+Shift+K (Windows) to go directly to the Firefox Web Console panel
    • or, navigate to Web Development > Web Console in the menu and open the Console panel

    Safari

    Before you can access the console in Safari on MacOS, you first need to enable the Develop menu:

    1. Choose Safari Menu > Preferences and select the Advanced Tab
    2. Select \"Show Develop menu in menu bar\"

    Once the Develop menu is enabled:

    • press Option+\u2318+C to go directly to the Javascript Console
    • or, navigate to Develop > Show Javascript Console in the browser menu

    Mobile Safari

    Browser logs on Apple mobile devices running iOS or iPadOS can be viewed when you connect them to a Mac. Before you can connect your device to a Mac, you must allow your device to be inspected:

    1. Open the Settings app
    2. Go to Safari
    3. Scroll down to Advanced
    4. Enable the Web Inspector toggle

    If you now connect the device to your Mac with a cable, websites opened in Safari on iOS and iPadOS will appear in a submenu for the connected device in the Develop menu of the Safari desktop browser. Note that when prompted, you may need to confirm that you trust the Mac you are connecting your device to.

    Web pages (and other content) are separated by app, making it easier for you to find the web page you are looking for. Once you have found and selected the site you want to inspect, a Web Inspector window will open. See Apple's Developer Guide for additional help and information.

    You can run this command to watch the Docker service logs, including the last 100 messages (omit --tail=100 to see them all, and -f to output only the last logs without watching them):

    docker compose logs -f --tail=100 \n

    A good way to troubleshoot configuration issues is to increase the log level. To enable trace log mode, set PHOTOPRISM_LOG_LEVEL to \"trace\" in the environment: section of the photoprism service (or use the --trace flag when running the photoprism command directly):

    services:\n  photoprism:\n    environment:\n      PHOTOPRISM_LOG_LEVEL: \"trace\"\n      ...\n

    Now restart all services for your changes to take effect:

    docker compose stop\ndocker compose up -d\n

    It can also be helpful to keep Docker running in the foreground while debugging, so that log messages are displayed directly. To do this, omit the -d parameter when (re)starting:

    docker compose stop\ndocker compose up\n

    If you see no errors or no logs at all, you may have started the server on a different host and/or port. There could also be an issue with your browser, browser plugins, firewall settings, or other tools you may have installed.

    The default Docker Compose config filename is docker-compose.yml. For simplicity, it doesn't need to be specified when running docker compose or docker-compose in the same directory. Config files for other apps or instances should be placed in separate folders.

    We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app. Ask for technical support if you need help, it could be a local configuration problem, or a misunderstanding in how the software works.

    "},{"location":"getting-started/troubleshooting/mariadb/","title":"Troubleshooting MariaDB Problems","text":"

    You are welcome to ask for help in our community chat. Sponsors receive direct technical support via email. Before submitting a support request, try to determine the cause of your problem.

    "},{"location":"getting-started/troubleshooting/mariadb/#compatibility","title":"Compatibility","text":"

    PhotoPrism is compatible with SQLite 3 and MariaDB 10.5.12+. Official support for MySQL 8 is discontinued as Oracle seems to have stopped shipping new features and enhancements. As a result, the testing effort required before each release is no longer feasible.

    Our configuration examples are generally based on the current stable version to take advantage of performance improvements. This does not mean that older versions are no longer supported and you must upgrade immediately. We recommend not using the :latest tag for the MariaDB Docker image and to upgrade manually by changing the tag once we had a chance to test a new major version, e.g.:

    services:\n  mariadb:\n    image: mariadb:11\n    ...\n
    "},{"location":"getting-started/troubleshooting/mariadb/#cannot-connect","title":"Cannot Connect","text":"

    First, verify that you are using the correct port (default is 3306) and host:

    • in the internal Docker network, the default hostname is mariadb (same as the service)
    • avoid changing the default network configuration, unless you are experienced with this
    • avoid using IP addresses other than 127.0.0.1 (localhost) directly, as they can change
    • only use localhost or 127.0.0.1 if the database port has been exposed as described below and you are on the same computer (host)
    • we recommend configuring a local hostname to access other hosts on your network

    To connect to MariaDB from your host or home network, you need to expose port 3306 in your compose.yaml or docker-compose.yml and restart the service for changes to take effect:

    services:\n  mariadb:\n    ports:\n      - \"3306:3306\"\n

    Set strong passwords if the database is exposed to an external network. Never expose your database to the public Internet in this way, for example, if it is running on a cloud server.

    If this doesn't help, check the Docker Logs for messages like disk full, disk quota exceeded, no space left on device, read-only file system, error creating path, wrong permissions, no route to host, connection failed, exec format error, no matching manifest, and killed:

    • Make sure that the database storage folder is readable and writable: Errors such as \"read-only file system\", \"error creating path\", \"failed to create folder\", \"permission denied\", or \"wrong permissions\" indicate a filesystem permission problem
    • If symbolic links are mounted or used within the storage folder, replace them with the actual paths and verify that they are accessible
    • If the MariaDB service has been \"killed\" or otherwise automatically terminated, this can point to a memory problem (add swap and/or memory; remove or increase usage limits)
    • In case the logs also show \"disk full\", \"quota exceeded\", or \"no space left\" errors, either the disk containing the storage folder is full (add storage) or a disk usage limit is configured (remove or increase it)
    • Log messages that contain \"no route to host\" may also indicate a general network configuration problem (follow our examples)
    • You have to resort to alternative Docker images to run MariaDB on ARMv7-based devices and those with a 32-bit operating system
    • You may find a solution in the official MariaDB Docker Image FAQ
    "},{"location":"getting-started/troubleshooting/mariadb/#wrong-password","title":"Wrong Password","text":"

    If the password you are using was specified in a compose.yaml or docker-compose.yml file and contains one or more $ characters, these must be escaped with $$ (a double dollar sign) so that, for example, \"compo$e\" becomes \"compo$$e\":

    services:\n  mariadb:\n    environment:\n      # sets password to \"compo$e\"\n      MARIADB_PASSWORD: \"compo$$e\"\n

    Also note that you cannot change the database password with MARIADB_PASSWORD after MariaDB has been started for the first time.

    In this case, you can either delete the database storage folder and restart the database service or follow the instructions under Lost Root Password.

    "},{"location":"getting-started/troubleshooting/mariadb/#bad-performance","title":"Bad Performance","text":"

    Many users reporting poor performance and high CPU usage have migrated from SQLite to MariaDB, so their database schema is no longer optimized for performance. For example, MariaDB cannot handle rows with text columns in memory and always uses temporary tables on disk if there are any.

    The instructions for these migrations were provided by a contributor and are not part of the original software distribution. As such, they have not been officially released, recommended, or extensively tested by us.

    If this is the case, please make sure that your migrated database schema matches that of a fresh, non-migrated installation. It may help to run the migrations manually in a terminal using the migrations subcommands. However, this does not guarantee that all issues such as missing indexes are resolved.

    Get Performance Tips \u203a View Database Schema \u203a

    "},{"location":"getting-started/troubleshooting/mariadb/#version-upgrade","title":"Version Upgrade","text":"

    Should MariaDB fail to start after upgrading from an earlier version (or migrating from MySQL), the internal management schema may be outdated. With older versions, it could only be updated manually. However, newer MariaDB Docker images support automatic upgrades on startup, so you don't have to worry about that anymore.

    When upgrading from MariaDB 10.x to 11.0, you must replace command: mysqld with command: (followed by the command flags) in your compose.yaml or docker-compose.yml file, otherwise the database server may fail to start.

    "},{"location":"getting-started/troubleshooting/mariadb/#manual-update","title":"Manual Update","text":"

    To manually upgrade the internal database schema, run this command in a terminal:

    docker compose exec mariadb mariadb-upgrade -uroot -p\n

    Enter the MariaDB \"root\" password specified in your compose.yaml or docker-compose.yml when prompted.

    Alternatively, you can downgrade to the previous version, create a database backup using the photoprism backup command, start a new database instance based on the latest version, and then restore your index with the photoprism restore command.

    "},{"location":"getting-started/troubleshooting/mariadb/#auto-upgrade","title":"Auto Upgrade","text":"

    To enable automatic schema updates, set MARIADB_AUTO_UPGRADE to a non-empty value in your compose.yaml or docker-compose.yml as shown in our config example:

    services:\n  mariadb:\n    image: mariadb:11\n    ...\n    environment:\n      MARIADB_AUTO_UPGRADE: \"1\"\n      MARIADB_INITDB_SKIP_TZINFO: \"1\"\n      ...\n

    Before starting MariaDB in production mode, the database image entrypoint script now runs mariadb-upgrade to update the internal management schema as needed. For example, when you pull a new major release and restart the service.

    Since PhotoPrism does not require time zone support, you can also add MARIADB_INITDB_SKIP_TZINFO to your config as shown above. However, this is only a recommendation and optional.

    "},{"location":"getting-started/troubleshooting/mariadb/#incompatible-schema","title":"Incompatible Schema","text":"

    If your database does not seem to be compatible with the currently installed version of PhotoPrism, for example because search results are missing or incorrect, first make sure you are using a supported database and that its internal management schema is up-to-date. How to do that is explained in the previous section.

    Once you have verified that neither is a problem, you can run the following command in a terminal to check the status of previous database schema migrations:

    docker compose exec photoprism photoprism migrations ls\n

    Omit the docker compose exec photoprism prefix if you are using an interactive terminal session or are running PhotoPrism directly on your computer without Docker.

    "},{"location":"getting-started/troubleshooting/mariadb/#re-run-migrations","title":"Re-Run Migrations","text":"

    Should the status of any migration not be OK, you can re-run failed migrations using this command in a terminal:

    docker compose exec photoprism photoprism migrations run -f\n

    The -f flag instructs the photoprism migrations run subcommand to re-run previously failed migrations. Use --help to see the command help.

    Additional migration command examples can be found in the Developer Guide.

    "},{"location":"getting-started/troubleshooting/mariadb/#complete-rescan","title":"Complete Rescan","text":"

    We recommend that you re-index your pictures after a schema migration, especially if problems persist. You can either start a rescan from the user interface by navigating to Library > Index, checking \"Complete Rescan\", and then clicking \"Start\", or by running this command in a terminal:

    docker compose exec photoprism photoprism index -f\n

    Be careful not to start multiple indexing processes at the same time, as this will lead to a high server load.

    "},{"location":"getting-started/troubleshooting/mariadb/#server-crashes","title":"Server Crashes","text":"

    If the server crashes unexpectedly or your database files get corrupted frequently, it is usually because they are stored on an unreliable device such as a USB flash drive, an SD card, or a shared network folder mounted via NFS or CIFS. These may also have unexpected file size limitations, which is especially problematic for databases that do not split data into smaller files.

    • Never use the same database files with more than one server instance
    • To share a database over a network, run the database server directly on the remote server instead of sharing database files
    • To repair your tables after you have moved the files to a local disk, you can start MariaDB with --innodb-force-recovery=1 (otherwise the same procedure as for recovering a lost password, see above)
    • Make sure you are using the latest Docker version and read the release notes for the database server version you are using
    "},{"location":"getting-started/troubleshooting/mariadb/#invalid-table-errors","title":"Invalid Table Errors","text":"

    If you are using macOS and see errors like Invalid (old?) table or database name '._column_stats', it may be because you are running MariaDB on a file system like ExFAT that does not support extended attributes. In this case, macOS automatically creates these files and MariaDB then reports them as invalid tables (which is technically correct). To remove extended attribute files, you can run the following in a terminal:

    find . -type f -name '._*' -delete\n

    Unless you open the storage folder again in macOS Finder, the errors should then be gone after restarting the database.

    "},{"location":"getting-started/troubleshooting/mariadb/#corrupted-files","title":"Corrupted Files","text":"

    \u21aa Server Crashes

    "},{"location":"getting-started/troubleshooting/mariadb/#lost-root-password","title":"Lost Root Password","text":"

    In case you forgot the MariaDB \"root\" password and the one specified in your configuration does not work, you can start the server with the --skip-grant-tables flag added to the mysqld command in your compose.yaml or docker-compose.yml. This will temporarily give full access to all users after a restart:

    services:\n  mariadb:\n    command: --skip-grant-tables\n

    Restart the mariadb service for changes to take effect:

    docker compose stop mariadb\ndocker compose up -d mariadb\n

    Now open a database console:

    docker compose exec mariadb mysql -uroot\n

    Enter the following commands to change the password for \"root\":

    FLUSH PRIVILEGES;\nALTER USER 'root'@'%' IDENTIFIED BY 'new_password';\nALTER USER 'root'@'localhost' IDENTIFIED BY 'new_password';\nexit\n

    When you are done, remove the --skip-grant-tables flag again to restore the original command and restart the mariadb service as described above.

    "},{"location":"getting-started/troubleshooting/mariadb/#server-relocation","title":"Server Relocation","text":"

    When moving MariaDB to another computer, cloud server, or virtual machine:

    • Move the complete storage folder along with it and preserve the file permissions
    • or restore your index from an SQL dump (backup file)
    • Perform a version upgrade if necessary
    • Make sure that PhotoPrism can access the database on the new host
    • Set strong passwords if the database is exposed to an external network
    • Never expose your database to the public Internet
    "},{"location":"getting-started/troubleshooting/mariadb/#unicode-support","title":"Unicode Support","text":"

    If the logs show \"incorrect string value\" database errors and you are running a custom MariaDB or MySQL server that is not based on our default configuration:

    • Full Unicode support must be enabled, e.g. using the mysqld command parameters --character-set-server=utf8mb4 and --collation-server=utf8mb4_unicode_ci
    • Note that an existing database may use a different character set if you imported it from another server
    • Before submitting a support request, verify the problem still occurs with a newly created database based on our example

    Run this command in a terminal to see the current values of the collation and character set variables (change the root password insecure and database name photoprism as specified in your compose.yaml or docker-compose.yml):

    echo \"SHOW VARIABLES WHERE Variable_name LIKE 'character\\_set\\_%' OR Variable_name LIKE 'collation%';\" | \\\ndocker compose exec -T mariadb mysql -uroot -pinsecure photoprism\n
    "},{"location":"getting-started/troubleshooting/mariadb/#mysql-errors","title":"MySQL Errors","text":"

    Official support for MySQL 8 is discontinued as Oracle seems to have stopped shipping new features and enhancements. As a result, the testing effort required before each release is no longer feasible.

    "},{"location":"getting-started/troubleshooting/metadata/","title":"Checking Image and Video Metadata","text":"

    We recommend checking the file metadata with Exiftool if some of your pictures are displayed incorrectly (stretched, distorted)1, information seems to be missing (e.g. title or description), or the wrong time and location are shown.

    To do this, run the following command within a Docker terminal session or on your host if you have Exiftool installed (-n displays the raw values without changes, -j will format the output as JSON, and -g optionally groups the output by metadata source):

    exiftool -n -j [filename]\n

    If the file specified with [filename] contains readable metadata, it will then be displayed to you as JSON-formatted values, for example:

    [{\n  \"SourceFile\": \"example.jpg\",\n  \"ExifToolVersion\": 12.76,\n  \"FileSize\": 200108,\n  \"FileType\": \"JPEG\",\n  \"MIMEType\": \"image/jpeg\",\n  \"Make\": \"HUAWEI\",\n  \"Model\": \"ELE-L29\",\n  \"Orientation\": 1,\n  \"ExposureTime\": 0.02,\n  \"FNumber\": 1.8,\n  \"ISO\": 100,\n  \"DateTimeOriginal\": \"2020:10:17 17:48:24\",\n  \"CreateDate\": \"2020:10:17 17:48:24\",\n  \"ImageWidth\": 500,\n  \"ImageHeight\": 375,\n  \"Aperture\": 1.8,\n  \"ShutterSpeed\": 0.02,\n  \"SubSecCreateDate\": \"2020:10:17 17:48:24.950488\",\n  \"SubSecDateTimeOriginal\": \"2020:10:17 17:48:24.950488\",\n  \"SubSecModifyDate\": \"2020:10:17 17:48:24.950488\",\n  \"GPSAltitude\": 84.47,\n  \"GPSDateTime\": \"2020:10:17 15:48:23Z\",\n  \"GPSLatitude\": 33.8120962,\n  \"GPSLongitude\": -117.9215491\n}]\n

    This allows you to check e.g. the values for Orientation and Rotation if you have problems with the image orientation.

    When you post the output on GitHub or in our Community Chat, please format it as follows for better readability:

    ```json\n[{\n  \"SourceFile\": \"example.jpg\",\n  \"ExifToolVersion\": 12.76,\n  ...\n}]\n```\n

    Thank you very much!

    "},{"location":"getting-started/troubleshooting/metadata/#exif-orientation","title":"Exif Orientation","text":"

    The numbers used in Exif metadata to specify the image orientation are defined as follows:

    1. = 0 degrees: the correct orientation, no adjustment is required.
    2. = 0 degrees, mirrored: image has been flipped back-to-front.
    3. = 180 degrees: image is upside down.
    4. = 180 degrees, mirrored: image has been flipped back-to-front and is upside down.
    5. = 90 degrees: image has been flipped back-to-front and is on its side.
    6. = 90 degrees, mirrored: image is on its side.
    7. = 270 degrees: image has been flipped back-to-front and is on its far side.
    8. = 270 degrees, mirrored: image is on its far side.

    Learn more \u203a

    "},{"location":"getting-started/troubleshooting/metadata/#installing-exiftool","title":"Installing Exiftool","text":"

    Running the following commands will install Exiftool on Debian or Ubuntu Linux if needed:

    sudo apt update\nsudo apt install -y exiftool\n

    See the Exiftool documentation for how to install it on other operating systems.

    1. If images are displayed in low resolution or slightly distorted, this may also be due to a problem with the thumbnail cache folder or your quality settings.\u00a0\u21a9

    "},{"location":"getting-started/troubleshooting/performance/","title":"Performance Tips","text":""},{"location":"getting-started/troubleshooting/performance/#mariadb","title":"MariaDB","text":"

    The InnoDB buffer pool serves as a cache for data and indexes. It is a key component for optimizing MariaDB performance. Its size should be as large as possible to keep frequently used data in memory and reduce disk I/O - typically the biggest bottleneck.

    By default, the buffer pool size is between 128 MB and 512 MB, depending on which configuration example you use. You can change it with the --innodb-buffer-pool-size command parameter in the mariadb: section of your config file. M stands for Megabyte, G for Gigabyte. Do not use spaces.

    If your server has plenty of physical memory, we recommend increasing the size to 1 or 2 GB:

    services:\n  mariadb:\n    command: --innodb-buffer-pool-size=1G ...\n

    As a rule of thumb, Innodb_buffer_pool_pages_free should never be less than 5% of the total pages. You can run the following SQL statement, for example using the mariadb command in a terminal, to display the number of free pages and other InnoDB-related status information:

    SHOW GLOBAL STATUS LIKE 'Innodb_buffer%';\n

    Advanced users may adjust additional parameters to further improve performance. Tools such as the mysqltuner.pl script can provide helpful recommendations for this.

    Windows and macOS

    If you are using Docker Desktop on Windows or macOS, remember to increase the total memory available for Docker services. Otherwise, they may run out of resources and cannot benefit from a larger cache size. In case PhotoPrism and MariaDB are running in a virtual machine, its memory size should be increased as well. Restart for changes to take effect.

    "},{"location":"getting-started/troubleshooting/performance/#migration-from-sqlite","title":"Migration from SQLite","text":"

    After migrating from SQLite, it is possible that columns do not have exactly the data type they should have or that indexes are missing. This can lead to poor performance. For example, MariaDB cannot process rows with text columns in memory and always uses temporary tables on disk if there are any.

    The instructions for these migrations were provided by a contributor and are not part of the original software distribution. As such, they have not been officially released, recommended, or extensively tested by us.

    If this is the case, please make sure that your migrated database schema matches that of a fresh, non-migrated installation . It may help to run the migrations manually in a terminal using the migrations subcommands. However, this does not guarantee that all issues such as missing indexes are resolved.

    View Database Schema \u203a

    "},{"location":"getting-started/troubleshooting/performance/#windows","title":"Windows","text":"

    Solve Windows-Specific Issues \u203a

    "},{"location":"getting-started/troubleshooting/performance/#storage","title":"Storage","text":"

    Local Solid-State Drives (SSDs) are best for databases of any kind:

    • database performance extremely benefits from high throughput which HDDs can't provide
    • SSDs have more predictable performance and can handle more concurrent requests
    • due to the HDD seek time, HDDs only support 5% of the reads per second of SSDs
    • the cost savings from using slow hard disks are minimal

    Switching to SSDs makes a big difference, especially for write operations and when the read cache is not big enough or can't be used.

    Never store database files on an unreliable device such as a USB flash drive, SD card, or shared network folder. These may also have unexpected file size limitations, which is especially problematic for databases that do not split data into smaller files.

    "},{"location":"getting-started/troubleshooting/performance/#memory","title":"Memory","text":"

    Indexing large photo and video collections benefits from plenty of memory for caching and processing large media files. Ideally, the amount of RAM should match the number of physical CPU cores. If not, reduce the number of workers as explained below.

    Also ensure that your server has at least 4 GB of swap configured and avoid setting a hard memory limit as this can cause unexpected restarts when the indexer temporarily needs more memory to process large files. Indexing RAW images and high-resolution panoramas may require additional swap space and/or physical memory beyond the recommended minimum.

    RAW image conversion and TensorFlow are disabled on systems with 1 GB or less memory. We take no responsibility for instability or performance problems if your device does not meet the requirements.

    "},{"location":"getting-started/troubleshooting/performance/#server-cpu","title":"Server CPU","text":"

    Last but not least, performance can be limited by your server CPU. If you've tried everything else, then only moving your instance to a more powerful device or cloud server may help.

    Be aware that most NAS devices are optimized for minimal power consumption and low production costs. Although their hardware gets faster with each generation, benchmarks show that even 8-year-old standard desktop CPUs like the Intel Core i3-4130 are often many times faster:

    "},{"location":"getting-started/troubleshooting/performance/#legacy-hardware","title":"Legacy Hardware","text":"

    It is a known issue that the user interface and backend operations, especially face recognition, can be slow or even crash on older hardware due to a lack of resources. Like most applications, PhotoPrism has certain requirements and our development process does not include testing on unsupported or unusual hardware.

    In many cases, performance can be improved through optimizations. Since these can prove to be very time-consuming and cost-intensive in practice, users and developers must decide on a case-by-case basis whether this provides sufficient benefit in relation to the costs or whether the use of more powerful hardware is faster and cheaper overall.

    We kindly ask you not to open a problem report on GitHub Issues for poor performance on older hardware until a full cause and feasibility analysis has been performed. GitHub Discussions or any of our other public forums and communities are great places to start a discussion.

    That being said, one of the advantages of open-source software is that users can submit pull requests with performance and other enhancements they would like to see implemented. This will result in a much faster solution than waiting for a core team member to remotely analyze your problem and then provide a fix.

    "},{"location":"getting-started/troubleshooting/performance/#troubleshooting","title":"Troubleshooting","text":"

    If your server runs out of memory, the index is frequently locked, or other system resources are running low:

    • Try reducing the number of workers by setting PHOTOPRISM_WORKERS to a reasonably small value in docker-compose.yml, depending on the CPU performance and number of cores
    • Ensure that your server has at least 4 GB of swap configured and avoid setting a hard memory limit as this can cause unexpected restarts when the indexer temporarily needs more memory to process large files
    • If you are using SQLite, switch to MariaDB, which is better optimized for high concurrency
    • As a last measure, you can disable the use of TensorFlow for image classification and facial recognition

    Other issues? Our troubleshooting checklists help you quickly diagnose and solve them.

    You are welcome to ask for help in our community chat. Sponsors receive direct technical support via email. Before submitting a support request, try to determine the cause of your problem.

    "},{"location":"getting-started/troubleshooting/raspberry-pi/","title":"Troubleshooting Raspberry Pi Problems","text":"

    You are welcome to ask for help in our community chat. Sponsors receive direct technical support via email. Before submitting a support request, try to determine the cause of your problem.

    "},{"location":"getting-started/troubleshooting/raspberry-pi/#hardware-watchdog-initiates-reboot","title":"Hardware Watchdog Initiates Reboot","text":"

    A watchdog timer is an electronic timer that is used to detect and correct computer malfunctions. When activated, it can trigger a reboot when the computer is under heavy load, e.g. when indexing pictures.

    Should your Raspberry Pi fail to reset the timer before it expires, the WDT signal will reboot it. It is disabled by default in the firmware.

    Users can set up \"dtparam\" also known as Device Tree config files for Raspberry Pi's in /boot/config.txt, which will enable the kernel module.

    It is the responsibility of the user to set the parameters of the watchdog daemon correctly.

    A common parameter that users set is the average CPU load for 1, 5, or 15 minutes. The default value for the 1 minute span is 24

    max-load-1 = 24\n

    The average load is the sum of the queue length and the number of jobs currently running on the CPUs. You can use the following commands to view load average statistics:

    uptime, procinfo, w, top\n

    Raspberry Pi users who have the hardware watchdog enabled need to set a more appropriate value for the 1-minute span for the maximum load than the default value. As a workaround, you can log the average workload to a file:

    #!/bin/bash\n\nwhile true; do\necho $(cat /proc/loadavg) >> test_file.log\nsleep 10\ndone\n

    This will write a log with a timestamp every 10 seconds. Run the above script while performing intensive tasks that would normally trigger a reboot, such as tagging faces. With this information, you can now set a new value that is greater than the recorded maximum.

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"getting-started/troubleshooting/sqlite/","title":"Troubleshooting SQLite Problems","text":"

    You are welcome to ask for help in our community chat. Sponsors receive direct technical support via email. Before submitting a support request, try to determine the cause of your problem.

    "},{"location":"getting-started/troubleshooting/sqlite/#bad-performance","title":"Bad Performance","text":"

    If you only have few pictures, concurrent users, and CPU cores, SQLite may seem faster compared to full-featured database servers like MariaDB. This changes as the index grows and the number of concurrent accesses increases. While MariaDB is optimized for high concurrency, SQLite frequently locks its index so that other operations have to wait. In the worst case, this can lead to locking errors and timeouts during indexing - especially in combination with a slow disk or network storage.

    The main advantage of SQLite is that you don't need to run a separate database server. It is therefore well suited for testing and can also be sufficient for small libraries with a few thousand files. If you are looking for scalability and high performance, it is not a good choice.

    Get MariaDB Performance Tips \u203a

    "},{"location":"getting-started/troubleshooting/sqlite/#locking-errors","title":"Locking Errors","text":"

    If you use traditional hard drives instead of SSDs, you will find that PhotoPrism frequently runs into locking issues with SQLite because your CPU is many times faster than the mechanical heads of your disks. To some extent, this may also happen with solid-state drives, but it is much more likely with slow storage.

    You may be able to optimize the behavior and reduce locking errors with SQLite parameters that you can set with the database DSN config option, but ultimately you should use an SSD if you want to keep SQLite or switch to MariaDB. Please note that our team cannot provide support otherwise.

    "},{"location":"getting-started/troubleshooting/sqlite/#server-crashes","title":"Server Crashes","text":"

    If the server crashes unexpectedly or your database files get corrupted frequently, it is usually because they are stored on an unreliable device such as a USB flash drive, an SD card, or a shared network folder mounted via NFS or CIFS. These may also have unexpected file size limitations, which is especially problematic for databases that do not split data into smaller files.

    • Never use the same database files with more than one server instance
    • Use SSDs instead of traditional hard drives, never use network storage
    • Consider using MariaDB instead of SQLite
    "},{"location":"getting-started/troubleshooting/sqlite/#corrupted-files","title":"Corrupted Files","text":"

    \u21aa Server Crashes

    "},{"location":"getting-started/troubleshooting/sqlite/#migrating-to-mariadb","title":"Migrating to MariaDB","text":"

    When migrating from SQLite to MariaDB, e.g. using scripts and instructions from the community, you should note that your database schema may no longer be optimized for performance and indexes may be missing. Also, MariaDB cannot handle rows with \"text\" columns in memory and always uses temporary tables on disk if there are any.

    If this is the case, please make sure that your migrated database schema matches that of a fresh, non-migrated installation . It may help to run the migrations manually in a terminal using the migrations subcommands. However, this does not guarantee that all issues such as missing indexes are resolved.

    Troubleshoot MariaDB Problems \u203a

    "},{"location":"getting-started/troubleshooting/windows/","title":"Solving Windows-Specific Issues","text":""},{"location":"getting-started/troubleshooting/windows/#ntfs-file-system","title":"NTFS File System","text":"

    If you experience poor performance when indexing large libraries stored on NTFS:

    • The I/O bandwidth used to update the Last Access Time can be a significant percentage of the total I/O bandwidth on NTFS volumes with a large number of files or folders (disable updates).1
    • In folders with many files, file names may start to conflict after NTFS uses all of the 8.3 short file names that are similar to the long names. Repeated conflicts between new and existing short names cause NTFS to regenerate the short file name from 6 to 8 times (disable short file names and reduce the number of files per folder).2 3
    • exFat can be faster than NTFS, especially on external SSD drives with a lot of small files.
    • Windows 10 and 11 allow physical disks formatted with the Linux ext4 file system to be mounted directly in WSL 2, which may be an option for some use cases.4
    "},{"location":"getting-started/troubleshooting/windows/#connecting-via-webdav","title":"Connecting via WebDAV","text":"

    If you followed our step-by-step guide and still have trouble connecting via WebDAV:

    • You need to change the basic authentication level (see below)
    • You do not have sufficient user rights (try as admin)
    • You are experiencing a general authentication problem
    • Your instance or reverse proxy uses an invalid HTTPS certificate
    • You are trying to connect to the wrong network or server

    To change the basic authentication level in the Windows registry:

    1. Open the Windows Registry Editor.
    2. Locate the following registry directory: HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\WebClient\\Parameters
    3. Locate the value BasicAuthLevel.
    4. The value data box should be set to 2. If the value is not 2, right click it and then select Modify.
    5. Change the value to 2.
    "},{"location":"getting-started/troubleshooting/windows/#webdav-file-size-limit","title":"WebDAV File Size Limit","text":"

    When uploading or downloading large files (more than 50 MB) on Windows, this error may occur:

    Error 0x800700DF: The file size exceeds the limit allowed and cannot be saved\n

    To allow larger files, you must increase the size limit in the Windows registry:5

    1. Open the Windows Registry Editor.
    2. Locate the following registry directory: HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\WebClient\\Parameters
    3. Locate the value FileSizeLimitInBytes.
    4. Set the value to 4294967295 (in Decimal).
    5. Restart your computer.

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    1. https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc781134(v=ws.10)?redirectedfrom=MSDN#last-access-time \u21a9

    2. https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc781134(v=ws.10)?redirectedfrom=MSDN#how-ntfs-generates-short-file-names \u21a9

    3. https://stackoverflow.com/a/9600126 \u21a9

    4. https://www.bleepingcomputer.com/news/microsoft/windows-10-now-lets-you-mount-linux-ext4-filesystems-in-wsl-2/ \u21a9

    5. https://docs.druva.com/Knowledge_Base/inSync/Troubleshooting/WebDAV_download_fails_with_file_size_exceeds__the_limit_error \u21a9

    "},{"location":"getting-started/vpn/tailscale/","title":"Tailscale VPN","text":"

    Should you experience problems with Tailscale, we recommend that you ask the Tailscale community for advice, as we cannot provide support for third-party software and services.

    1. Open the Tailscale website and Select Use Tailscale button.

    2. Sign up with an email address or using any of your other accounts and install Tailscale on all the relevant devices. Detailed and clear instructions are available to guide your through the process depending on the operating system.

      For example below the instruction for Linux and Android

    3. When the devices have Tailscale installed (and logged in via the same account) they all appear in the overview as in the printscreen below.

    4. Each device gets an IP address allocated (100.xxx.xxx.xxx), which can be used to reach them via the VPN network. This IP address with addition of the port number where Photoprism is run on can be used to reach Photoprism outside the home network.

    "},{"location":"getting-started/vpn/tailscale/#deny-incoming-vpn-traffic-from-cloud-server","title":"Deny incoming vpn traffic from cloud server","text":"

    If you have a Photoprism instance running on a cloud server, you might want to be able to connect from your desktop computer to the cloud server and deny connections from the cloud server. Denying connections from the cloud server is useful if it is compromised.

    That can be achieved by setting up ACL's (Access Control Lists). Read about ACL's and tags in the tailscale documentation.

    This example shows how to:

    • Create two tags \"lan\" and \"cloud\"
    • Create an ACL that allows machines tagged \"lan\" to connect to every machine in the tailnet.
    • Tags a desktop machine with \"lan\" and a cloud server with \"cloud\"

    As the cloud server is not specifically allowed to connect anywhere on the tailnet, it cannot connect to machines tagged \"lan\".

    Step 1

    Go to your tailscale acl admin console.

    Add the two tags and the acl as marked on the screenshot below:

    Step 2

    Go to your tailscale machines admin console.

    Add the \"tag:lan\" and \"tag:cloud\" to your desktop and cloud machines as demonstrated:

    Open the ACL tags dialog window:

    Add/remove tags as needed and click on \"Save\":

    Step 3

    Verify the changes work as intended.

    • Connect to your cloud based PhotoPrism instance and make sure it works. You could try to do this:

      • Create an album
      • Tag some photos
      • Add the tagged photos to the new album
    • ssh into the cloud server from your desktop machine, that should work.

    • From the cloud server, ping your desktop, ssh into it or something else. All of it should fail.

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"license/agpl/","title":"GNU Affero General Public License","text":"

    Version 3, 19 November 2007

    Copyright \u00a9 2007 Free Software Foundation, Inc.

    Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

    "},{"location":"license/agpl/#preamble","title":"Preamble","text":"

    The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software.

    The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users.

    When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.

    Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software.

    A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public.

    The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version.

    An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license.

    The precise terms and conditions for copying, distribution and modification follow.

    "},{"location":"license/agpl/#terms-and-conditions","title":"TERMS AND CONDITIONS","text":""},{"location":"license/agpl/#0-definitions","title":"0. Definitions","text":"

    \u201cThis License\u201d refers to version 3 of the GNU Affero General Public License.

    \u201cCopyright\u201d also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.

    \u201cThe Program\u201d refers to any copyrightable work licensed under this License. Each licensee is addressed as \u201cyou\u201d. \u201cLicensees\u201d and \u201crecipients\u201d may be individuals or organizations.

    To \u201cmodify\u201d a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a \u201cmodified version\u201d of the earlier work or a work \u201cbased on\u201d the earlier work.

    A \u201ccovered work\u201d means either the unmodified Program or a work based on the Program.

    To \u201cpropagate\u201d a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.

    To \u201cconvey\u201d a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.

    An interactive user interface displays \u201cAppropriate Legal Notices\u201d to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.

    "},{"location":"license/agpl/#1-source-code","title":"1. Source Code","text":"

    The \u201csource code\u201d for a work means the preferred form of the work for making modifications to it. \u201cObject code\u201d means any non-source form of a work.

    A \u201cStandard Interface\u201d means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.

    The \u201cSystem Libraries\u201d of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A \u201cMajor Component\u201d, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.

    The \u201cCorresponding Source\u201d for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.

    The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.

    The Corresponding Source for a work in source code form is that same work.

    "},{"location":"license/agpl/#2-basic-permissions","title":"2. Basic Permissions","text":"

    All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.

    You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.

    Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.

    "},{"location":"license/agpl/#3-protecting-users-legal-rights-from-anti-circumvention-law","title":"3. Protecting Users' Legal Rights From Anti-Circumvention Law","text":"

    No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.

    When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.

    "},{"location":"license/agpl/#4-conveying-verbatim-copies","title":"4. Conveying Verbatim Copies","text":"

    You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.

    You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.

    "},{"location":"license/agpl/#5-conveying-modified-source-versions","title":"5. Conveying Modified Source Versions","text":"

    You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:

    • a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
    • b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to \u201ckeep intact all notices\u201d.
    • c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
    • d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.

    A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an \u201caggregate\u201d if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.

    "},{"location":"license/agpl/#6-conveying-non-source-forms","title":"6. Conveying Non-Source Forms","text":"

    You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:

    • a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
    • b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
    • c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
    • d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
    • e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.

    A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.

    A \u201cUser Product\u201d is either (1) a \u201cconsumer product\u201d, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, \u201cnormally used\u201d refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.

    \u201cInstallation Information\u201d for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.

    If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).

    The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.

    Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.

    "},{"location":"license/agpl/#7-additional-terms","title":"7. Additional Terms","text":"

    \u201cAdditional permissions\u201d are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.

    When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.

    Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:

    • a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
    • b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
    • c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
    • d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
    • e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
    • f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.

    All other non-permissive additional terms are considered \u201cfurther restrictions\u201d within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.

    If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.

    Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.

    "},{"location":"license/agpl/#8-termination","title":"8. Termination","text":"

    You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).

    However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

    Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

    Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.

    "},{"location":"license/agpl/#9-acceptance-not-required-for-having-copies","title":"9. Acceptance Not Required for Having Copies","text":"

    You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.

    "},{"location":"license/agpl/#10-automatic-licensing-of-downstream-recipients","title":"10. Automatic Licensing of Downstream Recipients","text":"

    Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.

    An \u201centity transaction\u201d is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.

    You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.

    "},{"location":"license/agpl/#11-patents","title":"11. Patents","text":"

    A \u201ccontributor\u201d is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's \u201ccontributor version\u201d.

    A contributor's \u201cessential patent claims\u201d are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, \u201ccontrol\u201d includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.

    Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.

    In the following three paragraphs, a \u201cpatent license\u201d is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To \u201cgrant\u201d such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.

    If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. \u201cKnowingly relying\u201d means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.

    If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.

    A patent license is \u201cdiscriminatory\u201d if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.

    Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.

    "},{"location":"license/agpl/#12-no-surrender-of-others-freedom","title":"12. No Surrender of Others' Freedom","text":"

    If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.

    "},{"location":"license/agpl/#13-remote-network-interaction-use-with-the-gnu-general-public-license","title":"13. Remote Network Interaction; Use with the GNU General Public License","text":"

    Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph.

    Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License.

    "},{"location":"license/agpl/#14-revised-versions-of-this-license","title":"14. Revised Versions of this License","text":"

    The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

    Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License \u201cor any later version\u201d applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation.

    If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.

    Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.

    "},{"location":"license/agpl/#15-disclaimer-of-warranty","title":"15. Disclaimer of Warranty","text":"

    THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \u201cAS IS\u201d WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

    "},{"location":"license/agpl/#16-limitation-of-liability","title":"16. Limitation of Liability","text":"

    IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

    "},{"location":"license/agpl/#17-interpretation-of-sections-15-and-16","title":"17. Interpretation of Sections 15 and 16","text":"

    If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.

    "},{"location":"license/agpl/#additional-terms","title":"ADDITIONAL TERMS","text":""},{"location":"license/agpl/#18-photoprism-trademark-and-brand-assets","title":"18. PhotoPrism\u00ae Trademark and Brand Assets","text":"

    (a) PhotoPrism\u2019s Brand Assets \u2014 including trademarks, logos, icons, fonts, corporate design, product and service names, and any other brand features and elements, whether registered or unregistered (\u201eBrand Assets\u201c) \u2014 are proprietary assets owned exclusively by PhotoPrism UG (\u201ePhotoPrism\u201c). We reserve the right to object to any use or misuse in any jurisdiction worldwide. Visit photoprism.app/trademark to learn more.

    (b) Contributors, licensees, business partners, and other third parties may never claim ownership of PhotoPrism's Brand Assets or brands confusingly similar to PhotoPrism's Brand Assets in any way, including, without limitation, as a trademark, service mark, company name or designation, domain name, social media profile/handle, or in any other manner.

    (c) You may not include the PhotoPrism trademark in the name of your app, product, or service, whether commercial or non-commercial in nature. This includes online services such as e-commerce, community, blog, information, advertising, and personal home pages, as well as apps, app stores, client apps, or third-party apps that interact with PhotoPrism.

    (d) In the event that any provision is found to be unenforceable by a court or other competent jurisdiction, the remaining portions hereof shall remain in full force and effect.

    END OF TERMS AND CONDITIONS

    "},{"location":"license/apache/","title":"Apache License","text":"

    Version 2.0, January 2004www.apache.org/licenses

    "},{"location":"license/apache/#terms-and-conditions-for-use-reproduction-and-distribution","title":"Terms and Conditions for use, reproduction, and distribution","text":""},{"location":"license/apache/#1-definitions","title":"1. Definitions","text":"

    \u201cLicense\u201d shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.

    \u201cLicensor\u201d shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.

    \u201cLegal Entity\u201d shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \u201ccontrol\u201d means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.

    \u201cYou\u201d (or \u201cYour\u201d) shall mean an individual or Legal Entity exercising permissions granted by this License.

    \u201cSource\u201d form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.

    \u201cObject\u201d form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.

    \u201cWork\u201d shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).

    \u201cDerivative Works\u201d shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.

    \u201cContribution\u201d shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \u201csubmitted\u201d means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \u201cNot a Contribution.\u201d

    \u201cContributor\u201d shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.

    "},{"location":"license/apache/#2-grant-of-copyright-license","title":"2. Grant of Copyright License","text":"

    Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.

    "},{"location":"license/apache/#3-grant-of-patent-license","title":"3. Grant of Patent License","text":"

    Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.

    "},{"location":"license/apache/#4-redistribution","title":"4. Redistribution","text":"

    You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:

    • (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
    • (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
    • (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
    • (d) If the Work includes a \u201cNOTICE\u201d text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.

    You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.

    "},{"location":"license/apache/#5-submission-of-contributions","title":"5. Submission of Contributions","text":"

    Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.

    "},{"location":"license/apache/#6-trademarks","title":"6. Trademarks","text":"

    This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.

    "},{"location":"license/apache/#7-disclaimer-of-warranty","title":"7. Disclaimer of Warranty","text":"

    Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \u201cAS IS\u201d BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.

    "},{"location":"license/apache/#8-limitation-of-liability","title":"8. Limitation of Liability","text":"

    In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.

    "},{"location":"license/apache/#9-accepting-warranty-or-additional-liability","title":"9. Accepting Warranty or Additional Liability","text":"

    While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.

    END OF TERMS AND CONDITIONS

    "},{"location":"license/apache/#appendix-how-to-apply-the-apache-license-to-your-work","title":"APPENDIX: How to apply the Apache License to your work","text":"

    To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets [] replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \u201cprinted page\u201d as the copyright notice for easier identification within third-party archives.

    Copyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n

    Learn more \u203a

    "},{"location":"license/docs/","title":"PhotoPrism\u00ae Documentation License","text":""},{"location":"license/docs/#creative-commons-attribution-noncommercial-sharealike-40-international-public-license","title":"Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License","text":"

    By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License (\"Public License\"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.

    "},{"location":"license/docs/#section-1-definitions","title":"Section 1 \u2013 Definitions.","text":"

    a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.

    b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.

    c. BY-NC-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License.

    d. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.

    e. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.

    f. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.

    g. License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution, NonCommercial, and ShareAlike.

    h. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.

    i. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.

    h. Licensor means the individual(s) or entity(ies) granting rights under this Public License.

    i. NonCommercial means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange.

    j. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.

    k. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.

    l. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.

    "},{"location":"license/docs/#section-2-scope","title":"Section 2 \u2013 Scope.","text":"

    a. License grant.

    1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:

    A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and

    B. produce, reproduce, and Share Adapted Material for NonCommercial purposes only.

    1. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.

    2. Term. The term of this Public License is specified in Section 6(a).

    3. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.

    4. Downstream recipients.

    A. Offer from the Licensor \u2013 Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.

    B. Additional offer from the Licensor \u2013 Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter\u2019s License You apply.

    C. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.

    1. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).

    b. Other rights.

    1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.

    2. Patent and trademark rights are not licensed under this Public License.

    3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes.

    "},{"location":"license/docs/#section-3-license-conditions","title":"Section 3 \u2013 License Conditions.","text":"

    Your exercise of the Licensed Rights is expressly made subject to the following conditions.

    a. Attribution.

    1. If You Share the Licensed Material (including in modified form), You must:

    A. retain the following if it is supplied by the Licensor with the Licensed Material:

    i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);

    ii. a copyright notice;

    iii. a notice that refers to this Public License;

    iv. a notice that refers to the disclaimer of warranties;

    v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;

    B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and

    C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.

    1. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.

    2. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.

    b. ShareAlike.

    In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply.

    1. The Adapter\u2019s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-NC-SA Compatible License.

    2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material.

    3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply.

    "},{"location":"license/docs/#section-4-sui-generis-database-rights","title":"Section 4 \u2013 Sui Generis Database Rights.","text":"

    Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:

    a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only;

    b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and

    c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.

    For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.

    "},{"location":"license/docs/#section-5-disclaimer-of-warranties-and-limitation-of-liability","title":"Section 5 \u2013 Disclaimer of Warranties and Limitation of Liability.","text":"

    a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.

    b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.

    c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.

    "},{"location":"license/docs/#section-6-term-and-termination","title":"Section 6 \u2013 Term and Termination.","text":"

    a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.

    b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:

    1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or

    2. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or

    For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.

    c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.

    d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.

    "},{"location":"license/docs/#section-7-other-terms-and-conditions","title":"Section 7 \u2013 Other Terms and Conditions.","text":"

    a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.

    b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.

    "},{"location":"license/docs/#section-8-interpretation","title":"Section 8 \u2013 Interpretation.","text":"

    a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.

    b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.

    c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.

    d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.

    "},{"location":"user-guide/","title":"User Guide","text":"

    Step-by-step installation instructions for our self-hosted community edition can be found in Getting Started. All you need is a Web browser and Docker to run the server. It is available for Mac, Linux, and Windows. PhotoPrism also runs on PikaPods, DigitalOcean, Raspberry Pi, Portainer, FreeBSD, and many NAS devices.

    Once the initial setup is complete, our First Steps \ud83d\udc63 tutorial guides you through the user interface and settings to ensure your library is indexed according to your individual preferences.

    "},{"location":"user-guide/#photoprism-plus","title":"PhotoPrism\u00ae Plus","text":"

    Our members can activate additional features by logging in with the admin user created during setup and then following the steps described in our activation guide. Thank you for your support, which has been and continues to be essential to the success of the project!

    Compare Memberships \u203a View Membership FAQ \u203a

    We recommend that new users install our free Community Edition before signing up for a membership.

    "},{"location":"user-guide/#getting-support","title":"Getting Support","text":"

    If you need help installing our software at home, you are welcome to post your question in GitHub Discussions or ask in our Community Chat. Common problems can be quickly diagnosed and solved using our Troubleshooting Checklists. Silver, Gold, and Platinum members are also welcome to email us for technical support and advice.

    View Support Options \u203a

    We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app. Contact us or a community member if you need help, it could be a configuration problem, or a misunderstanding in how the software works.

    "},{"location":"user-guide/faq/","title":"Frequently Asked Questions","text":""},{"location":"user-guide/faq/#general","title":"General","text":"Does your software depend on any external services?

    As explained in our Privacy Policy, reverse geocoding and interactive world maps depend on retrieving the necessary information from us and MapTiler AG, headquartered in Switzerland. Both services are provided with a very high level of privacy and confidentiality.

    Your use of these services is fully covered by us. Depending on your usage, this can save you much more than the cost of a PhotoPrism+ Membership, since other providers generally charge usage-based fees and often don't allow you to cache the data they provide, compromising performance and your privacy with unnecessary requests.

    View Privacy Policy \u203a View Compliance FAQ \u203a

    In order to successfully set up your installation and view location details in PhotoPrism, you must allow incoming requests as well as those to our Geocoding API and Docker if you have a firewall installed, and make sure that your Internet connection is working:

    Learn more \u203a

    Why are some features only available to members?

    PhotoPrism is 100% self-funded and independent. Voluntary donations do not cover the cost of a team working full time to provide you with updates, documentation, and support. It is your decision whether you want to sign up to enjoy additional benefits.

    View Membership FAQ \u203a

    What are the advantages of purchasing a commercial license?

    A key difference between the public license and a commercial license agreement is that you get access to additional support and configuration options, as well as the right to customize functionality to your needs without having to publicly disclose your changes. Our Compliance FAQ gives answers to the most frequently asked questions about product compliance and scalability.

    Compare Team Editions \u203a

    When exactly will new features be released?

    Our Project Roadmap shows what tasks are in progress and what features will be implemented next. You may give ideas you like a thumbs-up, so we know what's most popular.

    Be aware that we have a zero-bug policy and do our best to help users when they need support or have other questions. This comes at a price though, as we can't give exact release dates for new features. Our team receives many more requests than can be implemented, so we want to emphasize that we are in no way obligated to implement the features, enhancements, or other changes you request. We do, however, appreciate your feedback and carefully consider all requests.

    Since sustained funding is key to quickly releasing new features, we encourage all users to support our mission by signing up as a member or purchasing a commercial license.

    "},{"location":"user-guide/faq/#membership","title":"Membership","text":"How can I activate my membership?

    To connect a new instance to your membership account, you will need to log in with the super admin user that is automatically created during setup (see your compose.yaml or docker-compose.yml file or the app store documentation), and then follow the steps described in our activation guide.

    View Activation Guide \u203a

    Are there alternatives to a recurring subscription?

    Yes, our Plus members automatically receive a free Lifetime Essentials membership after 24 months. Likewise, Silver members receive a Lifetime Plus membership after 24 months, Gold members after 12 months, and Platinum members after only 6 months.

    If you would like to sign up for a Silver, Gold or Platinum membership, you can do so either directly on our website or on Patreon. In addition, we are working on a Plus Feature Pack that includes just the features without support, so we can offer it to you at a lower price.

    Note that as a lifetime member you will always receive updates and support for your personal use from us, unlike with so-called lifetime licenses, which may only be good until the next major version is released.

    View Membership FAQ \u203a

    What happens if I cancel my membership?

    If you are eligible for a Lifetime Essentials or Plus membership, you can continue to use these features even if you decide to stop supporting us. Otherwise, you can continue to use all the freely available features. In no case will you lose access to your pictures.

    Compare Features \u203a

    "},{"location":"user-guide/faq/#user-interface","title":"User Interface","text":"Can I select multiple pictures at once?

    Yes, this is possible. How it works depends on what kind of device you use.

    Desktop Browser

    Select the first picture by clicking in the lower right corner.

    The user interface is now in selection mode:

    • to additionally select individual pictures, click them anywhere except on the play/view icons in the corner
    • to select multiple pictures at once, use a shift+click to select all pictures between the last selected picture and the one you shift+click

    Mobile Devices

    Select the first picture with a long touch.

    The user interface is now in selection mode:

    • to additionally select individual pictures, touch them anywhere except on the play/view icons in the corner
    • to select multiple pictures at once, use a long touch to select all pictures between the last selected picture and the one you long touch
    Can I use trees for organizing my pictures and albums?

    Except in Library > Originals and for object classification in Labels, PhotoPrism does not support hierarchically organized content for a number of reasons:

    First, there are many tools (including Windows Explorer and Mac OS Finder) that already browse folders in such a way.

    A common UX challenge is dealing with namespaces. For example, the album \"Berlin\" may exist 5 times in different parts of a tree. To avoid ambiguities, simple input fields need to be replaced with a tree browser that shows the complete context. This is especially difficult on mobile screens.

    Personal albums can typically be browsed by time, with optional filters for more specific results. This is different in Enterprise asset management, where trees are required to manage responsibilities & permissions. We might do a special release for professional users later.

    While you have complete freedom with organizing your original files and folders, we don't think trees should be an integral part of our user interface. Most users won't be able to sort their memories in a strictly hierarchical way and prefer to explore them in multiple dimensions instead.

    "},{"location":"user-guide/faq/#maps-places","title":"Maps & Places","text":"Why are some pictures positioned at unvisited locations on the map?

    PhotoPrism can estimate the location of pictures taken without GPS information by extrapolating it from the location of other pictures taken on the same day. These estimates can be disabled in the settings if you don't want them.

    Are the keys for using interactive world maps provided free of charge?

    All users have access to a high-resolution vector map that we host on our own infrastructure, so no commercial API key is required. It is based on data published by OpenStreetMap (OSM).

    In addition, we automatically provide our members and business customers with an API key for MapTiler's commercial service, which includes satellite, outdoor and 3D maps. You can test these on our public demo.

    Learn more \u203a

    Why don't you use the free map tile service provided by OpenStreetMap?

    Other free and open-source software sometimes uses the public maps that OpenStreetMap provides for development and testing. These are not intended for end-user applications like ours.

    Using their service also means that their usage and privacy policies apply, as your request data is stored and used to generate publicly available reports. This differs from our services, which ensure a high level of privacy and provide a better user experience with faster loading times.

    Learn more \u203a

    "},{"location":"user-guide/faq/#media-library","title":"Media Library","text":"What media file types are supported?

    PhotoPrism supports indexing, viewing, and converting most popular image, video and RAW formats, including JPEG, PNG, GIF, BMP, HEIF, HEIC, MP4, MOV, WebP, and WebM. TIFF is partially supported without extensions like GeoTIFF.

    When indexing, a JPEG or PNG sidecar file is automatically created for videos and images in other formats, such as RAW or vector graphics. It is needed for thumbnail generation, image classification, and face detection. JPEG XL support is planned as soon as it is generally available and enough compatible tools exist.

    If installed, converting RAW files is possible with the following converters (our Docker image includes both):

    • Darktable (supported cameras)
    • RawTherapee (supported cameras)

    On a Mac, RAW files can also be converted with Sips (supported cameras). Our goal is to provide top-notch support for all RAW formats, regardless of camera make and model. Please let us know about any issues with a particular camera or file format.

    For maximum browser compatibility, video codecs and containers supported by FFmpeg can be transcoded to MPEG-4 AVC on demand, just as still images can be extracted for thumbnail creation.

    Make sure you have JSON sidecar files enabled if you have videos, live photos, and/or animated GIFs so that video-specific metadata such as codec, frames, and duration can be extracted, indexed, and searched.

    For a complete list of file formats and extensions, see our downloadable Feature Overview.

    What metadata sidecar file types are supported?

    Currently, three types of file formats are supported:

    Why are my video files not indexed?

    In case FFmpeg is disabled or not installed, videos cannot be indexed because still images cannot be created. You should also have ExifTool enabled to extract metadata such as duration, resolution, and codec.

    Some files seem hidden, where are they?

    If the quality filter is enabled, you might find them in Search > Review. Otherwise, their format may not be supported, they may be corrupted, or they may be stacked with other files if their name, exact date & location, or unique image ID indicate they belong to the same photo. You may then unstack them if this happened by mistake e.g. because of bad metadata.

    View Troubleshooting Checklist \u203a

    For what reasons can files be stacked?
    1. Files that share the same file and folder name (except for the file extension) are always stacked, for example /2018/IMG_1234.jpg and /2018/IMG_1234.avi
    2. Files with sequential names like /2018/IMG_1234 (2).jpg and /2018/IMG_1234 (3).jpg can be stacked as well (optional)
    3. File metadata indicates that the pictures were taken at the same position within the same second (optional)
    4. File metadata includes the same Unique Image ID or XMP Instance ID (optional)

    You can change your preferences for 2 - 4 in the Stacks section under Settings > Library.

    Note that it is not possible to disable stacking of files with the same name as this would break important functionality, most notably support for Apple Live Photos (which consist of a photo and a video file), any other multi-file/hybrid formats like RAW/JPEG, and indexing of metadata from XMP/JSON sidecar files.

    Are files automatically unstacked when I change the settings?

    When you change the stacks-related settings under Settings > Library, files that are already stacked will not be unstacked automatically. This is because unstacking is a resource-intensive operation that requires each file to be re-indexed.

    The result also depends on the exact order in which you unstack the files, as non-media sidecar files, for example, remain bound to the remaining media file in a stack. We consider providing a command for this in a future release and appreciate any contributions in this regard.

    If you are new to PhotoPrism and want to re-index your library with different settings, you can run the photoprism reset command in a terminal to reset the index and start from scratch. Learn more \u203a

    I already indexed some files. Why are Folders, Calendar and Moments still empty?

    Folders, Calendar and Moments are populated at the end of the indexing process.

    Why does the count in Search not match the count of files in Originals?

    Library > Originals shows actual files, whereas Search counts unique photos and videos.

    Photos and videos may have more than one file, for example:

    • A raw file + related jpg file + related xmp file = 3 files, 1 photo
    • A mp4 file + related jpg file = 2 files, 1 video

    It is also possible that multiple .jpg files are stacked because they are related to each other.

    When should I perform a complete rescan?

    We recommend performing a complete rescan after major updates to take advantage of new search filters and sorting options. Be sure to read the notes for each release to see what changes have been made and if they might affect your library, for example, because of the file types you have or because new search features have been added. If you encounter problems that you cannot solve otherwise (i.e. before reporting a bug), please also try a rescan and see if it solves the problem.

    You can start a rescan from the user interface by navigating to Library > Index, selecting \"Complete Rescan\", and then clicking \"Start\". Manually entered information such as labels, people, titles or descriptions will not be modified when indexing, even if you perform a \"complete rescan\". Be careful not to start multiple indexing processes at the same time, as this will lead to a high server load.

    Can I use the web interface to permanently delete files?

    Yes, you can permanently delete files.

    In which cases could files in the originals folder get modified?

    PhotoPrism generally does not write to the originals folder, with the following exceptions: (1) You rotate an image in the user interface, so its Exif header must be updated. (2) You unstack files that were stacked based on their name, so they must be renamed. (3) You add files using the import functionality or the web upload. (4) You manually delete files in the user interface. (5) You have configured the originals folder as your sidecar folder. (6) You access the originals folder with a WebDAV client to manage your files without having read-only mode enabled.

    "},{"location":"user-guide/faq/#json","title":"JSON","text":"

    If not disabled via PHOTOPRISM_DISABLE_EXIFTOOL or --disable-exiftool, ExifTool is used to automatically create a JSON sidecar for each media file. In this way, embedded XMP and video metadata can also be indexed. Native metadata extraction is limited to common Exif headers. Note that this causes small amount of overhead when indexing for the first time.

    JSON files can also be useful for debugging, as they contain the full metadata and can be processed with common development tools and text editors.

    Metadata JSON files exported from Google Photos can be read as well. Support for more schemas may be added over time.

    "},{"location":"user-guide/faq/#yaml","title":"YAML","text":"

    Unless disabled by setting the PHOTOPRISM_SIDECAR_YAML option to \"false\" in your configuration, PhotoPrism automatically creates/updates human-friendly YAML sidecar files during indexing and after manual editing of fields such as title, date, or location. They serve as a backup in case the database (index) is lost, or when folders are synchronized with a remote instance.

    Like JSON, YAML files can be opened with common development tools and text editors. However, changes are not synchronized with the original index, as this could overwrite existing data.

    "},{"location":"user-guide/faq/#xmp","title":"XMP","text":"

    XMP (Extensible Metadata Platform) is an XML-based metadata container format developed by Adobe. It provides many more fields (as part of embedded models like Dublin Core) than Exif. This also makes it difficult - if not impossible - to provide full support. Reading title, copyright, artist, and description from XMP sidecar files is implemented as a proof-of-concept, contributions are welcome. Indexing of embedded XMP is only possible via ExifTool, see above.

    "},{"location":"user-guide/faq/#raw-images","title":"RAW Images","text":"What is a RAW image file?

    Professional and semi-professional photographers often keep their originals in a lossless RAW format, close to how they were taken with the physical sensor, rather than in a compressed image format like JPEG, especially if they shoot with a digital SLR camera. Newer mobile phones may also be able to capture images in RAW mode. Our goal is to provide top-notch support for all RAW images, regardless of camera make and model. A full list of file types and extensions can be found in our Knowledge Base.

    Since web browsers generally cannot display RAW image files directly, they must be converted. This is done during import or initial indexing. It can also be triggered manually in a terminal with the photoprism convert command.

    Will JPEGs be updated when the related RAW or XMP files change?

    JPEGs are currently not regenerated when related RAW or XMP files change. RAW files are digital negatives by design. PhotoPrism therefore assumes that their image information is immutable.

    XMP files can affect the appearance, but most of the metadata they contain, such as title and description, does not. Creating JPEGs from RAW files is a time-consuming task, and in most cases would cause a huge, unjustified amount of overhead. In addition, the rendering information in XMP files is not well standardized. For example, changes you make in Photoshop may not be compatible with Darktable.

    We recommend manually updating existing JPEG sidecar files as needed or creating additional JPEGs, so you can choose between different versions. New files and other metadata changes are detected and reflected in the index as usual when your library is scanned.

    Are edits preserved when converting a RAW image with an XMP sidecar file?

    PhotoPrism currently supports Darktable and RawTherapee as RAW image converters. Darktable fully supports XMP sidecar files, RawTherapee might only partially. However, XMP is only a \"container\" format, so the fields (namespaces) used there to indicate how an image should be converted (as well as other metadata) differ between Lightroom/Photoshop, Darktable, and RawTherapee.

    In other words, just because an application generally supports XMP that doesn't mean it can use metadata created with another application or by another vendor like Adobe. If you think that's confusing, well, that's because it is. You have an open format, but you still suffer from vendor lock-in - probably not entirely unintentional on Adobe's part.

    From our experience, some basic edits done with Adobe tools - such as cropping - might be preserved when you convert the same RAW image with other software like Darktable. Advanced edits, such as lens or color corrections, will likely not be applied.

    "},{"location":"user-guide/faq/#live-photos","title":"Live Photos","text":"Why can't I play Live Photos or find stacks when I search for specific images?

    Our search API and user interface perform a file search. This is intentional since \"stacks\" can contain files of different types and properties, such as color.

    For example, there may be color and monochrome versions. Now, when you search for them or sort them by color, the user interface must display individual files. Otherwise, the results showing a color image/video when you filter by monochrome would make no sense.

    Likewise, if you search for filename.mp4.*, you will find only JPEGs without video, because the video file extension is .mp4 without an extra dot at the end.

    We recommend using the path: and/or name: filters with wildcards if searching for individual files limits the search results too much. Most users will want to find all related files so that they can be displayed together, e.g. as live photos consisting of a video and an image.

    You can combine these filters with other filters such as live to ensure that the results include only pictures with a specific media type. Alternatively, you can use the filename: filter with a more permissive wildcard that excludes the file extension.

    "},{"location":"user-guide/faq/#metadata","title":"Metadata","text":"Windows shows different metadata values. Could this be a bug in PhotoPrism?

    We recommend that you use ExifTool to see all metadata fields and values, as Windows has limited functionality.

    It might then become clear why there are differences. For example, it could be that Windows does not support some fields and therefore ignores them, or that the data shown is actually from the file system and not from the files. Should you still believe to have found a bug, please provide us with sample files so that we can reproduce the issue.

    Why is the date of pictures without metadata displayed as Unknown in the search results?

    If there is no date information available in the metadata or the original file names, the file system modification time is used to sort pictures in search results and to create canonical file names for them during import. However, this is usually not the actual date a photo was taken (or a graphic was created by the original author), but only the time you downloaded or copied it. As a result, the date of pictures without a reliable creation date will be displayed as \"Unknown\" until you manually set a date in the edit dialog.

    Why do some pictures have 08/12/2002 as date if they were not taken on that day?

    This is usually caused by a bug in Android that caused photos to be created with an incorrect CreateDate. While the date can easily be changed in the edit dialog, this only updates the index without modifying your originals. To fix the date directly in your image or video files, please use other applications like Photoshop, or ExifTool, and re-index your library.

    Why do some pictures have an odd date like 01/01/1980?

    This may happen in case there was an issue with your camera's settings when the photo was taken. While the date can easily be changed in the edit dialog, this only updates the index without modifying your originals.

    To fix the date directly in your image or video files, please use other applications like Photoshop, or ExifTool, and re-index your library.

    What's the difference between keywords and labels?

    Keywords contain a list of search terms extracted from metadata, file names, and other sources like geodata. Pictures with matching keywords automatically show up in related Labels.

    Although related, keywords and labels serve different purposes:

    • Labels may have parent categories and are primarily used for classification, like \"animal\", \"cat\", or \"boat\". Duplicates and ambiguities should be avoided.

    • Keywords are primarily used for searching. They may include similar terms and translations, like \"kitten\", \"kitty\", and \"cat\".

    How can I check the metadata of images and videos?

    We recommend using Exiftool if some of your pictures are displayed incorrectly (stretched, distorted), information seems to be missing (e.g. title or description), or the wrong time and location are shown.

    Learn more \u203a

    "},{"location":"user-guide/faq/#thumbnails","title":"Thumbnails","text":"Isn't it insecure that thumbnail image URLs work even if you are not logged in?

    Like most commercial image hosting services, we've chosen to use a cookie-free thumbnail API to minimize request latency and avoid unnecessary network traffic. If you were to copy private session cookies and use them in a different browser window, you would have a similar problem, except that they also work for other API endpoints, not just a single image.

    Even if URLs were to become invalid every minute: Digital copies are as good as originals. Once shared and downloaded, such images should be considered \"leaked\" because they are cached and can be re-shared by the recipient at any time, with no sure way to get all copies back. Any form of protection we could provide would essentially be \"snake oil\", could be circumvented, and would have a negative impact on the user experience, such as disabling the browser cache or context menu.

    For the highest level of protection, it is recommended to shield your private server from the public Internet. Always use HTTPS, a VPN and/or ideally TLS client certificates and make sure that only people you trust have access to your instance.

    Visit docs.photoprism.app/developer-guide/media/thumbnails/ to learn more.

    "},{"location":"user-guide/faq/#webdav","title":"WebDAV","text":"Why are files uploaded via WebDAV not indexed/imported immediately?

    PHOTOPRISM_AUTO_INDEX and PHOTOPRISM_AUTO_IMPORT let you specify how long PhotoPrism should wait before indexing or importing newly uploaded files. The default setting is 300 seconds, or 5 minutes. This is a safety mechanism for users with slow uploads to avoid incomplete file sets, for example when uploading pictures with sidecar files. You can therefore reduce the delay if you have a fast connection and usually do not upload stacks of related files such as RAW images with sidecar JPEG and XMP files.

    In some cases, it is also possible that the index is already being updated, so you will have to wait until the process is complete before indexing new files.

    Why do I get an error when trying to add a remote server for syncing?

    When adding a new remote server, PhotoPrism tests a number of common endpoints. Only when that fails, you'll see an error. There may be different reasons for this:

    • you are using HTTPS with an invalid certificate (not signed, outdated, domain doesn't match,...)
    • your server has permission issues, or an otherwise bad configuration. For example, Nextcloud blocks requests if the host doesn't match trusted_domains in its config.php
    • the IP is not reachable from your PhotoPrism instance due to network settings, or a firewall
    • the internal hostname can not be resolved to an IP address
    • it's the wrong host or port
    • username or password are wrong

    Curl is an excellent tool for testing HTTP connections if you don't mind using a terminal:

    curl -X PROPFIND -H \"Depth: 1\" -u user:pass https://example.org/webdav/\n

    To avoid overlooking issues, it's best to run it from the same Docker container, virtual machine, or server environment where PhotoPrism is installed.

    My file sync app fails with \"unable to parse TLS packet headers\" when trying to connect via WebDAV?

    Because of security considerations, some backup tools and file sync apps like FolderSync removed support for non-SSL HTTP communication.

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure HTTPS reverse proxy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted by anyone, including your provider, hackers, and governments. Backup tools and file sync apps may refuse to connect as well.

    "},{"location":"user-guide/first-steps/","title":"First Steps \ud83d\udc63","text":"

    Once the initial setup is complete, there are only two more steps before you can start browsing your pictures:

    1. Configure your library and advanced settings according to your individual preferences.
    2. Choose whether you want to index your originals directly, leaving all file and folder names unchanged, or use the optional import feature, which automatically removes duplicates, gives files a unique name, and sorts them by year and month.

    If you want to use folders that already exist on your computer, make sure you configured them as originals respectively import folders during setup.

    To add new pictures, you can either copy them to the originals or import folder, for example using WebDAV, or upload them using a browser, which will automatically import them once uploaded.

    Then start indexing or importing, depending on which strategy you have chosen.

    Ensure there is enough disk space available for creating thumbnails and verify filesystem permissions before starting to index: Files in the originals folder must be readable, while the storage folder including all subdirectories must be readable and writeable.

    "},{"location":"user-guide/first-steps/#while-indexing-is-in-progress","title":"While indexing is in progress...","text":"

    Your photos and videos will successively become visible in search results and other parts of the user interface. Open the Logs tab in Library to watch the indexer working. The counts in the navigation are constantly updated, so you can follow the progress.

    In case some of your pictures are still missing after indexing has been completed, they might be in Review due to low quality or incomplete metadata. You can turn this and other features off in Settings, depending on your specific use case.

    Of course, you can continue using your favorite tools for processing RAW files, editing metadata, or importing new shots. Go to Library and click Start to update the index after files have been changed, added, or removed. This can also be automated by configuring a schedule for regular indexing using the PHOTOPRISM_INDEX_SCHEDULE config option

    While indexing, JPEG sidecar files may be created for originals in other formats such as RAW and HEIF. This is required for image classification, facial recognition, and for displaying them in a Web browser. Sidecar and thumbnail files will be added to the storage folder, so that your originals folder won't be modified.

    "},{"location":"user-guide/first-steps/#setting-up-your-devices","title":"Setting up Your Devices","text":"

    Finally, once indexing is complete and you're happy with the results, you can configure automatic syncing from your phone and install the Progressive Web App (PWA) on your desktop and mobile home screens as needed.

    "},{"location":"user-guide/navigate/","title":"Navigating the User Interface","text":"

    The user interface for browsing and searching your pictures is based on the following components. Screenshots in this documentation generally show pages in a medium-resolution desktop browser. All pages are fully responsive, so they may look different on mobile devices.

    "},{"location":"user-guide/navigate/#1-main-navigation","title":"1. Main Navigation","text":"

    Located on the left, minimized on mobile devices. Click on the links to switch between different pages like Photos, Albums, Places, or Settings.

    "},{"location":"user-guide/navigate/#2-toolbar","title":"2. Toolbar","text":"

    Located on the top. Find photos or videos by entering search terms like cats and filters like label:cat. A list with possible search filters can be found here.

    "},{"location":"user-guide/navigate/#3-reload-button","title":"3. Reload Button","text":"

    reloads search results without reloading the full page.

    "},{"location":"user-guide/navigate/#4-view-button","title":"4. View Button","text":"

    Click to switch to a different search result view (cards: , mosaic: , or list: ).

    "},{"location":"user-guide/navigate/#5-upload-button","title":"5. Upload Button","text":"

    opens the upload dialog. Available on most pages, unless read-only mode is enabled or upload is disabled in Settings.

    "},{"location":"user-guide/navigate/#6-expanded-toolbar","title":"6. Expanded Toolbar","text":"

    The expanded toolbar contains additional options and search filters for country, year, month, camera, color, and category.

    "},{"location":"user-guide/navigate/#context-menu","title":"Context Menu","text":"

    When photos or videos are selected, the context menu appears in the lower right corner. The number displayed is the count of currently selected items. It also contains the following buttons:

    • Archive photos
    • Add photos to album
    • Download photos
    • Mark photos as private
    • Open edit dialog
    • Share photos

    To unselect all items, click the cross at the top:

    "},{"location":"user-guide/navigate/#selection-mode-and-multi-select","title":"Selection Mode and Multi-Select","text":""},{"location":"user-guide/navigate/#desktop-browser","title":"Desktop Browser","text":"

    Select the first picture by clicking in the lower right corner.

    The user interface is now in selection mode:

    • to additionally select individual pictures, click them anywhere except on the play/view icons in the corner
    • to select multiple pictures at once, use a shift+click to select all pictures between the last selected picture and the one you shift+click
    "},{"location":"user-guide/navigate/#mobile-devices","title":"Mobile Devices","text":"

    Select the first picture with a long touch.

    The user interface is now in selection mode:

    • to additionally select individual pictures, touch them anywhere except on the play/view icons in the corner
    • to select multiple pictures at once, use a long touch to select all pictures between the last selected picture and the one you long touch
    "},{"location":"user-guide/pwa/","title":"Mobile App (PWA)","text":"

    PhotoPrism currently does not include a native app that can be installed through an app store. However, there are many compatible apps, and you can conveniently install our Progressive Web App (PWA) on your desktop or home screen for an almost native app-like experience.

    "},{"location":"user-guide/pwa/#installation-requirements","title":"Installation Requirements","text":"

    The compatibility of our PWA has been tested with Apple Safari and Google Chrome, but other modern browsers like Firefox or Microsoft Edge may generally be compatible as well.

    When self-hosting PhotoPrism, please make sure the site URL is configured correctly. In addition, PWAs must be hosted on a dedicated domain with HTTPS in order to be installed. If that is not possible, you can still choose \"Create Shortcut...\", \"Add to Home Screen...\", or a similarly named action from the browser menu to make the app accessible from your home screen.

    "},{"location":"user-guide/pwa/#step-by-step-instructions","title":"Step-by-Step Instructions","text":"Apple Safari (iOS)Google Chrome (Android)
    1. Open PhotoPrism in Safari
    2. Click

    3. Click Add to Home Screen

    4. Choose a name and click Add

    1. Open PhotoPrism in Chrome
    2. Click

    3. Click Install app

    4. Choose a name and click Add

    The PWA is now installed on the home screen of your device and can be launched from there.

    "},{"location":"user-guide/backups/","title":"Creating Backups","text":"

    At a minimum, a backup of PhotoPrism should include the files in your originals folder and a copy of the index database. We also recommend backing up the storage folder so that you don't need to recreate any thumbnail or sidecar files, and your backup includes the complete configuration.

    The easiest way to create a full backup is to first run the backup command to generate a database dump as shown below. Then back up your originals and storage folders using any standard file backup utility.

    "},{"location":"user-guide/backups/#scheduled-backups","title":"Scheduled Backups","text":"

    By default, PhotoPrism 240523-923ee0cf7 and newer versions automatically create daily database backups for you, with up to 3 copies being retained. The schedule, the type of backups, and the number of backups to be retained can be changed in the configuration.

    "},{"location":"user-guide/backups/#backup-command","title":"Backup Command","text":"

    You can run the following command in a terminal to manually create a new MariaDB or SQLite database backup:

    docker compose exec photoprism photoprism backup -i -f\n

    If you are using Podman on a Red Hat-compatible Linux distribution:

    podman-compose exec photoprism photoprism backup -i -f\n

    By default, a backup is created in storage/backup/mysql/[YYYY-MM-DD].sql. A custom backup base folder can be configured with PHOTOPRISM_BACKUP_PATH

    Omit the -f flag if you do not want to overwrite existing files. You can also specify a custom filename as an argument (or - to write the SQL dump to stdout):

    docker compose exec photoprism photoprism backup -i [filename]\n

    Alternative ways to create SQL dumps from SQLite are shown in our advanced backup guide.

    Note that our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    "},{"location":"user-guide/backups/#important-folders","title":"Important Folders","text":""},{"location":"user-guide/backups/#originals","title":"Originals","text":"

    The originals folder contains your original photo and video files. You can back it up and restore it using any standard file backup program if you have not already set this up.

    "},{"location":"user-guide/backups/#storage","title":"Storage","text":"

    SQLite, config, cache, backup, thumbnail and sidecar files are saved in the storage folder. As with the originals folder, the exact path on your computer depends on your configuration.

    We recommend that you back up this folder as well so that you don't need to recreate the thumbnails and have a complete backup of your configuration. As for the originals folder, you can use any standard file backup utility to do this.

    Depending on the resources available to us, a future version may include additional features so that you do not have to rely on external tools to perform file backups and can use a web interface.

    "},{"location":"user-guide/backups/export/","title":"Metadata Exports","text":"

    Control over your data doesn't end with the ability to create and restore a database backup. PhotoPrism additionally provides you with human-readable YAML files that allow you to view and restore the metadata of your albums and photos, even if you didn't create a regular database backup or have lost it.

    If backups have not been disabled in the Advanced Settings, metadata exports of all your albums and photos are automatically created in your storage folder. They will also be updated when changes are made.

    Be aware that the original data remains in your database. Any changes you make to the files therefore have no effect on it and will not become visible on the user interface, unless your database gets lost and the index is restored from the files.

    "},{"location":"user-guide/backups/export/#album-backups","title":"Album Backups","text":"

    Album backups are created for the following album types: album, folder, state, moment and month. You find those backups inside your storage path in /albums.

    "},{"location":"user-guide/backups/export/#album-backups_1","title":"Album Backups","text":"

    For each album the following metadata is stored in the YAML file:

    • UID, Slug, Type, Title, Location, Category, Description, Sort Order, Country, CreatedAt, UpdatedAt, Photos (UID + date the photo was added to the album)
    "},{"location":"user-guide/backups/export/#folder-backups","title":"Folder Backups","text":"

    For each folder the following metadata is stored in the YAML file:

    • UID, Slug, Type, Title, Location, Category, Description, Filter, Sort order, Country, Year, Month, Day, CreatedAt, UpdatedAt
    "},{"location":"user-guide/backups/export/#month-backups","title":"Month Backups","text":"

    For each month the following metadata is stored in the YAML file:

    • UID, Slug, Type, Title, Location, Category, Description, Filter, Sort Order, Country, Year, Month, CreatedAt, UpdatedAt
    "},{"location":"user-guide/backups/export/#state-backups","title":"State Backups","text":"

    For each state the following metadata is stored in the YAML file:

    • UID, Slug, Type, Title, Location, Category, Description, Filter, Sort Order, Country, CreatedAt, UpdatedAt
    "},{"location":"user-guide/backups/export/#moment-backups","title":"Moment Backups","text":"

    For each moment the following metadata is stored in the YAML file:

    • UID, Slug, Type, Title, Location, Category, Description, Filter, Sort Order, Country, Year, CreatedAt, UpdatedAt
    "},{"location":"user-guide/backups/export/#photo-backups","title":"Photo Backups","text":"

    PhotoPrism creates YAML backup files for each photo in your sidecar path.

    The following metadata is stored:

    • TakenAt + Source, UID, Type, Title + Source, Description + Source, OriginalName, TimeZone, PlaceSrc, Altitude, Lat, Lng, Year, Month, Day, Iso, Exposure, FNumber, FocalLength, Quality, Favorite, Private, Keywords + Source, Notes + Source, Subject + Source, Artist + Source, Copyright + Source, License + Source, CreatedAt, UpdatedAt, EditedAt, DeletedAt (Archived)

    Helpful information can be found on GitHub as well.

    "},{"location":"user-guide/backups/external-storage/","title":"Using External Storage","text":"

    Due to their performance and because they can lose data over time, we do not recommend using conventional SD cards, USB sticks or external USB 2 hard disk drives to store your files, except for backups.

    External Solid-State Drives (SSD) connected via USB 3 are generally reliable and fast enough to keep your originals, database, and storage folders. This way you can, for example, do the indexing on one computer, eject the drive, and then connect it to another computer to browse your pictures.

    Note, though, that database files may not be binary compatible in some cases (e.g. if the version or computer architecture does not match) and could also get corrupted when you disconnect an external drive before all changes have been written to disk. We therefore recommend that you regularly create database backups, so you can easily restore your index if necessary.

    "},{"location":"user-guide/backups/folders/","title":"Folder Overview","text":"

    The following gives you an overview of the most important folders that PhotoPrism uses:

    "},{"location":"user-guide/backups/folders/#originals","title":"Originals","text":"

    The originals folder contains the original photo and video files of your library. PhotoPrism will not move or rename the files in this folder without a user requesting it. Unless read-only mode is enabled, new files can be added using the web upload dialog, the import functionality, or by mounting the folder via WebDAV.

    Its location can be changed by setting the PHOTOPRISM_ORIGINALS_PATH environment variable.

    "},{"location":"user-guide/backups/folders/#import","title":"Import","text":"

    When importing, files are copied or moved from the import folder to the originals folder. In the process, duplicates are automatically skipped, and the imported files are given a unique file name and are sorted by year and month.

    The location of the import folder can be changed by setting the PHOTOPRISM_IMPORT_PATH environment variable.

    "},{"location":"user-guide/backups/folders/#storage","title":"Storage","text":"

    Unless you use a custom configuration, the storage folder is used to keep config, cache, backup, thumbnail, and sidecar files.

    Its location can be changed by setting the PHOTOPRISM_STORAGE_PATH environment variable.

    Learn more \u203a

    We recommend not to configure the storage folder to be inside the originals folder unless the name starts with a . to indicate that it is hidden.

    "},{"location":"user-guide/backups/folders/#cache","title":"Cache","text":"

    The cache folder contains the subdirectories json and thumbnails for storing ExifTool JSON files and thumbnail images.

    Its location can be changed by setting the PHOTOPRISM_CACHE_PATH environment variable.

    "},{"location":"user-guide/backups/folders/#json","title":"JSON","text":"

    Unless you have disabled ExifTool in Settings > Advanced, it may used to create JSON files with the metadata of a file in this directory e.g. when indexing or importing new files.

    "},{"location":"user-guide/backups/folders/#thumbnails","title":"Thumbnails","text":"

    PhotoPrism creates thumbnails in different sizes for each photo. Those are stored in the thumbnails directory. More information on thumbnails can be found here.

    "},{"location":"user-guide/backups/folders/#sidecar","title":"Sidecar","text":"

    The sidecar folder contains YAML backup files for each picture as well as e.g. automatically generated JPEG versions of RAW images. Both can be configured in Settings > Advanced.

    Its location can be changed by setting the PHOTOPRISM_SIDECAR_PATH environment variable.

    "},{"location":"user-guide/backups/folders/#config","title":"Config","text":"

    The config folder contains configuration files and certificates.

    Its location can be changed by setting the PHOTOPRISM_CONFIG_PATH environment variable.

    "},{"location":"user-guide/backups/folders/#backup","title":"Backup","text":"

    The backup folder contains database dumps as well as album backup files and is located in the storage folder by default.

    Its location can be changed by setting the PHOTOPRISM_BACKUP_PATH environment variable.

    "},{"location":"user-guide/backups/folders/#temp","title":"Temp","text":"

    Uploads, downloads, and other temporary files may be temporarily stored in the temp folder.

    Its location can be changed by setting the PHOTOPRISM_TEMP_PATH environment variable.

    "},{"location":"user-guide/backups/folders/#assets","title":"Assets","text":"

    The assets folder contains static resources such as machine learning models, icons, and templates.

    Its location can be changed by setting the PHOTOPRISM_ASSETS_PATH environment variable.

    "},{"location":"user-guide/backups/restore/","title":"Restoring Backups","text":"

    To restore your instance, you must have the files in your originals folder and a copy of the index database. We also recommend having a backup of the storage folder so that you don't need to recreate any thumbnail or sidecar files, and your backup includes the complete configuration:

    • if you have a backup copy of your storage and originals folders, the easiest way is to restore those folders first and then run the restore command in case you are using MariaDB
    • otherwise, you additionally need to perform a complete rescan of your library to recreate missing sidecar and thumbnail files
    • some of the metadata and your albums can also be recovered from YAML sidecar files even if you don't have a copy of the index database, unless you have disabled this feature
    "},{"location":"user-guide/backups/restore/#restore-command","title":"Restore Command","text":"

    To restore the index from an existing MariaDB dump, you can run the following command:

    docker compose exec photoprism photoprism restore -i -f\n

    If you are using Podman on a Red Hat-compatible Linux distribution:

    podman-compose exec photoprism photoprism restore -i -f\n

    This will automatically search the backup folder for the most recent index dump and restore it. A custom backup base folder can be configured with PHOTOPRISM_BACKUP_PATH.

    Omit the -f flag to prevent overwriting an existing index. As with the backup command, you can also specify a specific dump filename as an argument:

    docker compose exec photoprism photoprism restore -i [filename]\n

    When the database is restored, all user accounts and passwords are restored as well. If you have changed your password, you must thus use the old password to sign in.

    Note that our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    "},{"location":"user-guide/library/","title":"Indexing Your Library","text":"

    Most users with an existing library will want to index their originals directly without using the optional import feature, leaving the file and folder names unchanged.

    When importing, files are first transferred from a temporary folder to the originals folder. In the process, duplicates are automatically skipped, and the imported files are given a unique file name and are sorted by year and month.

    Importing is also an efficient way to add files, since PhotoPrism does not need to search your originals folder to find new files.

    Hidden files and folders that start with a ., @, _., or __ like __MACOSX are automatically ignored. Other names to be ignored can be added to a .ppignore file in the originals or import folder it should affect. You can put it either in the main folder or in a subfolder to limit the scope.

    "},{"location":"user-guide/library/#indexing-originals","title":"Indexing Originals","text":"

    Use index if you want to index your photos and videos directly in the originals folder, leaving the file and folder names unchanged.

    Your folder structure in originals might look like this:

    During indexing:

    • files will not be renamed or moved
    • your existing folder structure is preserved, so you can later choose to have your folders appear as albums
    • metadata from your files is read to create captions, titles, and locations for your photos
    • thumbnails and optionally JSON and/or YAML files containing metadata are created

    After indexing, the originals folder has not been changed in any way:

    "},{"location":"user-guide/library/#advantages","title":"Advantages","text":"
    • existing file and folder names remain unchanged.
    • you can search your images by their current name and location
    • indexing is usually faster because no files have to be copied or moved

    You can move media files between the different directories within your originals folder. The indexer detects this and updates the path automatically when running the next time.

    "},{"location":"user-guide/library/#importing-files","title":"Importing Files","text":"

    Importing is more efficient when adding files as you don't need to re-index all originals to find new photos and videos. Uploads will also be treated as import, you can't directly upload to originals (yet).

    Your initial folder structure in import might look like this:

    During import:

    • files are copied or moved from their source directory to the originals folder
    • duplicates are automatically skipped, \"Move\" also deletes them in the source directory as if they were successfully moved
    • imported files are given a unique file name and are sorted by year and month
    • the original file name is indexed as a file property
    • all imported files are indexed, the rest remains in the import folder

    After importing with \"Copy\" (default setting), your folders might look like this::

    After importing with \"Move\" your folders might look like this:

    "},{"location":"user-guide/library/#advantages_1","title":"Advantages","text":"
    • unsupported files stay untouched in the import directory
    • no duplicates in your originals directory

    The original file and folder names are used to extract keywords. For example, when you index a folder with the path \"Vacation/Africa\", all files from this folder will get the keywords \"vacation\" and \"africa\".

    "},{"location":"user-guide/library/#conclusion","title":"Conclusion","text":"

    In case your picture library is not well organized and/or you have many duplicates, you may consider importing your files as this will remove duplicates. Be aware that imported files are given a unique file name and are sorted by year and month.

    Provided you have a well-organized library with meaningful file and folder names, it is best to index your originals directly and leave the file and folder names unchanged.

    "},{"location":"user-guide/library/duplicates/","title":"Duplicate Detection","text":"

    Duplicate files are automatically detected and skipped while indexing your library, so that they only appear once in search results and albums. Their SHA1 checksums and sizes are used for comparison.

    Browsing and deleting duplicates through the web user interface is planned for a future release.

    "},{"location":"user-guide/library/duplicates/#file-import","title":"File Import","text":"

    When importing, files are first copied or moved from a temporary folder to the originals folder. In the process, duplicates are automatically skipped. \"Move\" also deletes them in the source directory as if they were successfully moved.

    "},{"location":"user-guide/library/duplicates/#related-files","title":"Related Files","text":"

    In addition to exact duplicates, there may be similar pictures and related sidecar files (with the same filename) in your library, for example:

    • raw + jpg + xmp
    • jpg + json
    • original + edited version
    • original + compressed version
    • live and timelapse photos

    Depending on your library settings, such files may be automatically grouped into stacks:

    Learn more \u203a Library Settings \u203a

    "},{"location":"user-guide/library/files/","title":"Browsing Files and Folders","text":"

    The Originals section displays all files of your originals directory.

    Clicking on a folder opens it. Clicking on a file opens its edit dialog.

    The context menu allows you to perform the following actions:

    "},{"location":"user-guide/library/files/#download-files","title":"Download Files","text":"
    1. Select files
    2. Open context menu
    3. Click
    "},{"location":"user-guide/library/files/#create-an-album-from-files","title":"Create an Album from Files","text":"
    1. Select files
    2. Open context menu
    3. Click
    4. Select existing album or enter new album name
    5. Click add to album
    "},{"location":"user-guide/library/import/","title":"Importing Files to Originals","text":"

    Most users with an existing library will want to index their originals directly without using the optional import feature, leaving the file and folder names unchanged. Importing first copies or moves from their source directory to the originals folder, which is optional.

    "},{"location":"user-guide/library/import/#manual-import","title":"Manual Import","text":"
    1. Add files to the import folder if not done already

    2. Go to Library using the main navigation, and open the Import tab

    3. Select a sub-folder or keep the default to import all files

    4. Select Move Files if you want imported files to be removed from the import folder

    5. Click on Import

    You may use WebDAV for adding files to the import folder. This is especially helpful if PhotoPrism is running on a remote server.

    Import is not possible in read-only mode because it requires write permissions to the folder of originals.

    "},{"location":"user-guide/library/import/#when-should-move-files-be-selected","title":"When should \"Move Files\" be selected?","text":"

    If you select this option, files that have been moved to the originals folder, or that already exist, will be automatically deleted from the import folder. This way you save disk space if you don't want to keep them as backup or for other reasons.

    "},{"location":"user-guide/library/import/#automatic-import","title":"Automatic Import","text":"

    Automatic imports are disabled by default, as a wrong configuration or unsupported usage can cause files or sets of files to be imported incompletely, e.g. if you are using a slow or unreliable Internet connection, which is of particular concern with large video or RAW files.

    If you enable automatic imports by setting the config option PHOTOPRISM_AUTO_IMPORT to a positive number indicating the safety delay in seconds, an import is automatically triggered after the safety delay when files are added to the import folder via WebDAV.

    Can I use PhotoPrism to sort files into a configurable folder structure?

    You have complete freedom in how you organize your originals. If you don't like the unique names and folders used by the import function, you can resort to external batch renaming tools, for example:

    • ExifTool
    • PhockUp
    • Photo Organizer

    Configurable import folders may be available in a later version. This is because - depending on the specific pattern - appropriate conflict resolution is required and the patterns must be well understood and validated to avoid typos or other misconfigurations that lead to undesired results for which we do not want to be responsible.

    "},{"location":"user-guide/library/metadata/","title":"Metadata Support","text":"

    Original media and sidecar files are scanned for Exif and XMP data, as well as proprietary metadata, including Google Photos JSON. For this, PhotoPrism has a built-in Exif parser, a simple XMP reader and can also use ExifTool to extract metadata in various formats such as Exif, XMP and IPTC:

    View Supported Tags \u203a

    The combined information is then normalized, merged, and enriched with additional information.

    Feel free to submit a feature or pull request for Exif or XMP metadata that is not supported yet.

    "},{"location":"user-guide/library/metadata/#external-changes","title":"External Changes","text":"

    If you update one of these tags with external tools such as ExifTool or Digikam, PhotoPrism reads the changes the next time it indexes the file, provided the file's modification date has been updated.

    "},{"location":"user-guide/library/metadata/#xmp-sidecar-files","title":"XMP Sidecar Files","text":"

    When a field is populated with data from an XMP sidecar file, that data is the only source for the field. This means that keywords from XMP override other keywords from PhotoPrism, such as those derived from colors or folder names.

    "},{"location":"user-guide/library/metadata/#cloud-migration","title":"Cloud Migration","text":"

    PhotoPrism also reads metadata from Google Photos JSON and Apple XMP files:

    Migrate from Google Photos \u203a

    Migrate from Apple Photos \u203a

    "},{"location":"user-guide/library/metadata/#enrichment","title":"Enrichment","text":"

    In addition to reading metadata from your original and sidecar files, PhotoPrism enriches the metadata of your photos with additional information:

    • dates or keywords from folder or filenames
    • keywords derived from image classification, color detection and facial recognition
    • GPS information from location estimates
    • keywords derived from location details
    "},{"location":"user-guide/library/metadata/#export","title":"Export","text":"

    We want you to be able to access your metadata independently of PhotoPrism and its database. That's why the indexer additionally creates human-readable YAML sidecar files that you can open with a text editor or other tools if needed.

    Except for the image orientation, PhotoPrism does not yet offer the ability to write changed metadata back to the original files to avoid possible data loss and conflicts with third-party apps. See GitHub Discussions.

    "},{"location":"user-guide/library/originals/","title":"Indexing Your Originals","text":"

    When using PhotoPrism for the first time, please make sure that the directory containing your photo and video collection has been configured as originals folder, and that the library settings match your individual preferences.

    "},{"location":"user-guide/library/originals/#manual-indexing","title":"Manual Indexing","text":"
    1. Go to Library using the main navigation

    2. Select a sub-folder or keep the default to index all files

    3. Select Complete Rescan to re-index all originals, including already indexed and unchanged files

    4. Press Start to start indexing

    You can use WebDAV-compatible apps, including Microsoft's Windows Explorer and Apple's Finder, to add files to your originals folders from a remote computer or mobile device.

    NSFW

    An NSFW detector can be enabled to automatically mark images with potentially objectionable content as private. Note that this feature is only partially reliable.

    Images that have already been indexed before the NSFW detector is activated will not be scanned by the detector.

    "},{"location":"user-guide/library/originals/#when-should-complete-rescan-be-selected","title":"When should \"Complete Rescan\" be selected?","text":"

    If you select this option, all files in the originals folder will be re-indexed, including already indexed and unchanged files.

    We recommend performing a complete rescan after major updates to take advantage of new search filters and sorting options. Be sure to read the notes for each release to see what changes have been made and if they might affect your library, for example, because of the file types you have or because new search features have been added. If you encounter problems that you cannot solve otherwise (i.e. before reporting a bug), please also try a rescan and see if it solves the problem.

    Manually entered information such as labels, people, titles or descriptions will not be modified when indexing, even if you perform a \"complete rescan\".

    "},{"location":"user-guide/library/originals/#cleanup-option","title":"Cleanup Option","text":"

    Admins can optionally enable the cleanup option to delete unused thumbnails from the cache folder and remove orphaned index entries. If you do this from time to time, it can speed up indexing and reduce storage usage.

    "},{"location":"user-guide/library/originals/#scheduled-and-automatic-indexing","title":"Scheduled and Automatic Indexing","text":"

    PhotoPrism 240523-923ee0cf7 and newer versions can optionally perform scheduled rescans of your library. This feature can be enabled by setting a schedule in your configuration. If you are using an external scheduler, please be careful not to start several indexing processes at the same time, as this not only causes a high server load, but may also lead to unexpected indexing results.

    By default, a library rescan is also triggered automatically after a safety delay of 5 minutes when originals are added or modified via WebDAV. You can change the safety delay or disable this feature through the PHOTOPRISM_AUTO_INDEX config option.

    Be aware that automatic indexing may cause files or sets of files to be incompletely indexed if you are using a slow or unreliable internet connection, which is of particular concern with large video or RAW files.

    "},{"location":"user-guide/library/originals/#ignoring-files-and-folders","title":"Ignoring Files and Folders","text":"

    Hidden files and folders that start with a ., @, _., or __ like __MACOSX will be automatically ignored when indexing your library.

    Other file and folder names that should be ignored can be added to a .ppignore file in the originals or import folder, e.g.:

    # ignore a directory by its name\nfoo\n# ignore all folders starting with a #\n[#]*\n# ignore all files\n*.*\n# ignore all files with gif extension\n*.gif\n# ignore videos which name start with MVI\nMVI_*.MOV\n# or\nMVI_*.*\n

    Ignore files can be placed either in the main directory or in a subfolder to limit their scope, as only matching files in the same directory and any subdirectories will be ignored. To match specific file extensions or other naming patterns, * can be used as a wildcard.

    Note that files and folders that have already been indexed cannot be retroactively removed from the index with a .ppignore file, i.e. they remain indexed and visible in the user interface, even if you later add their name or a matching name pattern.

    If you are a new user and files or folders have already been indexed, it is generally easiest to reset the database and start with a new index by running photoprism reset in a terminal.

    "},{"location":"user-guide/library/upload/","title":"File Upload Using the Web UI","text":"From ToolbarFrom Library
    1. Click in the upper right corner

    2. In case you want to upload the files directly to an album select one

    3. Click Upload

    4. Select files

    5. Click Upload to start uploading and importing the selected files

    1. Go to Library using the main navigation, and open the Import tab

    2. Click Upload

    3. In case you want to upload the files directly to an album select one

    4. Click Upload

    5. Select files

    6. Click Upload to start uploading and importing the selected files

    "},{"location":"user-guide/library/webdav/","title":"WebDAV File Upload","text":"

    WebDAV-compatible apps and clients such as PhotoSync, Microsoft's Windows Explorer, and Apple's Finder can connect directly to PhotoPrism:

    Connect via WebDAV \u203a

    After files have been transferred, you can index or import them as usual. By default, indexing and importing start automatically after a safety delay when files have been uploaded using WebDAV.

    You can disable WebDAV in the advanced settings. Since it requires write permissions and authentication, the built-in WebDAV server is automatically disabled when running in read-only or public mode.

    You can also use WebDAV to download files from your library: Simply connect to http://server-ip:2342/originals/ (local server without HTTPS) or https://yourdomain/originals/ (public server with HTTPS enabled), and then copy the files to a folder on your local device.

    "},{"location":"user-guide/organize/albums/","title":"Albums","text":""},{"location":"user-guide/organize/albums/#create-new-album","title":"Create New Album","text":"
    1. Go to Albums
    2. In the upper right corner click

    3. A new album with name \"Month Year\" is created

    "},{"location":"user-guide/organize/albums/#edit-album-details","title":"Edit Album Details","text":"

    Go to Albums and open the album edit dialog

    From TitleContext MenuAlbum Toolbar

    Click on the album title

    Select album, open context menu and click

    Open album and click in the upper right corner

    Edit album details and click save

    "},{"location":"user-guide/organize/albums/#add-photos-to-album","title":"Add Photos to Album","text":"
    1. Select photos and videos
    2. Click context menu
    3. Click

    4. Select album

    5. Click add to album

    You can select many photos at once using shift.

    "},{"location":"user-guide/organize/albums/#remove-photos-from-album","title":"Remove Photos from Album","text":"
    1. Go to your album
    2. Select photos/videos you want to remove
    3. Click context menu
    4. Click

    "},{"location":"user-guide/organize/archive/","title":"Archive","text":"

    You can move photos and videos you do not want to keep in your collection to Archive. Content that is archived is not deleted but it will not appear in any section apart from Archive.

    "},{"location":"user-guide/organize/archive/#archive-photos","title":"Archive Photos","text":"
    1. Select photos/videos
    2. Click context menu
    3. Click

    "},{"location":"user-guide/organize/archive/#restore-photos-from-archive","title":"Restore Photos from Archive","text":"
    1. Go to Archive
    2. Select photos/videos
    3. Click context menu
    4. Click

    "},{"location":"user-guide/organize/calendar/","title":"Calendar","text":"

    Our Calendar enables you to browse through your photos by year and month.

    The context menu allows you to perform the following actions:

    "},{"location":"user-guide/organize/calendar/#download-months","title":"Download Months","text":"
    1. Select month
    2. Open context menu
    3. Click
    "},{"location":"user-guide/organize/calendar/#create-albums-from-months","title":"Create Albums from Months","text":"
    1. Select month
    2. Open context menu
    3. Click
    4. Select existing album or enter new album name
    5. Click add to album
    "},{"location":"user-guide/organize/delete/","title":"Removing Files Permanently","text":"

    You can permanently delete photo and video files you do not want to keep from your filesystem. Photos and videos must be archived before they can be deleted permanently.

    Before you start, make sure the Delete feature is enabled in Settings.

    "},{"location":"user-guide/organize/delete/#delete-selected-files","title":"Delete Selected Files","text":"
    1. Select the photos and videos your want to delete
    2. Go to Archive
    3. Click context menu
    4. Click
    5. Confirm
    "},{"location":"user-guide/organize/delete/#delete-all-archived-photos","title":"Delete All Archived Photos","text":"
    1. Go to Archive
    2. Click
    3. Click Delete All
    "},{"location":"user-guide/organize/download/","title":"Downloading Files","text":"Using Context MenuFrom Fullscreen ModeFrom Edit Dialog
    1. Select photos
    2. Click on the context menu
    3. Click . Photos will be downloaded in original size

    You can select many photos at once using shift.

    You can configure which files should be downloaded for each photo.

    1. Click on the photo
    2. In fullscreen mode click

    You can configure which files should be downloaded for each photo.

    1. Open the photo's edit dialog
    2. Open the Files Tab
    3. Click Download

    "},{"location":"user-guide/organize/edit/","title":"Viewing and Editing Picture Details","text":"

    When you click on a title in the cards view or in the full screen viewer, you can see all the information related to a picture and perform changes to it if you have permission to do so.

    Cards ViewFull Screen ViewerContext Menu
    1. Click on the title, date/time, or camera details of a picture

    1. Click on in the upper right corner

    1. Select one or multiple pictures
    2. Click on context menu
    3. Click

    "},{"location":"user-guide/organize/edit/#details","title":"Details","text":"

    In the Details tab, you can view and edit general metadata such as title, date, location, camera, lens, description, and copyright:

    Much of this information is automatically recognized and updated while indexing. If you edit these fields, the changed values will be preserved and are not overwritten even when you reindex your library.

    To quickly set new coordinates, you can paste them into Latitude or Longitude if they have the format 48.265684, 7.721380.

    Clicking the Apply button saves the changes you have made, but does not close the dialog, while the Done button saves your changes and closes the dialog.

    When performing a search, text in the Title, Description, and Keywords fields can be found, while Notes are private and will be ignored.

    Geolocation Plugin

    Our community has contributed a browser plugin that allows you to easily change the latitude and longitude of a picture by selecting its location on a map:

    This browser plugin can be installed through the Chrome Web Store.

    "},{"location":"user-guide/organize/edit/#labels","title":"Labels","text":"

    In the Labels tab, you can view, add and edit labels and see whether they have been recognized automatically or added manually.

    "},{"location":"user-guide/organize/edit/#people","title":"People","text":"

    Open the People tab to see whose face has been recognized in the picture and assign names to faces that have not yet been recognized.

    "},{"location":"user-guide/organize/edit/#files","title":"Files","text":"

    The Files tab shows you all the files that belong to a picture. If it is a RAW image, you might for example also see a JPEG version of it and an XMP sidecar file:

    Click on to see additional details such as file size, type, and codec:

    If there is more than one JPEG or PNG file, a button in the file details will allow you to change the primary image displayed as a preview in albums and search results. You can also delete non-primary files or unstack files by clicking on the action buttons.

    "},{"location":"user-guide/organize/folders/","title":"Folders","text":"

    We automatically display all folders of your originals directory in the Folders section. In case you add new files to your originals directory your folders will be updated.

    The context menu allows you to perform the following actions:

    "},{"location":"user-guide/organize/folders/#download-folders","title":"Download Folders","text":"
    1. Select folder
    2. Open context menu
    3. Click
    "},{"location":"user-guide/organize/folders/#create-albums-from-folders","title":"Create Albums from Folders","text":"
    1. Select folder
    2. Open context menu
    3. Click
    4. Select existing album or enter new album name
    5. Click add to album
    "},{"location":"user-guide/organize/labels/","title":"Labels","text":"

    PhotoPrism uses labels to classify images. Other tools use the term tags instead of labels. Labels are set automatically when adding new photos. You can manually add new labels or edit/remove the ones that have been created by us.

    In Labels you find all labels of your photos and videos. You can star labels by clicking . Starred labels will be listed first.

    PhotoPrism also attaches each generated label to a broader group (or category) of labels. For example, there is a general category 'vehicle' which will include labels such as 'cab', 'catamaran', 'lifeboat' and 'bullet train'. These broad label categories cannot be edited, but can be used in a search, in an identical way to all other labels.

    You may wish to see these broad label categories in addition to the usual labels in the Labels pages. Clicking the icon in the upper right-hand corner will switch between turning them on ('Show More') and off ('Show Less'). You can also include these label categories as part of a more complex search filter - the label categories from your photos will appear in the search filter bar as a drop-down selection under the option 'All Categories'.

    "},{"location":"user-guide/organize/labels/#view-all-photos-with-a-label","title":"View all Photos with a Label","text":"
    1. Go to Labels
    2. Click on the label you are interested in

    Alternatively you can use the search field in Photos/Videos. You search for photos with a special label like this: label:dog.

    "},{"location":"user-guide/organize/labels/#add-labels-to-photos","title":"Add Labels to Photos","text":"
    1. Go to the photo edit dialog
    2. Go to Labels tab
    3. Click on the label field in the last row of the label table
    4. Enter a label name
    5. Click on the right side of this row

    "},{"location":"user-guide/organize/labels/#removedelete-labels-from-photos","title":"Remove/Delete Labels from Photos","text":"

    Labels that have been set automatically can be removed. Manually added labels can be deleted.

    1. Go to the photo edit dialog
    2. Go to Labels tab
    3. Click the / button of the label you want to remove/delete

    Removed labels have a confidence of 0% and can be activated again at any time by clicking add.

    You can hide Labels in Settings

    "},{"location":"user-guide/organize/labels/#rename-labels","title":"Rename Labels","text":"
    1. Go to the photo edit dialog of any photo that has the label you want to rename

    2. Go to Labels tab

    3. Click on the label name you want to change
    4. Change the name and click enter

    5. The changes will be applied to all photos with this label after the next indexing

    Be aware this change applies to all photos that have this label.

    "},{"location":"user-guide/organize/labels/#delete-labels","title":"Delete Labels","text":"

    You can permanently delete a label. No file will get a deleted label set during indexing.

    1. Go to Labels
    2. Select the label you want to delete
    3. Open the context menu
    4. Click

    5. Confirm

    In case you want a deleted label to appear again, you need to add it to one photo and then index all files again.

    "},{"location":"user-guide/organize/moments/","title":"Moments","text":"

    PhotoPrism creates moments out of your memories. Moments get constantly updated in case you add new photos.

    Moments can be based on location and time e.g. Germany 2020 or on labels like Nature & Landscape or Pets.

    The context menu allows you to perform the following actions:

    "},{"location":"user-guide/organize/moments/#remove-moments","title":"Remove Moments","text":"
    1. Select moment
    2. Open context menu
    3. Click
    4. Confirm

    Only the moment will be deleted. Your files stay untouched.

    "},{"location":"user-guide/organize/moments/#download-moments","title":"Download Moments","text":"
    1. Select moment
    2. Open context menu
    3. Click
    "},{"location":"user-guide/organize/moments/#create-albums-from-moments","title":"Create Albums from Moments","text":"
    1. Select moment
    2. Open context menu
    3. Click
    4. Select existing album or enter new album name
    5. Click add to album
    "},{"location":"user-guide/organize/panoramas/","title":"Panoramas","text":"

    PhotoPrism automatically marks photos with an aspect ratio of 2/1 or higher as panorama. You can view all your panorama images in the Panorama section.

    "},{"location":"user-guide/organize/panoramas/#edit-panorama-flags","title":"Edit Panorama Flags","text":"
    1. Open the photo edit dialog
    2. Click
    3. Set or unset the panorama flag
    "},{"location":"user-guide/organize/people/","title":"Face Recognition","text":"

    Our latest version includes facial recognition that lets you find pictures of your family and friends. Be ready to discover long forgotten shots! New faces are detected as you scan your library. They are then grouped by similarity, so you can quickly match them to people.

    Recognition does not start until your library has been fully scanned. Searching and updating faces temporarily causes a high CPU load and may take a while, depending on your hardware and the number of images you have.

    Existing clusters are automatically optimized in the background, for example, when new faces are detected, you have reported a bad match, or new files are added to your library.

    "},{"location":"user-guide/organize/people/#recognized-new-people","title":"Recognized & New People","text":"

    The people section shows you recognized people as well as new face clusters.

    To star a person click . Starred persons appear first.

    "},{"location":"user-guide/organize/people/#why-doesnt-the-new-faces-page-show-all-faces","title":"Why doesn't the New Faces page show all faces?","text":"

    The 'New Faces' page only shows automatically recognized face clusters, as there may be thousands of unknown faces in your library, including random movie actors or faces on shampoo bottles.

    You can use the face:new search filter to find images with unknown people. We recommend combining this filter with other filters like year or location when searching for specific people. The People tab in the photo edit dialog shows all faces, so you can name them or report a bad match by pressing the button.

    "},{"location":"user-guide/organize/people/#when-a-face-was-not-detected","title":"When a face was not detected...","text":"

    There can be several reasons why a face was not detected:

    • You may need to wait until indexing is complete, as face recognition will not begin until your library has been scanned
    • Only the primary file in stacks will be searched for faces
    • Faces can be smaller than the minimum size configured
    • Our face detection did not scan the image thoroughly enough
    • Reducing the resolution or quality of generated thumbnails negatively impacts face detection and recognition results, just like when you cannot see properly
    • Contrast plays a major role, so a bright face with gray hair on a gray background may be less obvious to our face detection than it is to you
    • In very rare cases, an actual face may be considered a false positive and thus be ignored

    Recognition compares the similarity of faces. The similarity threshold for a face is reduced when you report a bad match.

    "},{"location":"user-guide/organize/people/#assign-names-to-faces","title":"Assign Names to Faces","text":"From PeopleFrom Photo Edit dialog
    1. Go to People
    2. Go to New
    3. Click on the input field
    4. Start typing a name
    5. Press enter

    1. Open the photo edit dialog
    2. Go to the People tab
    3. Click on the input field
    4. Start typing a name
    5. Press enter

    The person you just added will appear under Recognized

    "},{"location":"user-guide/organize/people/#hiding-people","title":"Hiding People","text":"

    You can hide a person in the Recognized section by clicking in the upper right corner. Pictures of this person continue to be visible in search results and albums.

    To see all people including hidden ones click .

    Hidden people can be recovered by clicking

    "},{"location":"user-guide/organize/people/#hiding-faces","title":"Hiding Faces","text":"

    You can hide face clusters from the New section, in the same way you hide people from the Recognized section.

    "},{"location":"user-guide/organize/people/#view-all-photos-of-a-person","title":"View all Photos of a Person","text":"From PeopleFrom Search
    1. Go to People
    2. Go to Recognized
    3. Click on the person you want to view

    1. Go to Search
    2. Search for person:\"jane-doe\"

    "},{"location":"user-guide/organize/people/#rename-people","title":"Rename People","text":"

    To rename all photos of a person:

    1. Go to People
    2. Go to Recognized
    3. Click on the persons name
    4. Type in a new name
    5. Press enter

    "},{"location":"user-guide/organize/people/#change-people-assignments","title":"Change People Assignments","text":"

    You may report bad matches by pressing the button underneath a face in the People tab. This will remove the name. You can either leave it blank or enter the name of a different person.

    When you reject a match, the corresponding face cluster will be updated in the background so that similar issues can be resolved automatically.

    1. Open the photo edit dialog
    2. Go to the People tab
    3. Click
    4. Then enter a new name or leave it empty

    "},{"location":"user-guide/organize/people/#remove-faces","title":"Remove Faces","text":"

    In case PhotoPrism detected something wrong as face (false positives), or in case you just don't want to keep a face on the people tab you're not interested in, you can remove it.

    1. Open the photo edit dialogue
    2. Go to the People tab
    3. Click

    You might undo this action before a reload.

    "},{"location":"user-guide/organize/people/#download-all-photos-of-a-person","title":"Download all Photos of a Person","text":"
    1. Go to People
    2. Select a person
    3. Open context menu
    4. Click
    "},{"location":"user-guide/organize/people/#create-albums-from-people","title":"Create Albums from People","text":"
    1. Go to People
    2. Select a person
    3. Open context menu
    4. Click
    5. Select existing album or enter new album name
    6. Click add to album
    "},{"location":"user-guide/organize/people/#search","title":"Search","text":"

    You can find photos with people on it using the following queries:

    • people, faces or faces:true will result in all photos with people
    • faces:false will show all photos without people
    • faces:3 will show all photos with at least 3 people on it
    • person:\"John Doe\" or subject:\"John Doe\" will show all photos of the person with the exact name John Doe
    • people:\"John\" or subjects:\"John\" will show all photos of people with a name like John e.g. John Doe and John Smith

    The person/subject and people/subjects filters can be used with & and | (see search for more details). Filters may be combined.

    person:\"John Doe&Jane Doe\" faces:3 will show all photos with John and Jane Doe and one other person.

    "},{"location":"user-guide/organize/people/#known-issues","title":"Known Issues","text":"

    For all known issues, see Getting Started > Known Issues > Face Recognition.

    "},{"location":"user-guide/organize/people/#legacy-hardware","title":"Legacy Hardware","text":"

    Face recognition can be slow (or even crash) on old devices due to insufficient resources.

    Like most applications, PhotoPrism has certain requirements and our development process does not include testing on unsupported or unusual hardware.

    "},{"location":"user-guide/organize/people/#asian-faces-and-children","title":"Asian Faces and Children","text":"

    It is a known issue that children and Asian-looking faces cannot be recognized reliably. Detection without automatic recognition should not be affected by that.

    This is because the model we use was trained with North American images, which unfortunately do not include many Asians. The absence of children in the training data comes from the fact that parents do not usually share such images under a public license (and may not have the right to do so).

    We will continue to improve our models over time as our resources allow.

    "},{"location":"user-guide/organize/people/#background-worker","title":"Background Worker","text":"

    Face recognition was developed and tested under the assumption that the background worker runs every 15 minutes, unless the backend is busy with other tasks like indexing. It has not been tested with much longer intervals and is not designed for that.

    PhotoPrism's background worker groups new faces by similarity, compares faces with clusters, and optimizes existing clusters as needed. Without these routine tasks, the number of faces to be processed becomes too large. The first and next time the worker runs, it can then cause a heavy server load until all the faces, face clusters, and related pictures have been updated. The longer you wait, the more CPU is required and the longer it takes.

    An important reason for the worker to run independently of actual changes in the main instance is that some users change the database content directly or run additional instances, for example for indexing. It is a problem that can be solved, but it takes time. If we were to ignore this and don't run the worker at all times, it could lead to many additional support requests, further reducing the amount of time we can spend on development.

    The handling of changes in multiple instances will be improved over time so that the worker can be run less frequently in future releases.

    Upcoming Features

    • manual tagging of faces
    • importing of XMP face tags
    • excluding people when browsing your pictures
    • automatic backup of tagged people in YAML files
    "},{"location":"user-guide/organize/places/","title":"Places","text":"

    Places displays all photos with GPS information on a world map.

    You can set a default map style in settings or choose between different styles by clicking .

    Clicking on a cluster, opens the cluster overlay.

    To open photos from this location in the search click .

    To clear the location filter click .

    "},{"location":"user-guide/organize/places/#search","title":"Search","text":"

    When using the search only photos matching the search term are shown on the map. You can use most of our search filters on the map as well.

    "},{"location":"user-guide/organize/places/#enable-terrain-mode","title":"Enable Terrain Mode","text":"

    Our \"Satellite\", \"Outdoor\" and \"Topography\" maps can also be viewed in 3D.

    To enable terrain mode click . To change the perspective, you can hold down the right mouse button and move it.

    "},{"location":"user-guide/organize/places/#open-photo-from-search-in-places","title":"Open Photo from Search in Places","text":"

    To navigate directly from the cards results view to the location of a picture on the world map, click on its location.

    "},{"location":"user-guide/organize/private/","title":"Hiding Private Photos","text":""},{"location":"user-guide/organize/private/#what-does-private-mean","title":"What does private mean?","text":"

    Some of your photos might be private for personal reasons. Our private functionality provides you with a solution to hide private photos or videos from some sections. This way you can let family and friends browse through your photos without risking that they see photos you do not want them to see.

    By default, photos marked as private will not appear in the following sections:

    • Search
    • Videos
    • People
    • Favorites
    • Places
    • Labels
    • Autogenerated Albums (Moments, Calendar, States, Folders)
    • Shared Albums

    Private photos will be displayed in the private section, in user generated albums and within the file browser.

    In case you want private content to appear everywhere you can configure that in Settings.

    "},{"location":"user-guide/organize/private/#toggle-private-flag","title":"Toggle Private Flag","text":"Using Context MenuFrom List View
    1. Go to Search
    2. Select photos/videos
    3. Click context menu
    4. Click

    1. Go to Search
    2. Make sure you are in list view
    3. Click or on the right

    "},{"location":"user-guide/organize/review/","title":"Reviewing Non-Photographic and Low-Quality Images","text":"

    When adding new photos a quality score from 1 to 5 is created automatically. Photos with a quality score lower than 3 do not appear in Search until you approve them (unless the quality filter was disabled in Settings)

    The quality score depends on the following:

    • Known date and/or GPS coordinates
    • At least 2 MP resolution if taken after 2012
    • Photo not classified as info or screenshot
    • Photo is favorite, was edited or approved

    In case you do not need the review mechanism you can turn it off in Settings

    "},{"location":"user-guide/organize/review/#approve-photos","title":"Approve Photos","text":"Context MenuCards ViewEdit Dialog
    1. Go to Review
    2. Select photos and open the context menu
    3. Click

    1. Go to Review and make sure you are in cards view
    2. Click

    1. Open the photo's edit dialog
    2. Click approve

    The quality score is constantly updated. If you add date or location information to a photo or like it, the quality score increases automatically. In case the new score is equal or greater than 3 the photo is approved automatically.

    "},{"location":"user-guide/organize/rotate/","title":"Rotate Image","text":"

    PhotoPrism allows you to rotate JPG and PNG images.

    1. Open the photo edit dialog
    2. Go to the Files tab
    3. Click on the orientation icon and choose the correct orientation

    The updated orientation is stored in the metadata of your original file. Therefore, it is not possible to rotate images in read-only mode at this time.

    "},{"location":"user-guide/organize/scans/","title":"Scans","text":"

    You can mark scanned photos as scans within the photo edit dialog.

    We aim to automatically mark scans in the future.

    "},{"location":"user-guide/organize/scans/#set-scan-flags","title":"Set Scan Flags","text":"
    1. Open the photo edit dialog
    2. Click
    3. Set or unset the scan flag
    "},{"location":"user-guide/organize/stacks/","title":"Stacks","text":"

    Stacks are groups of files that have the same origin but differ in quality, format, size, or color. Go to Settings > Library to change the stacks-related settings for your library.

    To see all images with a group of related files, open Stacks in the expanded Search navigation:

    Since videos and Live Photos are always stacked with a still image, they are not included in the search results when you navigate to Search > Stacks.

    "},{"location":"user-guide/organize/stacks/#for-what-reasons-can-files-be-stacked","title":"For what reasons can files be stacked?","text":"
    1. Files that share the same file and folder name (except for the file extension) are always stacked, for example /2018/IMG_1234.jpg and /2018/IMG_1234.avi
    2. Files with sequential names like /2018/IMG_1234 (2).jpg and /2018/IMG_1234 (3).jpg can be stacked as well (optional)
    3. File metadata indicates that the pictures were taken at the same position within the same second (optional)
    4. File metadata includes the same Unique Image ID or XMP Instance ID (optional)

    You can change your preferences for 2 - 4 in the Stacks section under Settings > Library.

    Note that it is not possible to disable stacking of files with the same name as this would break important functionality, most notably support for Apple Live Photos (which consist of a photo and a video file), any other multi-file/hybrid formats like RAW/JPEG, and indexing of metadata from XMP/JSON sidecar files.

    "},{"location":"user-guide/organize/stacks/#are-files-automatically-unstacked-when-i-change-the-settings","title":"Are files automatically unstacked when I change the settings?","text":"

    When you change the stacks-related settings under Settings > Library, files that are already stacked will not be unstacked automatically. This is because unstacking is a resource-intensive operation that requires each file to be re-indexed.

    The result also depends on the exact order in which you unstack the files, as non-media sidecar files, for example, remain bound to the remaining media file in a stack. We consider providing a command for this in a future release and appreciate any contributions in this regard.

    If you are new to PhotoPrism and want to re-index your library with different settings, you can run the photoprism reset command in a terminal to reset the index and start from scratch. Learn more \u203a

    "},{"location":"user-guide/organize/stacks/#which-sequential-naming-patterns-are-supported","title":"Which sequential naming patterns are supported?","text":"

    If stacking by Sequential Name has been enabled, files with e.g. the following names would be stacked with the file /2018/IMG_1234.jpg:

    • /2018/IMG_1234 (2).jpg /2018/IMG_1234 (3).jpg
    • /2018/IMG_1234 copy.jpg /2018/IMG_1234 copy 1.jpg /2018/IMG_1234 copy 2.jpg
    • /2018/IMG_1234 (-2.7) /2018/IMG_1234 (+3.3).jpg /2018/IMG_1234(-2.7).jpg /2018/IMG_1234(+3.3).jpg
    "},{"location":"user-guide/organize/stacks/#browse-related-files","title":"Browse Related Files","text":"
    1. Click on

    2. Use arrows to see all photos of the sequence

    "},{"location":"user-guide/organize/stacks/#change-primary-files","title":"Change Primary Files","text":"

    The JPEG file marked as primary is used in our views. It is listed first in the files tab.

    To change the primary file:

    1. Open the photos edit dialog

    2. Open Files tab

    3. Click of the file you want to set as primary

    4. Click primary

    "},{"location":"user-guide/organize/stacks/#unstack-files","title":"Unstack Files","text":"
    1. Open the photos edit dialog

    2. Open Files tab

    3. Click of the JPEG file that is not marked as primary

    4. Click unstack

    Now, both files appear in our views.

    "},{"location":"user-guide/organize/stacks/#delete-non-primary-files","title":"Delete Non-Primary Files","text":"
    1. Open the photos edit dialog

    2. Open Files tab

    3. Click of the JPEG file that is not marked as primary

    4. Click delete

    5. Confirm

    "},{"location":"user-guide/organize/video/","title":"Browsing and Playing Videos","text":"

    Navigate to Videos to browse all your videos. To play a video, click .

    Please note that not all video and audio formats can be played with every browser. For example, AAC - the default audio codec for MPEG-4 AVC / H.264 - is supported natively in Chrome, Safari, and Edge, while it is only optionally supported by the OS in Firefox and Opera.

    In case FFmpeg is disabled or not installed, videos cannot be indexed because still images cannot be created. You should also have ExifTool enabled to extract metadata such as duration, resolution, and codec.

    "},{"location":"user-guide/organize/video/#live-photos","title":"Live Photos","text":"

    Short videos up to 3 seconds are categorized and displayed as Live Photos, regardless of your phone's make and model. You can recognize this by the icon that appears in the upper left corner.

    Move the mouse cursor over the thumbnail to play the video without changing the view. You can limit a search to Live Photos by using the type:live filter or the keyword live.

    "},{"location":"user-guide/organize/video/#transcoding","title":"Transcoding","text":"

    For maximum browser compatibility, PhotoPrism can transcode video codecs and containers supported by FFmpeg to MPEG-4 AVC, as well as extract still images for thumbnail creation:

    • if FFmpeg is disabled or not installed, indexing and importing videos is not possible because still images cannot be created
    • if ExifTool is disabled or not installed, indexing and importing videos is only partially possible because the video metadata cannot be extracted and thus the duration, resolution, and codec are unknown
    • MPEG-4 AVC videos can be played natively by most modern browsers and are not re-encoded, even if they exceed the configured bitrate limit; to reduce the size of AVC videos, you can manually replace the original files with a smaller version or wait for a future release that offers this functionality
    • OGV, VP8, VP9, AV1, WebM, and HEVC videos will be streamed directly if they are supported by your browser and do not exceed the configured bitrate limit
    • other formats must always be transcoded

    If necessary, videos are transcoded on demand. This can cause unacceptable delays when large video files are played for the first time.

    In this case, you can run the following command in a terminal to pre-transcode all video files as needed:

    docker compose exec photoprism photoprism convert\n

    Our setup guide for advanced users explains how to configure hardware video transcoding.

    Make sure that there is enough disk space available on your server before transcoding all video files, as this may require a significant amount of extra storage.

    HEVC/H.265 video files can have a .mp4 file extension too, which is often associated with AVC only. This is because MP4 is a container format, meaning that the actual video content may be compressed with H.264, H.265, or something else. The file extension doesn't really tell you anything other than that it's probably a video file.

    "},{"location":"user-guide/search/","title":"Browsing and Searching Your Library","text":"

    PhotoPrism offers many views and search filters so you can explore your photo collection in multiple dimensions instead of just scrolling through it day by day.

    This helps you rediscover long-forgotten shots, find specific pictures, or quickly create albums based on search results.

    "},{"location":"user-guide/search/#views-and-filters","title":"Views and Filters","text":"

    Using the main navigation you can visit the different sections of your photo library:

    "},{"location":"user-guide/search/#search","title":"Search","text":"

    Shows all photos and videos that are not in review or archived or private.

    In case the review, private or archive functions are turned off - Search displays those photos and videos as well.

    "},{"location":"user-guide/search/#monochrome","title":"Monochrome","text":"

    Shows all monochrome photos and videos.

    "},{"location":"user-guide/search/#panoramas","title":"Panoramas","text":"

    Shows all panorama photos.

    "},{"location":"user-guide/search/#stacks","title":"Stacks","text":"

    Shows all stacked photos.

    "},{"location":"user-guide/search/#scans","title":"Scans","text":"

    Shows all scans.

    "},{"location":"user-guide/search/#review","title":"Review","text":"

    Shows all photos that are in review.

    "},{"location":"user-guide/search/#archive","title":"Archive","text":"

    Shows archived photos.

    "},{"location":"user-guide/search/#albums","title":"Albums","text":"

    Shows albums you created.

    "},{"location":"user-guide/search/#unsorted","title":"Unsorted","text":"

    Shows all photos that are not part of an album.

    "},{"location":"user-guide/search/#videos","title":"Videos","text":"

    Shows videos that are not in review or archived or private.

    "},{"location":"user-guide/search/#live-photos","title":"Live Photos","text":"

    Shows all short videos up to 3 seconds.

    "},{"location":"user-guide/search/#people","title":"People","text":"

    Shows photos and videos grouped by people on it.

    "},{"location":"user-guide/search/#favorites","title":"Favorites","text":"

    Shows all photos and videos you liked.

    "},{"location":"user-guide/search/#moments","title":"Moments","text":"

    Discover albums we automatically create for you.

    "},{"location":"user-guide/search/#calendar","title":"Calendar","text":"

    Organizes your photos due to time taken.

    "},{"location":"user-guide/search/#places","title":"Places","text":"

    Displays all photos and videos with location information on a worldmap.

    "},{"location":"user-guide/search/#states","title":"States","text":"

    Shows your photos grouped by location.

    "},{"location":"user-guide/search/#labels","title":"Labels","text":"

    Shows your photos and videos grouped by labels like cat, dog or beach.

    "},{"location":"user-guide/search/#folders","title":"Folders","text":"

    Displays all folders of your originals directory.

    "},{"location":"user-guide/search/#private","title":"Private","text":"

    Shows photos and videos marked as private.

    "},{"location":"user-guide/search/#originals","title":"Originals","text":"

    Hierarchical view of your originals directory.

    "},{"location":"user-guide/search/filters/","title":"Using Search Filters","text":"

    Powerful search filters let you easily find specific photos and videos, for example:

    • Persons visible on a picture
    • Objects that are displayed on a picture
    • The main color of a picture
    • The file or folder name of a picture
    • Location where a picture has been taken
    • Other metadata such as camera, lens, chroma...

    Just give it a try!

    "},{"location":"user-guide/search/filters/#introduction","title":"Introduction","text":"

    The following filters can be set via dropdowns in the search toolbar:

    • Country, Year, Month, Order, Camera, Color, Category.

    If you set multiple filters, only pictures that meet all filter criteria will be displayed in the search result. Filters can generally be combined unless they contradict each other.

    In addition, these and many other filters can be entered into the toolbar search box as follows:

    label:cat color:green type:live\n

    A complete overview of the available search filters can be found below.

    "},{"location":"user-guide/search/filters/#and-search","title":"AND Search","text":"

    To combine different filters use a space as separator:

    mono:true review:false\n

    The search result shows pictures that are monochrome and not in review.

    Additionally some filters can be combined with & as follows:

    keywords:buffalo&water\n

    or:

    keywords:\"buffalo & water\"\n

    This query will show all photos that have the keywords water and buffalo.

    & is supported by the following filters:

    • albums, keywords, subject/person, subjects/people.

    The label filter does not support &. You can use the keywords filter instead, as all labels are keywords as well.

    "},{"location":"user-guide/search/filters/#or-search","title":"OR Search","text":"

    An OR search is possible using |:

    label:cat|dog\n

    This will show all photos that have either the label cat or dog.

    The following filters work with |:

    • albums, color, country, state, city, day, month, year, keywords, label, path, subject/person, subjects/people, title, type, name, filename, original, hash
    "},{"location":"user-guide/search/filters/#wildcard","title":"Wildcard","text":"

    The * character will act as a wildcard:

    name:\"IMG_23*\"\n

    This will show all photos which name start with IMG_23.

    name:\"*_23*\"\n

    This will show all photos which name contain _23, like IMG_2356.MOV , 2021_02_23.jpg, etc.

    Wildcards can be combined with & or |: filename:\"*IMG123*|*_22F6FC19.jpg\"

    "},{"location":"user-guide/search/filters/#filter-reference","title":"Filter Reference","text":"

    This is a complete list of supported search filters with examples. Filters can generally be combined unless they contradict each other, e.g. results cannot be monochrome and have high color saturation at the same time.

    Filter Type Examples Notes dist decimal dist:50 Distance to Position (km) lat decimal lat:41.894043 GPS Position (Latitude) lng decimal lng:-87.62448 GPS Position (Longitude) chroma number chroma:70 Chroma (0-100) diff number diff:-1 diff:2 Differential Perceptual Hash (000000-FFFFFF) quality number quality:0 quality:3 Minimum quality score (1-7) album string album:berlin Album UID or Name, supports * wildcards albums string albums:\"South Africa & Birds\" Album Names (combinable with & and |) alt string alt:300-500 GPS Altitude (m) camera string camera:canon Camera Make/Model Name category string category:airport Location Category city string city:\"Berlin\" Location City (separate with |) color string color:\"red|blue\" Color Name (purple, magenta, pink, red, orange, gold, yellow, lime, green, teal, cyan, blue, brown, white, grey, black) (separate with |) country string country:\"de|us\" Location Country Code (separate with |) day string day:3|13 Day of Month (1-31, separate with |) f string f:2.8-4.5 Aperture (f-number) face string face:PN6QO5INYTUSAATOFL43LL2ABAV5ACZG Face ID, yes, no, new, or kind faces string faces:yes faces:3 Minimum number of Faces (yes = 1) favorite string favorite:true favorite:false Finds images by favorite status filename string filename:\"2021/07/12345.jpg\" File Name with path and extension (separate with |) folder string folder:\"*/2020\" Path Name (separate with |), supports * wildcards geo string geo:yes Finds pictures with or without coordinates hash string hash:2fd4e1c67a2d SHA1 File Hash (separate with |) id string id:123e4567-e89b-... Finds pictures by Exif UID, XMP Document ID or Instance ID iso string iso:200-400 ISO Number (light sensitivity) keywords string keywords:\"sand&water\" Keywords (combinable with & and |) label string label:cat|dog Label Names (separate with |) latlng string latlng:\"name\" GPS Bounding Box (Lat N, Lng E, Lat S, Lng W) lens string lens:ef24 Lens Make/Model Name mm string mm:28-35 Focal Length (35mm equivalent) month string month:7|10 Month (1-12, separate with |) mp string mp:3-6 Resolution in Megapixels (MP) name string name:\"IMG_9831-112*\" File Name without path and extension (separate with |) near string near:pqbcf5j446s0futy Finds nearby pictures (UID) olc string olc:8FWCHX7W+ OLC Position (Open Location Code) original string original:\"IMG_9831-112*\" Original file name of imported files (separate with |) path string path:2020/Holiday Path Name (separate with |), supports * wildcards people string people:\"Jane & John\" Subject Names (combinable with & and |) person string person:\"Jane Doe & John Doe\" Subject Names, exact matches (combinable with & and |) s2 string s2:4799e370ca54c8b9 S2 Position (Cell ID) scan string scan:true scan:false Finds scanned photos and documents state string state:\"Baden-W\u00fcrttemberg\" Location State (separate with |) subject string subject:\"Jane Doe & John Doe\" Alias for person subjects string subjects:\"Jane & John\" Alias for people title string title:\"Lake*\" Title (separate with |) type string type:raw Media Type (image, video, raw, live, animated); separate with | uid string uid:pqbcf5j446s0futy Limits results to the specified internal unique IDs year string year:1990|2003 Year (separate with |) animated switch animated:yes Finds animated GIFs archived switch archived:yes Finds archived pictures error switch error:yes Finds pictures with errors hidden switch hidden:yes Finds hidden pictures (broken or unsupported) landscape switch landscape:yes Finds pictures in landscape format live switch live:yes Finds Live Photos and short videos mono switch mono:yes Finds pictures with few or no colors panorama switch panorama:yes Finds pictures with an aspect ratio > 1.9:1 photo switch photo:yes Finds only photos, no videos portrait switch portrait:yes Finds pictures in portrait format primary switch primary:yes Finds primary JPEG files only private switch private:yes Finds private pictures public switch public:yes Excludes private pictures raw switch raw:yes Finds pictures with RAW image file review switch review:yes Finds pictures in review square switch square:yes Finds images with an aspect ratio of 1:1 stack switch stack:yes Finds pictures with more than one media file stackable switch stackable:yes Finds pictures that can be stacked with additional media files unsorted switch unsorted:yes Finds pictures not in an album unstacked switch unstacked:yes Finds pictures with a file that has been removed from a stack vector switch vector:yes Finds vector graphics only video switch video:yes Finds video files only after timestamp after:\"2022-01-30\" Finds pictures taken after this date before timestamp before:\"2022-01-30\" Finds pictures taken before this date

    Why can't I play live photos or find stacks when I search for specific images?

    Our search API and user interface perform a file search. This is intentional since \"stacks\" can contain files of different types and properties, such as color.

    For example, there may be color and monochrome versions. Now, when you search for them or sort them by color, the user interface must display individual files. Otherwise, the results showing a color image/video when you filter by monochrome would make no sense.

    Likewise, if you search for filename.mp4.*, you will find only JPEGs without video, because the video file extension is .mp4 without an extra dot at the end.

    We recommend using the path: and/or name: filters with wildcards if searching for individual files limits the search results too much. Most users will want to find all related files so that they can be displayed together, e.g. as live photos consisting of a video and an image.

    You can combine these filters with other filters such as live to ensure that the results include only pictures with a specific media type. Alternatively, you can use the filename: filter with a more permissive wildcard that excludes the file extension.

    "},{"location":"user-guide/search/views/","title":"Search Result Views","text":"

    PhotoPrism offers you three different views to browse your photos and videos. In addition you can choose between multiple light and dark themes.

    Cards ViewMosaic ViewList View

    The cards view displays important metadata like title, time and location next to the photos

    The mosaic view lets you enjoy your photos without distraction

    The list view provides you photos and metadata in a well-arranged list

    To switch between views you can either use the filter in the filter bar or the view button (, , ) in the upper right corner.

    Additionally, you can open your photos/videos in fullscreen mode and start a slideshow ().

    "},{"location":"user-guide/settings/account/","title":"Account Settings","text":"

    For security reasons, changing account-related settings through the user interface requires password authentication, so these settings will not be available to you when public mode is enabled.

    "},{"location":"user-guide/settings/account/#change-password","title":"Change Password","text":"
    1. Go to Settings
    2. Open Account tab
    3. Click Change Password
    4. Enter your current password
    5. Enter your new password twice
    6. Click Change
    "},{"location":"user-guide/settings/account/#2-factor-authentication","title":"2-Factor Authentication","text":"

    Two-factor authentication (2FA) can add an extra layer of security to your account in case someone gains access to your password. If enabled, you will need a randomly generated verification code in addition to your password to log in.

    Learn more \u203a

    "},{"location":"user-guide/settings/account/#apps-and-devices","title":"Apps and Devices","text":"

    If 2FA is enabled for your account, other apps and services will no longer be able to use your password as they do not have access to the verification codes.

    You can therefore generate app-specific passwords for them by navigating to Settings > Account and then clicking the Apps and Devices button. We also recommend using app-specific passwords in case 2FA is not enabled for your account.

    Example for generating an app password that you can use with WebDAV-compatible file synchronization apps like PhotoSync:

    By selecting the WebDAV scope, you ensure that the app password cannot be used to log in through the regular user interface or for other actions. Apps will also not be able to change your password or manage user accounts, even if you grant them Full Access.

    "},{"location":"user-guide/settings/account/#connect-via-webdav","title":"Connect via WebDAV","text":"

    To open a dialog that shows you the URLs required to connect an app or computer via WebDAV:

    1. Go to Settings
    2. Open Account tab
    3. Click Connect via WebDAV

    "},{"location":"user-guide/settings/advanced/","title":"Advanced Settings","text":"

    System config options such as the image quality can be changed on the advanced settings page. You can also disable specific features and enable the debug or read-only mode.

    Since they are not safe to use without authentication, these settings are not available when running in public mode. Changing config options is still possible via configuration files and with command parameters.

    Changing advanced settings always requires a restart to take effect. Selecting a different thumbnail quality or size won't replace existing thumbnails. You can regenerate them using the command-line interface.

    All config options can be set in your compose.yaml or docker-compose.yml or via command-line parameters as well. Manually changed values are saved in a config file. It is stored in the storage/config folder by default.

    "},{"location":"user-guide/settings/advanced/#global-options","title":"Global Options","text":""},{"location":"user-guide/settings/advanced/#debug-logs","title":"Debug Logs","text":"

    When enabled, debug logs are shown in Library>Logs. Requires restart.

    The corresponding config toggle is PHOTOPRISM_DEBUG.

    "},{"location":"user-guide/settings/advanced/#experimental-features","title":"Experimental Features","text":"

    When enabled, your instance will be updated with experimental features.

    The corresponding config toggle is PHOTOPRISM_EXPERIMENTAL.

    "},{"location":"user-guide/settings/advanced/#read-only-mode","title":"Read-only Mode","text":"

    When enabled, importing, uploading and deleting files is not possible.

    The corresponding config toggle is PHOTOPRISM_READONLY.

    "},{"location":"user-guide/settings/advanced/#disable-backups","title":"Disable Backups","text":"

    This option prevents the creation of database, album and YAML sidecar file backups.

    The corresponding config toggle is PHOTOPRISM_DISABLE_BACKUPS.

    "},{"location":"user-guide/settings/advanced/#disable-webdav","title":"Disable WebDAV","text":"

    This option prevents building WebDav connections. Requires restart for changes to be applied.

    The corresponding config toggle is PHOTOPRISM_DISABLE_WEBDAV.

    "},{"location":"user-guide/settings/advanced/#disable-places","title":"Disable Places","text":"

    When selected, geo-information (latitude, longitude) will still be read (and indexed) from your files metadata, however PhotoPrism will not use reverse lookup to determine place names using those coordinates as it normally would.

    The Places section will not be visible.

    The corresponding config toggle is PHOTOPRISM_DISABLE_PLACES.

    "},{"location":"user-guide/settings/advanced/#disable-exiftool","title":"Disable ExifTool","text":"

    This option prevents the creation of json files with Exif data in storage/sidecar. Note that you must have ExifTool enabled to extract video metadata such as duration, resolution, and codec.

    The corresponding config toggle is PHOTOPRISM_DISABLE_EXIFTOOL.

    "},{"location":"user-guide/settings/advanced/#disable-tensorflow","title":"Disable TensorFlow","text":"

    When selected, image classification and facial recognition will be disabled as both rely on tensorflow.

    The corresponding config toggle is PHOTOPRISM_DISABLE_TENSORFLOW.

    "},{"location":"user-guide/settings/advanced/#backups","title":"Backups","text":""},{"location":"user-guide/settings/advanced/#database-backups","title":"Database Backups","text":"

    When selected, database backups are created based on the configured schedule.

    The corresponding config toggle is PHOTOPRISM_BACKUP_DATABASE. The schedule as well as the number of backups that will be retained can be configured with PHOTOPRISM_BACKUP_SCHEDULE and PHOTOPRISM_BACKUP_RETAIN.

    "},{"location":"user-guide/settings/advanced/#album-backups","title":"Album Backups","text":"

    When selected, YAML files that back up album metadata will be created based on the configured schedule.

    The corresponding config toggle is PHOTOPRISM_BACKUP_ALBUMS. The backup schedule can be configured with PHOTOPRISM_BACKUP_SCHEDULE.

    "},{"location":"user-guide/settings/advanced/#sidecar-files","title":"Sidecar Files","text":"

    When selected, YAML files that back up picture metadata will be created.

    The corresponding config toggle is PHOTOPRISM_SIDECAR_YAML.

    "},{"location":"user-guide/settings/advanced/#preview-images","title":"Preview Images","text":"

    This section controls how JPEG preview and thumbnail images are rendered. These are high-quality, scaled-down versions of your originals.

    Thumbnails are necessary because web browsers are bad at resizing large images to fit the screen. Using full-resolution originals for slideshows and in search results would also consume a lot of browser memory and significantly reduce indexing performance.

    "},{"location":"user-guide/settings/advanced/#downscaling-filter","title":"Downscaling Filter","text":"

    This lets you select the algorithm used to resize your original images when creating thumbnails. A detailed description of the available filters can be found in the section below.

    For a good trade-off between quality and performance, we recommend choosing the lanczos filter. It may be a little slower in creating thumbnails, but produces very high quality images. In comparison, the less sophisticated cubic filter may be 30% faster.

    The corresponding config option is PHOTOPRISM_THUMB_FILTER.

    This option is only available if the native imaging image processing library has been enabled by setting PHOTOPRISM_THUMB_LIBRARY to \u201cimaging\u201d in your compose.yaml or docker-compose.yml configuration file.

    "},{"location":"user-guide/settings/advanced/#static-and-dynamic-size-limits","title":"Static and Dynamic Size Limits","text":"

    Static Size Limit: During initial indexing or import (as thumbnails are generated), no thumbnails will be created above this size. The corresponding config option is PHOTOPRISM_THUMB_SIZE.

    Dynamic Size Limit: During dynamic (on-demand) thumbnail generation, no thumbnails will be created above this size. The corresponding config option is PHOTOPRISM_THUMB_SIZE_UNCACHED.

    Reducing the Static Size Limit of thumbnails has a significant impact on face recognition and image classification results. Simply put, it means that the indexer can no longer see properly.

    If the configured size limit is exceeded (for example, if users have a larger screen), a sufficiently large thumbnail can't be created, and the photo viewer may be forced to display the original image instead. Downscaling images in browsers typically results in poor quality, and they may also be displayed in the wrong orientation.

    The smallest configurable size is 720px for consumption by the indexer to perform color detection, face detection, and image classification. Recreating them every time they are needed is too demanding for even the most powerful servers. Unless you only have a few small images, it would render the app unusable.

    It is recommended that you set these limits high so that browsing pictures is as smooth as possible. However, if the amount of disk storage required is a serious problem, and you are willing to increase server load instead, it is possible to set the Static Size Limit to the minimum of 720px in combination with a higher Dynamic Size Limit. This allows the server to generate larger thumbnails on demand. It may also result in a noticeable delay when viewing pictures in full-screen mode.

    To view original images, enable Dynamic Previews, and configure Dynamic Size Limit and Static Size Limit to a small value like 720. When viewing images exceeding that limit, the original files will be displayed.

    "},{"location":"user-guide/settings/advanced/#which-thumbnails-will-be-generated","title":"Which thumbnails will be generated?","text":"

    The smallest configurable static and dynamic size limit is 720px, so most sizes up to fit_720 are always generated by default. Higher size limits generate thumbnails with more detail at higher resolutions - either statically (pre-generated while indexing) or on demand if the configuration permits.

    Optional thumbnail sizes cannot be pre-generated and are only rendered on request, for example when sharing an image on Instagram.

    The following overview shows the name, dimensions, and aspect ratio for each thumbnail size as well as a description of how it is used:

    Name Width Height Aspect Ratio Available Usage colors 3 3 1:1 Always Color Detection tile_50 50 50 1:1 Always List View tile_100 100 100 1:1 Always Places View left_224 224 224 1:1 On-Demand TensorFlow right_224 224 224 1:1 On-Demand TensorFlow tile_224 224 224 1:1 Always TensorFlow, Mosaic View tile_500 500 500 1:1 Always Cards View fit_720 720 720 Preserved Always SD TV, Mobile tile_1080 1080 1080 1:1 Optional Instagram fit_1280 1280 1024 Preserved On-Demand HD TV, SXGA fit_1600 1600 900 Preserved Optional Social Media fit_1920 1920 1200 Preserved On-Demand Full HD fit_2048 2048 2048 Preserved Optional DCI 2K, Tablets fit_2560 2560 1600 Preserved On-Demand Quad HD, Notebooks fit_3840 3840 2400 Preserved Optional 4K Ultra HD fit_4096 4096 4096 Preserved On-Demand DCI 4K, Retina 4K fit_7680 7680 4320 Preserved On-Demand 8K Ultra HD 2

    Generated thumbnail files are stored in the storage/cache/thumbnails folder, where the path and file name depend on the size and file hash, e.g. storage/cache/thumbnails/1/a/3/1a30c1f...9_100x100_center.jpg.

    "},{"location":"user-guide/settings/advanced/#dynamic-previews","title":"Dynamic Previews","text":"

    Enable generating thumbnails on-the-fly as they're required (either for viewing or analysing with TensorFlow). This saves disk space, but is more processor-intensive and so not recommended when hosting on less powerful devices (such as Raspberry Pi).

    The corresponding config toggle is PHOTOPRISM_THUMB_UNCACHED.

    "},{"location":"user-guide/settings/advanced/#image-quality","title":"Image Quality","text":""},{"location":"user-guide/settings/advanced/#jpeg-quality","title":"JPEG Quality","text":"

    Choose a value above 90 to display your images in the best possible quality. Note that higher values require more space in the storage folder for less compressed thumbnail files, which may also take longer to create.

    Lower quality thumbnails, on the other hand, are smaller, load faster on slow Internet connections, and require less space in the storage folder and in the browser cache.

    • Quality levels of 90% or higher are generally considered high quality
    • 80% to 90% is considered medium quality
    • 70% to 80% is considered low quality, as you might see with highly compressed content on social media

    Anything below 70% is generally of very low quality.

    Example: If a quality of 95 results in a thumbnail file size of 500kB, then reducing the quality to 80 reduces the file size to about 100kB.

    The corresponding config option is PHOTOPRISM_JPEG_QUALITY.

    The actual compression depends on how much information an image contains. Empty areas and skies, for example, are easier to compress. Images with a lot of details suffer the most. For this reason, reducing the quality of thumbnails also negatively impacts face recognition and image classification results. Simply put, this means that the indexer sees fewer details.

    "},{"location":"user-guide/settings/advanced/#jpeg-size-limit","title":"JPEG Size Limit","text":"

    This sets the maximum size of the JPEG files created when converting original RAW images.

    The corresponding config toggle is PHOTOPRISM_JPEG_SIZE.

    RawTherapee and \"heif-convert\" cannot limit the resolution of JPEG files when converting files from other formats such as RAW, DNG, HEIC or AVIF.

    "},{"location":"user-guide/settings/advanced/#png-size-limit","title":"PNG Size Limit","text":"

    This sets the maximum size of the PNG files created when converting original images.

    The corresponding config toggle is PHOTOPRISM_PNG_SIZE.

    "},{"location":"user-guide/settings/advanced/#file-conversion","title":"File Conversion","text":"

    Many photographers keep their originals in some sort of lossless RAW format instead of compressed JPEG, especially when shooting with a Digital SLR. Some mobile phones also support RAW or use HEIC/HEIF for a similar purpose. PhotoPrism aims at providing excellent support for all RAW formats, independent of camera brand and model. Please let us know when there is an issue with your specific device.

    Web browsers in general cannot display RAW image files. They need to be converted, which is what our import and convert commands do. You'll also find a checkbox for this step in our Web UI.

    In addition, PhotoPrism also supports TIFF, PNG, BMP and GIF files. Be aware that files in those formats often don't contain useful metadata and are typically used for screenshots, charts, graphs and icons only.

    Generated sidecar files will be stored outside your originals folder by default, so that RAW to JPEG conversion also works in read-only mode.

    "},{"location":"user-guide/settings/advanced/#disable-darktable","title":"Disable Darktable","text":"

    If this feature is disabled, Darktable will not be used for RAW conversion.

    The corresponding config toggle is PHOTOPRISM_DISABLE_DARKTABLE.

    "},{"location":"user-guide/settings/advanced/#disable-rawtherapee","title":"Disable RawTherapee","text":"

    If this feature is disabled, RawTherapee is not used for RAW conversion.

    The corresponding config toggle is PHOTOPRISM_DISABLE_RAWTHERAPEE.

    "},{"location":"user-guide/settings/advanced/#use-presets","title":"Use Presets","text":"

    Disables simultaneous conversion of RAW files to apply Darktable presets.

    The corresponding config toggle is PHOTOPRISM_RAW_PRESETS.

    "},{"location":"user-guide/settings/advanced/#disable-imagemagick","title":"Disable ImageMagick","text":"

    If this feature is disabled, ImageMagick is not used for conversion.

    The corresponding config toggle is PHOTOPRISM_DISABLE_IMAGEMAGICK.

    "},{"location":"user-guide/settings/advanced/#disable-ffmpeg","title":"Disable FFmpeg","text":"

    If this feature is disabled, FFmpeg is not used to transcode videos or extract still images for thumbnail creation, and indexing or importing video files is not possible.

    The corresponding config toggle is PHOTOPRISM_DISABLE_FFMPEG.

    To prevent inexperienced users from accidentally disabling the creation of thumbnails for videos FFmpeg can only be disabled when Experimental Features are enabled.

    "},{"location":"user-guide/settings/advanced/#downscaling-filters","title":"Downscaling Filters","text":""},{"location":"user-guide/settings/advanced/#linear","title":"Linear","text":"

    Bilinear interpolation takes a weighted average of the four neighborhood pixels to calculate its final interpolated value. The result is a much smoother image than the original image. When all known pixel distances are equal, then the interpolated value is simply their sum divided by four. This technique performs interpolation in both directions, horizontal and vertical. This technique gives better result than nearest neighbor interpolation and take less computation time compared to bicubic interpolation.

    "},{"location":"user-guide/settings/advanced/#cubic","title":"Cubic","text":"

    Catmull-Rom is a local interpolating spline developed for computer graphics purposes. Its initial use was in design of curves and surfaces, and has recently been used in several applications. Catmull-Rom splines are a family of cubic interpolating splines formulated such that the tangent at each point is calculated using the previous and next point on the spline. The results are similar to ones produced by bicubic interpolation with regards to sharpness, but the Catmull-Rom reconstruction is clearly superior in smooth signal region.

    "},{"location":"user-guide/settings/advanced/#lanczos","title":"Lanczos","text":"

    The Lanczos interpolation function is a mathematical formula used to smoothly interpolate the value of a digital image between its samples. It maps each sample of the given image to a translated and scaled copy of the Lanczos kernel, which is a sinc function windowed by the central hump of a dilated sinc function. The sum of these translated and scaled kernels is then evaluated at the desired pixel. Lanczos interpolation has the best properties in terms of detail preservation and minimal generation of aliasing artifacts for geometric transformations not involving strong down sampling. However higher order Lanczos interpolation requires high computational time, which makes it unsuitable for most commercial software.

    "},{"location":"user-guide/settings/advanced/#blackman","title":"Blackman","text":"

    Blackman is a modification of Lanczos that has better control of ringing artifacts.

    "},{"location":"user-guide/settings/advanced/#examples","title":"Examples","text":"

    Original image:

    The same image resized from 600x400px to 150x100px using different resampling filters. From faster (lower quality) to slower (higher quality):

    Filter Resize result Nearest Neighbor Bilinear Sharp Bicubic Lanczos

    Source: A Comparative Analysis of Image Interpolation Algorithms

    "},{"location":"user-guide/settings/general/","title":"General Settings","text":"

    In the General settings tab, you can configure basic user interface settings as well as the maps in Places:

    "},{"location":"user-guide/settings/general/#user-interface","title":"User Interface","text":"

    You can change the theme and language of the User interface.

    To make PhotoPrism suit your individual needs, the following sections and functionalities can be en- or disabled. Disabled sections do not appear in the main navigation.

    "},{"location":"user-guide/settings/general/#upload","title":"Upload","text":"

    When disabled, uploading files via upload is not possible. This might be useful when you grant others access to your PhotoPrism but do not want them to upload photos.

    "},{"location":"user-guide/settings/general/#download","title":"Download","text":"

    When disabled, no files can be downloaded using the PhotoPrism UI. Please note that it may still be possible to download files using the integrated browser functionalities.

    "},{"location":"user-guide/settings/general/#share","title":"Share","text":"

    When disabled, album sharing and upload to remote services like ownCloud is not possible.

    "},{"location":"user-guide/settings/general/#people","title":"People","text":"

    When disabled, the people section is hidden. To disable face detection while indexing, you may set PHOTOPRISM_DISABLE_FACES and/or PHOTOPRISM_DISABLE_TENSORFLOW to \"true\" in your config.

    "},{"location":"user-guide/settings/general/#hide-private","title":"Hide Private","text":"

    Excludes content marked as private from search results, shared albums, labels and places.

    "},{"location":"user-guide/settings/general/#archive","title":"Archive","text":"

    When disabled, there is no Archive. Photos that have been archived beforehand will appear again in search results.

    "},{"location":"user-guide/settings/general/#edit","title":"Edit","text":"

    When disabled, it is not possible to edit photo details.

    "},{"location":"user-guide/settings/general/#delete","title":"Delete","text":"

    When disabled, permanent deletion of files from the archive is not possible.

    "},{"location":"user-guide/settings/general/#originals","title":"Originals","text":"

    When disabled, there is no Originals section.

    "},{"location":"user-guide/settings/general/#moments","title":"Moments","text":"

    When disabled, there is no Moments section.

    "},{"location":"user-guide/settings/general/#labels","title":"Labels","text":"

    When disabled, there is no Labels section and you cannot add or edit labels.

    "},{"location":"user-guide/settings/general/#library","title":"Library","text":"

    When disabled, there is no Library section.

    "},{"location":"user-guide/settings/general/#import","title":"Import","text":"

    When disabled, there is no possibility to import photos. You need to use index instead to add new photos.

    "},{"location":"user-guide/settings/general/#logs","title":"Logs","text":"

    When disabled, server logs are not shown.

    "},{"location":"user-guide/settings/general/#places","title":"Places","text":"

    When disabled, there is no Places section.

    "},{"location":"user-guide/settings/general/#places_1","title":"Places","text":"

    At the bottom of the General settings tab, you may choose your preferred map style and animation length for Places. PhotoPrism includes four high-resolution world maps to see where you've been, and for rediscovering long-forgotten shots.

    To enhance your photos with location data such as state, city and category, we've also launched our own geo-information service based on OpenStreetMap. A future release will additionally provide public events' data, so that albums of popular music festivals, or sports gatherings, can be created automatically.

    "},{"location":"user-guide/settings/general/#downloads","title":"Downloads","text":"

    Note that your choice will not affect ZIP archives when you download complete albums. However, we may add settings for this in a future release.

    "},{"location":"user-guide/settings/general/#originals_1","title":"Originals","text":"

    Only the files in the originals folder will be downloaded, but not any files that were automatically created in the sidecar folder. This is the recommended default.

    "},{"location":"user-guide/settings/general/#raw","title":"RAW","text":"

    Download RAW image files.

    "},{"location":"user-guide/settings/general/#sidecar","title":"Sidecar","text":"

    Download sidecar files as used for XMP metadata. This is generally not recommended except for some professional workflows.

    "},{"location":"user-guide/settings/library/","title":"Library Settings","text":"

    Library Settings are only available for Super Admins.

    "},{"location":"user-guide/settings/library/#index","title":"Index","text":""},{"location":"user-guide/settings/library/#estimates","title":"Estimates","text":"

    Estimates the location of pictures taken without GPS information by extrapolating it from the location of other pictures taken on the same day.

    Be aware that, if you have pictures from unrelated events at different locations, the GPS coordinates of pictures from one event will be applied/extrapolated to pictures of the other event that lack coordinates (even if these are in different folders).

    "},{"location":"user-guide/settings/library/#quality-filter","title":"Quality Filter","text":"

    Requires a review of non-photographic and low-quality images before they appear in search results.

    "},{"location":"user-guide/settings/library/#preview-images","title":"Preview Images","text":"

    Automatically creates JPEG or PNG preview images for other file types so they can be displayed in search results and in the full-screen viewer.

    Preview Images should be enabled, otherwise PhotoPrism cannot index file types other than JPEG or PNG unless a preview sidecar file with the same filename prefix already exists. See Stacks to learn more about naming conventions of sidecar files.

    To prevent inexperienced users from accidentally disabling the creation of thumbnails Preview Images can only be disabled when Experimental Features are enabled.

    "},{"location":"user-guide/settings/library/#stacks","title":"Stacks","text":"

    Stacks are groups of files that have the same origin but may differ in quality, format, size, or color. You can navigate to Search > Stacks to find pictures with stacked media files.

    PhotoPrism offers you the following optional stacking methods, which you can choose to enable based on your personal preferences:

    • Place & Time stacks pictures taken at same GPS position and second
    • Unique ID, matches the ImageUniqueID (Exif) or Instance ID (XMP)
    • Sequential Name, for example /2018/IMG_1234 (2).jpg and /2018/IMG_1234 (3).jpg

    Files that share the same file and folder name (except for the file extension) are always stacked, for example /2018/IMG_1234.jpg and /2018/IMG_1234.avi.

    Note that it is not possible to disable stacking of files with the same name as this would break important functionality, most notably support for Apple Live Photos (which consist of a photo and a video file), any other multi-file/hybrid formats like RAW/JPEG, and indexing of metadata from XMP/JSON sidecar files.

    "},{"location":"user-guide/settings/library/#are-files-automatically-unstacked-when-i-change-the-settings","title":"Are files automatically unstacked when I change the settings?","text":"

    When you change these settings, files that are already stacked will not be unstacked automatically. This is because unstacking is a resource-intensive operation that requires each file to be re-indexed.

    The result also depends on the exact order in which you unstack the files, as non-media sidecar files, for example, remain bound to the remaining media file in a stack. We consider providing a command for this in a future release and appreciate any contributions in this regard.

    If you are new to PhotoPrism and want to re-index your library with different settings, you can run the photoprism reset command in a terminal to reset the index and start from scratch.

    "},{"location":"user-guide/settings/library/#which-sequential-naming-patterns-are-supported","title":"Which sequential naming patterns are supported?","text":"

    If stacking by Sequential Name has been enabled, files with e.g. the following names would be stacked with the file /2018/IMG_1234.jpg:

    • /2018/IMG_1234 (2).jpg /2018/IMG_1234 (3).jpg
    • /2018/IMG_1234 copy.jpg /2018/IMG_1234 copy 1.jpg /2018/IMG_1234 copy 2.jpg
    • /2018/IMG_1234 (-2.7) /2018/IMG_1234 (+3.3).jpg /2018/IMG_1234(-2.7).jpg /2018/IMG_1234(+3.3).jpg
    "},{"location":"user-guide/settings/sync/","title":"Services","text":"

    You can connect your PhotoPrism instance to other services with WebDAV support, such as other PhotoPrism instances, Nextcloud or ownCloud.

    This might be useful if you want to share or synchronize files between multiple services.

    Since they are not safe to use without authentication, these settings are not available when running in public mode.

    PhotoPrism can also serve/share files via WebDAV to be browsed on macOS or Windows. See instructions.

    "},{"location":"user-guide/settings/sync/#add-service","title":"Add Service","text":"

    <! -- PhotoPrism may connect with WebDAV servers like a second PhotoPrism instance or ownCloud, so that you can automatically sync your latest pictures. -->

    1. Go to Settings
    2. Open Services tab
    3. Click Connect
    4. Fill in your service url, username and password
    5. Click connect
    6. Now the other service is connected with PhotoPrism
    "},{"location":"user-guide/settings/sync/#edit-connection-details","title":"Edit Connection Details","text":"
    1. Go to Settings
    2. Open Services tab
    3. Click
    4. Edit account details and click Save
    "},{"location":"user-guide/settings/sync/#edit-upload-settings","title":"Edit Upload Settings","text":"
    1. Go to Settings
    2. Open Services tab
    3. Click into the upload cell of your service

    4. Select the folder to which photos should be uploaded and click save

    You can now share albums or files with this service.

    Due to problems with some Nextcloud settings it might be that uploading to Nextcloud results in 0 byte files. You find information on how to solve it here.

    "},{"location":"user-guide/settings/sync/#edit-sync-settings","title":"Edit Sync Settings","text":"
    1. Go to Settings
    2. Open Services tab
    3. Click into the sync cell of your service
    4. Enable synchronization in the upper right corner
    5. Choose a folder from your service
    6. Choose a sync interval
    7. Select the options that are suitable for you and click Save
    "},{"location":"user-guide/settings/sync/#remote-sync-options","title":"Remote Sync Options","text":"
    • Download remote files will download all files from the selected folder of the other service that do not yet exist in PhotoPrism
    • Upload local files will upload all files (including private or archived ones) from PhotoPrism to your service that do not yet exist there
    • Preserve filenames will keep filenames without renaming them
    • Sync raw and video files will upload/download raw and video files alongside with JPEGS
    "},{"location":"user-guide/share/","title":"Sharing Albums via Link","text":"

    Create sharing links to share albums, moments, calendar months, states or folders with your friends.

    Your friends are able to view and download photos from shared albums. Photos marked as private won't show up.

    When link visitors click on the location of a photo, they can view the photos of the shared album in the map view

    Clicking allows link visitors to end their session.

    Support for optional password protection of sharing links as well as other enhancements are planned.

    "},{"location":"user-guide/share/#create-sharing-link","title":"Create Sharing Link","text":"Using Context MenuVia Toolbar
    1. Go to Albums / Moments / Calendar / States / Folders
    2. Select the album you want to share
    3. Open the context menu
    4. Click
    1. Go to Albums / Moments / Calendar / States / Folders
    2. Open the album by clicking on it
    3. Click

    Then

    1. Click to open the link details
    2. Set a secret and expiry date
    3. Click save

    4. Copy the link by clicking on it

    5. Share it with your friends

    Share multiple albums with one link

    You can share multiple albums using the same link by using the same secret.

    You can create additional links with different secrets and expiry dates by clicking .

    "},{"location":"user-guide/share/#delete-sharing-link","title":"Delete Sharing Link","text":"
    1. Go to Albums
    2. Click on the album cover

    3. Click

    4. Click

    "},{"location":"user-guide/share/services-share/","title":"Share with other Apps","text":"

    In settings you can connect your PhotoPrism instance to other services with WebDAV support, such as other PhotoPrism instances, Nextcloud or ownCloud.

    "},{"location":"user-guide/share/services-share/#share-files-with-other-apps","title":"Share Files with other Apps","text":"
    1. Go to Search
    2. Select photos you want to upload
    3. Open the context menu
    4. Click
    5. Select your account and click upload

    Due to problems with some Nextcloud settings it might be that uploading to Nextcloud results in 0 byte files. You find information on how to solve it here.

    "},{"location":"user-guide/share/services-share/#share-albums-with-other-apps","title":"Share Albums with other Apps","text":"
    1. Go to Albums / Moments / Calendar / States / Folders
    2. Select the album you want to share
    3. Open the context menu
    4. Click
    5. Click WebDAV Upload
    6. Select your account and click upload
    "},{"location":"user-guide/sync/dropbox/","title":"Syncing with Dropbox","text":"

    It's possible to use Dropbox to store your photos, while viewing and managing them through PhotoPrism.

    1. Set up a Dropbox account.
    2. Install the Dropbox desktop client.
    3. Sync your Dropbox to a local directory.
    4. If using Docker, configure your compose.yaml or docker-compose.yml with;
      volumes:\n  - \"~/Dropbox/Photos:/photoprism/originals\"\n
    5. Follow the PhotoPrism getting started guide as normal.
    "},{"location":"user-guide/sync/dropbox/#auto-upload-from-mobile","title":"Auto-upload from Mobile","text":"

    The Dropbox mobile apps also have a 'Camera Upload' feature which syncs photos to Dropbox, and then to any machine with Dropbox installed.

    To auto-import uploaded files into PhotoPrism;

    1. Install the Dropbox iOS or Android app.
    2. Enable 'Camera Uploads' in the Dropbox app's settings.
    3. Install the Dropbox desktop client on your server or a network-accessible machine
    4. Configure the Camera Uploads folder as your import directory for PhotoPrism. In your compose.yaml or docker-compose.yml file, this is;
      volumes:\n  - \"~/Dropbox/Camera Uploads:/photoprism/import\"\n
    5. Optional: Enable 'delete on import' in PhotoPrism's settings to delete imported files from Dropbox. This saves Dropbox space, allowing you to remain within the 2 GB free tier.

    The Dropbox mobile app needs to be opened periodically or it tends to fail to identify and sync new photos.

    "},{"location":"user-guide/sync/dropbox/#smart-selective-sync","title":"Smart / Selective Sync","text":"

    A useful (although paid) feature is Dropbox Smart Sync (with optional auto-evict) which will download the files from Dropbox's servers only when you (or PhotoPrism) accesses them (such as during initial indexing, or when downloading an original file via the PhotoPrism interface).

    This can save space on your server by automically offloading the originals unless/until they're viewed.

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"user-guide/sync/mobile-devices/","title":"Syncing with Mobile Devices","text":"

    You can use any app that supports the WebDAV protocol to synchronize photos and videos between your phone and PhotoPrism.

    Based on our own experience, we can highly recommend PhotoSync as it is one of the most feature-rich and sophisticated apps currently available for iOS and Android.

    An overview of mobile sync apps for iOS and Android can be found below.

    WebDAV access can be disabled under Settings > Advanced. Since it requires write permissions and authentication, the built-in WebDAV server is automatically disabled when running in read-only or public mode.

    "},{"location":"user-guide/sync/mobile-devices/#using-photosync","title":"Using PhotoSync","text":""},{"location":"user-guide/sync/mobile-devices/#set-photoprism-or-webdav-as-target","title":"Set PhotoPrism or WebDAV as Target","text":"
    1. Open PhotoSync and click
    2. Click Configure
    3. Select PhotoPrism as target

    4. Enter your settings

      Server: Your server url, e.g. \"example.com\".

      Port: Your port. If you are using HTTPS the port is 443.

      Login: Your username, e.g. \"admin\".

      Password: Your admin password.

      Directory: /import/ or /originals/ depending on your preferred ingestion method.

      Use SSL: Should be enabled.

      PikaPods users can find more information here.

    5. Click Done

    6. You may adapt transfer details to match your preferences

    "},{"location":"user-guide/sync/mobile-devices/#set-up-automatic-sync","title":"Set Up Automatic Sync","text":"
    1. Open PhotoSync and click
    2. Click Autotransfer

    3. Click Add new trigger and choose one or more trigger

    4. Choose PhotoPrism as target

    5. Click Done

    Because PhotoSync uses WebDAV to send files, PhotoPrism automatically starts importing/indexing when it receives new files.

    "},{"location":"user-guide/sync/mobile-devices/#sync-apps-for-ios-and-android","title":"Sync Apps for iOS and Android","text":"

    As an alternative to PhotoSync, you can also use a wide range of other apps to synchronize your pictures with PhotoPrism, either directly via WebDAV or by sharing the originals folder as an SMB network drive through your operating system or cloud provider:

    Name Platform Synchronization Price Download PhotoSync Pro iOS, Android WebDAV, SMB \u20ac6.99 App Store, Google Play PhotoSync Premium iOS, Android WebDAV, SMB \u20ac24.99 App Store, Google Play EasySync Android WebDAV \u20ac2.99 Google Play, F-Droid Syncthing Android Syncthing Free Google Play FolderSync Pro Android WebDAV, SMB \u20ac6.49 Google Play Owlfiles Pro iOS, Android WebDAV, SMB \u20ac59.99 App Store, Google Play

    Note that this overview is provided for your convenience only and that we are unable to provide technical support for any of these apps. If you have problems, please contact the author or ask the community for help. You are welcome to suggest additional sync apps, so we can include them in this list.

    "},{"location":"user-guide/sync/services-sync/","title":"Syncing with other WebDAV-compatible Apps and Services","text":"

    Under Settings > Services you can connect your PhotoPrism instance to other apps and services that can be accessed via WebDAV, e.g. other PhotoPrism instances, Nextcloud or ownCloud.

    When syncing, your files will be uploaded or downloaded to/from another service, which requires additional storage space. If you want PhotoPrism to index files from another local application, e.g. Nextcloud, we recommend mounting its file storage directory as originals folder instead of synchronizing the files via WebDAV. This prevents unnecessary copies of your files from being created.

    "},{"location":"user-guide/sync/services-sync/#automatically-uploaddownload-files-tofrom-another-app","title":"Automatically Upload/Download Files to/from another App","text":"
    1. Go to Settings
    2. Open Services tab
    3. Click into the sync cell of your service
    4. Enable synchronization in the upper right corner
    5. Choose a folder from your service
    6. Choose a sync interval
    7. Select the options that are suitable for you and click Save
    "},{"location":"user-guide/sync/services-sync/#remote-sync-options","title":"Remote Sync Options","text":"
    • Download remote files will download all files from the selected folder of the other service that do not yet exist in PhotoPrism
    • Upload local files will upload all files (including private or archived ones) from PhotoPrism to your service that do not yet exist there
    • Preserve filenames will keep filenames without renaming them
    • Sync raw and video files will upload/download raw and video files alongside with JPEGS
    "},{"location":"user-guide/sync/webdav/","title":"Connecting via WebDAV","text":"

    WebDAV-compatible apps and clients such as PhotoSync, Microsoft's Windows Explorer, and Apple's Finder can connect directly to PhotoPrism.

    This mounts the originals and/or import folder as a network drive, allowing you to open, edit, and delete files from a remote device as if they were local.

    After files have been transferred, you can index or import them as usual. By default, indexing and importing start automatically after a safety delay when files have been uploaded using WebDAV.

    It is also possible to sync files with external WebDAV servers such as ownCloud or other PhotoPrism instances.

    You can disable WebDAV by navigating to Settings > Advanced and selecting the corresponding option. As WebDAV access always requires authentication, the integrated server is automatically disabled if your instance is running in public mode.

    Do not use WebDAV without HTTPS outside your local, private network as your password would be transmitted, in clear text, over the Internet. Backup tools and file sync apps like FolderSync may refuse to connect as well.

    "},{"location":"user-guide/sync/webdav/#server-url","title":"Server URL","text":"

    If your instance is connected to the public Internet, the WebDAV URL of the originals folder has the following format, where example.com must be replaced with the actual hostname and admin with the actual username:

    https://admin@example.com/originals/\n

    For users running a local instance on the default port 2342 without HTTPS, the URL of the originals folder is as follows (the default username for new instances is admin, unless you have changed it in the configuration):

    http://admin@localhost:2342/originals/\n

    Please note that the slash at the end of the path must not be omitted and that the WebDAV URL in your client apps needs to be updated if the hostname or port of the server changes.

    You can view the originals folder URL by navigating to Settings > Account and then clicking Connect via WebDAV. It is possible to connect to the import folder instead by replacing /originals/ with /import/ in the URL.

    "},{"location":"user-guide/sync/webdav/#microsoft-windows","title":"Microsoft Windows","text":"

    On Windows, you must instead enter a resource string in the following format to configure WebDAV access, where example.com must be replaced with the actual hostname of your instance:

    \\\\example.com@SSL\\originals\\\n

    If your server does not use the standard port 443 for HTTPS, Windows lets you specify a custom port such as 8443 directly after @SSL:

    \\\\example.com@SSL@8443\\originals\\\n

    For local installations running on the default port 2342 without HTTPS, enter the following resource in the connection dialog (you may need to update the registry settings for this):

    \\\\localhost:2342\\originals\\\n

    Please note that the slash at the end must not be omitted and that the WebDAV resource in Windows needs to be updated when the hostname or port of the server changes.

    You can view the originals folder resource by navigating to Settings > Account and then clicking Connect via WebDAV. It is possible to connect to the import folder instead by replacing \\originals\\ with \\import\\.

    "},{"location":"user-guide/sync/webdav/#credentials","title":"Credentials","text":"

    To access your instance via WebDAV, you can use your username in combination with your account password or an app password, e.g. if you have 2-Factor Authentication (2FA) enabled for your account or authenticate via OpenID Connect (OIDC) as using your account password is not possible in this case.

    If access is denied even though the login credentials are correct, please check whether the account has a role with WebDAV access and WebDAV is enabled for the specific account.

    Learn more \u203a

    "},{"location":"user-guide/sync/webdav/#connect-to-a-webdav-server","title":"Connect to a WebDAV Server","text":"macOSWindows
    1. In the Finder on your Mac, choose Go > Connect to Server
    2. Enter the URL as shown above in the Server Address field
    3. Click Connect

    If you cannot connect to your instance via WebDAV using these instructions:

    • You do not have sufficient user rights (try as admin)
    • You are experiencing a general authentication problem
    • Your instance or reverse proxy uses an invalid HTTPS certificate
    • You are trying to connect to the wrong network or server
    1. Open Windows File Explorer
    2. Right click This PC
    3. From the dropdown, select Map network drive...

    4. Enter the drive letter and folder you want to map your WebDAV connection to

    5. Check the boxes Reconnect at sign-in and Connect using different credentials
    6. Click the Connect to a Web site that you can use to store your documents and pictures link

    7. Click Next

    8. Click Choose a custom network location and then click Next

    9. In the Internet or network address field, enter the URL as shown above and click Next

    10. Enter your username and password and click Ok

    11. Enter a name for the network location and click Next

    12. Click Finish

    The originals folder appears as a mapped drive in Windows Explorer, and you can immediately add, edit, or delete files and directories using the Windows File Explorer.

    If you cannot connect to your instance via WebDAV using these instructions:

    • You may need to change the basic authentication level in the registry
    • You do not have sufficient user rights (try as admin)
    • You are experiencing a general authentication problem
    • Your instance or reverse proxy uses an invalid HTTPS certificate
    • You are trying to connect to the wrong network or server
    "},{"location":"user-guide/use-cases/apple/","title":"Migrate from Apple Photos","text":""},{"location":"user-guide/use-cases/apple/#transfer-files","title":"Transfer Files","text":"
    1. Select the files or albums you want to export
    2. Click File > Export > Export Unmodified Original For Photos
    3. Select Export IPTC as XMP
    4. Click Export
    5. Move the exported files/folders to your originals or import diectory and start indexing or importing
    "},{"location":"user-guide/use-cases/apple/#metadata","title":"Metadata","text":"

    Apple saves the following information in its XMP files:

    • Title
    • Description
    • TakenAt Date
    • Keywords (include people)
    • GPS information

    The following metadata is read by PhotoPrism from the exported XMP files for each photo during indexing:

    • Title
    • Description
    • TakenAt Date
    • Keywords

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"user-guide/use-cases/flickr/","title":"Migrate from Flickr","text":""},{"location":"user-guide/use-cases/flickr/#transfer-files","title":"Transfer Files","text":"
    1. Go to https://www.flickr.com/account
    2. Request your Flickr data
    3. Depending on the amount of photos it can take a few days for your data to be exported
    4. Download your data and add it to your originals directory (including json files)
    5. Start indexing

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"user-guide/use-cases/google/","title":"Migrate from Google Photos","text":""},{"location":"user-guide/use-cases/google/#transfer-files","title":"Transfer Files","text":"
    1. Go to Google Takeout
    2. Click Deselect all then check only Google Photos.
    3. Trigger the export of your Google Photos Data.
    4. Depending on the number of photos, it can take a few days for your data to be exported
    5. Download your data and extract all archives into to your originals or import folder
      • the folder should include the photos themselves, alongside json files for each of the photos
    6. Start indexing or importing
    "},{"location":"user-guide/use-cases/google/#metadata","title":"Metadata","text":"

    The following metadata is read by PhotoPrism from each photo's JSON file

    • Title
    • Description
    • Geolocation Info (lat/long)
    • Date/Time Taken
    • Date/Time Created
    • Date/Time Updated
    "},{"location":"user-guide/use-cases/google/#transfer-albums","title":"Transfer Albums","text":"

    Google Photos albums won't be automatically imported yet as we're trying to find a way to deal with auto-generated albums users may not want to import.

    The community has created a bash script to import albums from a Google Takeout. For more information and support, see the project page on Github:

    inthreedee/photoprism-transfer-album

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    "},{"location":"user-guide/users/","title":"Managing User Accounts","text":"

    PhotoPrism\u00ae Plus includes a web user interface for account and session management, in addition to the command-line interface available in all editions.

    You can add, edit and delete user accounts by navigating to Settings > Users as an Admin:

    "},{"location":"user-guide/users/#adding-a-new-user","title":"Adding a New User","text":""},{"location":"user-guide/users/#editing-user-details","title":"Editing User Details","text":"

    Only super admins can change the authentication provider of an account through the web interface, except for their own account, so that they do not accidentally lock themselves out e.g. by setting it to \"none\".

    "},{"location":"user-guide/users/#changing-passwords","title":"Changing Passwords","text":"

    Super admins can reset a user's password, while regular admins can change passwords only if they know the current password.

    "},{"location":"user-guide/users/#deleting-a-user","title":"Deleting a User","text":""},{"location":"user-guide/users/2fa/","title":"2-Factor Authentication","text":"

    Two-factor authentication (2FA) can add an extra layer of security to your account in case someone gains access to your password. If enabled, you will need a randomly generated verification code in addition to your password to log in:

    "},{"location":"user-guide/users/2fa/#authenticator-apps","title":"Authenticator Apps","text":"

    To enable 2FA for your account, you need a compatible authenticator app or device, for example:

    • Google Authenticator
    • Microsoft Authenticator
    • 2FA Authenticator (2FAS)

    It is best if you have the authenticator app installed on your phone, as this way you can easily set it up by scanning the displayed QR code with your camera and always have it with you.

    Hardware Devices

    While there are also dedicated hardware devices available as an alternative to authenticator apps, these are less common and we cannot give any recommendations.

    "},{"location":"user-guide/users/2fa/#setup","title":"Setup","text":""},{"location":"user-guide/users/2fa/#step-1-verification-code","title":"Step 1: Verification Code","text":"

    You can enable 2FA for your account by navigating to Settings > Account and then clicking the 2-Factor Authentication button to open the setup dialog:

    On the following page, scan the displayed QR code with your authenticator app (or use the setup key shown if you are using an app or device without camera) and then enter the generated verification code to proceed.

    "},{"location":"user-guide/users/2fa/#step-2-recovery-code","title":"Step 2: Recovery Code","text":"

    In the last step before 2FA is activated, you will be shown a recovery code that you can use to access your account when you cannot generate a valid verification code with your app or device:

    To avoid being locked out of your account, please download, print or copy this recovery code and keep it in a safe place. It is a one-time use code that will disable 2FA for your account when you use it.

    "},{"location":"user-guide/users/2fa/#step-3-app-passwords","title":"Step 3: App Passwords","text":"

    If 2FA is enabled for your account, other apps and services will no longer be able to use your password as they do not have access to the verification codes.

    You can therefore generate app-specific passwords for them by navigating to Settings > Account and then clicking the Apps and Devices button. We also recommend using app-specific passwords in case 2FA is not enabled for your account.

    Example for generating an app password that you can use with WebDAV-compatible file synchronization apps like PhotoSync:

    By selecting the WebDAV scope, you ensure that the app password cannot be used to log in through the regular user interface or for other actions. Apps will also not be able to change your password or manage user accounts, even if you grant them Full Access.

    "},{"location":"user-guide/users/2fa/#new-authenticator","title":"New Authenticator","text":"

    To switch to a new authenticator app or device, first deactivate 2FA and then re-enable it.

    "},{"location":"user-guide/users/2fa/#deactivating-2fa","title":"Deactivating 2FA","text":"

    If 2FA has been enabled for your account, you can disable it by navigating to Settings > Account, clicking the 2-Factor Authentication button and then entering your password for confirmation:

    Should you lose access to your authenticator app or device, you can use your recovery code to regain access to your account. It is a one-time use code that disables 2FA for your account when you use it.

    Alternatively, if you don't remember your recovery code, you can ask an administrator to disable 2FA for you in the User Details dialog of the Admin Web UI or by running the following command in a terminal:

    photoprism users mod --disable-2fa [username]\n

    "},{"location":"user-guide/users/cli/","title":"User Management Commands","text":""},{"location":"user-guide/users/cli/#changing-a-password","title":"Changing a Password","text":"

    Running the following in a terminal changes the password of an existing user without affecting other account settings, e.g. if you cannot remember the currently set password or if there was a problem configuring the initial admin account (replace [username] with the username of the account you want to update):

    photoprism passwd [username]\n

    Note that when you use Docker Compose and do not already have a terminal session open, you must prepend docker compose exec photoprism so that the command is executed within the photoprism container, for example:

    docker compose exec photoprism photoprism passwd admin\n

    This also applies to other terminal commands, including those listed below.

    The examples in our documentation use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    "},{"location":"user-guide/users/cli/#removing-a-password","title":"Removing a Password","text":"

    Changing the authentication of an existing account to a password-less provider like OIDC will not remove a previously set password, so it can still be used to log in (optionally also with 2FA).

    If a local password has been set for such an account that should no longer be used, you can remove it by running the following command in a terminal:

    photoprism passwd --rm [username]\n
    "},{"location":"user-guide/users/cli/#managing-user-accounts","title":"Managing User Accounts","text":"

    As an alternative to the web user interface, you can run the following commands in a terminal to perform tasks such as adding, viewing, editing and deleting user accounts:

    CLI Command Description photoprism users ls [search] Searches existing user accounts photoprism users legacy [search] Searches legacy user accounts photoprism users add [options] [username] Adds a new user account photoprism users show [username] Displays user account information photoprism users mod [options] [username] Modifies an existing user account photoprism users rm [username] Removes a user account photoprism users reset --yes Removes all accounts and resets the database

    Users who experience login problems after upgrading from development builds, or old releases prior to November 2022, can run the photoprism users reset --yes command to recreate the session and user management database tables so they are compatible with the current version. Note that any client access tokens and app passwords that users may have created will also be deleted and must be recreated.

    "},{"location":"user-guide/users/cli/#command-options","title":"Command Options","text":"

    The users add and users mod commands support these flags to set or change account properties:

    Command Flag Description --name NAME, -n NAME full NAME for display in the interface --email EMAIL, -m EMAIL unique EMAIL address of the user --password PASSWORD, -p PASSWORD PASSWORD for local authentication (8-72 characters) --role value, -r value user account ROLE (admin, user, viewer or guest) (default: \"admin\") --auth PROVIDER, -A PROVIDER authentication PROVIDER (default, local, oidc or none) --auth-id ID authentication ID e.g. Subject ID or Distinguished Name (DN) --superadmin, -s make user super admin with full access --no-login, -l disable login on the web interface --webdav, -w allow to sync files via WebDAV --upload-path value, -u value upload files to this sub-folder --disable-2fa deactivate two-factor authentication"},{"location":"user-guide/users/cli/#creating-a-new-account","title":"Creating a New Account","text":"

    The photoprism users add command creates a new user account or offers to restore a previously deleted account with the same username if it exists. For example, you can run the following to add a new admin account with the username \"bob\" and the password \"mysecret\":

    docker compose exec photoprism photoprism users add -p mysecret -n \"Bob\" bob\n

    If you do not specify an initial password with the -p flag, you will be prompted to enter a password for the new account. Further account properties can be set with the flags listed above.

    Some user account roles such as User and Viewer are currently only available with a membership to support development and maintenance.

    "},{"location":"user-guide/users/cli/#viewing-account-details","title":"Viewing Account Details","text":"

    To view the account properties of a specific user, use the show subcommand:

    docker compose exec photoprism photoprism users show bob\n
    "},{"location":"user-guide/users/cli/#searching-user-accounts","title":"Searching User Accounts","text":"

    To list all existing accounts, you can run the following:

    docker compose exec photoprism photoprism users ls\n

    With the photoprism users ls command, you can also find specific accounts based on a search term you provide:

    docker compose exec photoprism photoprism users ls bob\n

    To display a description and the available options for a command, use the --help flag:

    docker compose exec photoprism photoprism users ls --help\n
    "},{"location":"user-guide/users/cli/#viewing-login-attempts","title":"Viewing Login Attempts","text":"

    For security reasons, the authentication logs are not accessible from the web user interface. They can only be viewed in the application service logs or by running the following command in a terminal:

    docker compose exec photoprism photoprism audit logins [username]\n
    "},{"location":"user-guide/users/cli/#command-options_1","title":"Command Options","text":"

    You can combine it with these flags to change the output format and the maximum number of search results:

    Command Flag Description --md, -m format as machine-readable Markdown --csv, -c export as semicolon separated values --tsv, -t export as tab separated values -n LIMIT LIMIT number of results (default: 100)"},{"location":"user-guide/users/cli/#example-report","title":"Example Report","text":"Client IP Username Realm Status Last Login Failed At 172.19.0.1 user api OK 2023-02-03 07:17:46 172.19.0.1 viewer api OK 2023-02-03 07:16:55 172.19.0.1 admin api OK 2023-02-03 06:55:06

    Run photoprism audit reset --yes to clear all audit logs and reset the database table to a clean state.

    "},{"location":"user-guide/users/cli/#session-management","title":"Session Management","text":"

    You can use the following terminal commands to generate, inspect and, if necessary, delete access tokens for the authentication of browsers and other clients (including app passwords):

    CLI Command Description photoprism auth ls [search] Lists currently authenticated users and clients photoprism auth add [username] Adds a new authentication secret for client applications photoprism auth show [identifier] Shows detailed information about a session photoprism auth rm [identifier] Deletes a session by id or access token photoprism auth reset --yes Resets the authentication of all users and clients

    In order to grant limited API access to other applications and services, the photoprism clients add command lets you generate OAuth2 client credentials for them, or you can use the photoprism auth add command to generate access tokens with a limited scope and lifetime.

    Learn more \u203a

    Should you experience login problems, for example after upgrading from a previous release or development preview, we recommend running the photoprism auth reset --yes command in a terminal to reset the auth_sessions table to a clean state and force a re-login of all users. Note that any client access tokens and app passwords that users may have created will also be deleted and must be recreated.

    "},{"location":"user-guide/users/client-credentials/","title":"Authenticating External Apps and Services","text":"

    In order to grant limited access to other applications and services, administrators can use the photoprism auth and photoprism clients subcommands to generate authentication tokens for them. While app passwords are bound to user accounts and can be generated by anyone from the UI, OAuth2 access tokens and client credentials can be used to access the REST API without being bound to an account.

    "},{"location":"user-guide/users/client-credentials/#app-passwords","title":"App Passwords","text":"

    All users can generate app-specific passwords for their own use from the web interface by navigating to Settings > Account and then clicking the Apps and Devices button.

    Alternatively, running the following command in a terminal will generate a new app-specific password e.g. for the admin account, so that WebDAV-compatible clients can synchronize files even if 2FA is enabled for the account or the account password is changed:

    docker compose exec photoprism photoprism auth add -n Sync -s \"webdav\" admin\n

    You will then be shown the generated app password so you can copy it and keep it in a safe place or enter it directly into an app, as you will not be able to see it again:

    |-----------------------------|---------------------|\n| App Password                | Authorization Scope |\n|-----------------------------|---------------------|\n| HY8fxO-8hvNqB-43UV4q-1AZ0vu | webdav              |\n|-----------------------------|---------------------|\n

    For added security, we recommend setting an expiration date for the app passwords and access tokens you generate. Common scopes for use with app passwords are either \"*\" for Full Access or \"webdav\" for WebDAV-compatible file synchronization apps.

    Besides using app passwords to create sessions through the POST /api/v1/session endpoint, developers can also use them as access tokens in the Bearer Authorization header without first creating a session access token.

    "},{"location":"user-guide/users/client-credentials/#command-options","title":"Command Options","text":"

    The following flags can be used with the photoprism auth add command (if you omit name or scope, you will be asked to enter them interactively):

    Command Flag Description --name CLIENT, -n CLIENT CLIENT name to help identify the application --scope SCOPES, -s SCOPES authorization SCOPES e.g. \"metrics\" or \"photos albums\" (\"*\" to allow all) --expires LIFETIME, -e LIFETIME authentication LIFETIME in seconds, after which access expires (-1 to disable the limit) (default: 31536000)"},{"location":"user-guide/users/client-credentials/#authorization-scopes","title":"Authorization Scopes","text":"

    One or more of these scopes can be specified to limit the access to certain API endpoints:

    files, folders, shares, photos, videos, favorites, albums, moments, calendar, people, places, labels, config, settings, services, users, sessions, logs, webdav, metrics

    Clients authenticated with app passwords are unable to change the account password or manage user accounts, even if you specify all scopes or use the wildcard \"*\" to allow all.

    "},{"location":"user-guide/users/client-credentials/#access-tokens","title":"Access Tokens","text":"

    If you do not specify a username as argument for the photoprism auth add command, a client access token will be generated (the same flags and scopes as above can be used to limit token authorization and lifetime):

    |--------------------------------------------------|---------------------|\n| Access Token                                     | Authorization Scope |\n|--------------------------------------------------|---------------------|\n| 7dbfa37b5a3db2a9e9dd186479018bfe2e3ce5a71fc2f955 | files folders       |\n|--------------------------------------------------|---------------------|\n

    Generating access tokens is a good choice for developers and other advanced users to connect scripts and external services to the PhotoPrism API, e.g. services that collect metrics or start indexing at regular intervals.

    Please note, however, that client access tokens cannot be used to synchronize files via WebDAV, even if the token authorization scope is set to \"webdav\" or \"*\", as this requires a registered user account. Access tokens can also not be used as a direct password replacement for apps, since clients are not allowed to use the POST /api/v1/session endpoint, which is required for logging in through the user interface.

    "},{"location":"user-guide/users/client-credentials/#client-credentials","title":"Client Credentials","text":"

    If clients support authentication via OAuth2 client credentials, you can use the following terminal commands to generate a client_id and client_secret for them, list registered clients, and delete client credentials that are no longer used:

    CLI Command Description photoprism clients ls [search] Lists registered client applications photoprism clients add [username] Registers a new client application photoprism clients show [identifier] Shows client configuration details photoprism clients mod [identifier] Updates client application settings photoprism clients rm [identifier] Deletes the specified client application photoprism clients reset --yes Removes all registered client applications

    For example, running the following in a terminal will generate credentials for Prometheus, with access limited to the metrics endpoint:

    docker compose exec photoprism photoprism clients add -n Prometheus -s metrics\n

    You will then be shown the generated client_id and client_secret so you can copy them and keep them in a safe place:

    |------------------|----------------------------------|\n| Client ID        | Client Secret                    |\n|------------------|----------------------------------|\n| csce0w2joodmirvi | 5VKkBeZLDvojjpE9XzCMXShnrxmxHWvN |\n|------------------|----------------------------------|\n

    OAuth2 client credentials cannot be directly used for synchronizing files via WebDAV, as a password replacement for apps, or for logging in to the web interface.

    "},{"location":"user-guide/users/client-credentials/#command-options_1","title":"Command Options","text":"

    The following parameters can be used with the photoprism clients add command, e.g. to limit the number of access tokens the client can request:

    Command Flag Description --name CLIENT, -n CLIENT CLIENT name to help identify the application --role ROLE, -r ROLE client authorization ROLE (default: \"client\") --scope SCOPES, -s SCOPES client authorization SCOPES e.g. \"metrics\" or \"photos albums\" (\"*\" to allow all) --expires LIFETIME, -e LIFETIME access token LIFETIME in seconds, after which a new token must be requested (default: 86400) --tokens NUMBER, -t NUMBER maximum NUMBER of access tokens that the client can request (-1 to disable the limit) (default: 10)

    If you omit the name or scope parameter, you will be asked to enter them interactively. One or more of these scopes can be specified to limit the access to certain API endpoints:

    files, folders, shares, photos, videos, favorites, albums, moments, calendar, people, places, labels, config, settings, services, users, sessions, logs, webdav, metrics

    When requesting access tokens, clients can further restrict the scope of the tokens by passing the scope parameter to the POST /api/v1/oauth/token endpoint.

    "},{"location":"user-guide/users/libraries/","title":"Multiple Libraries","text":"

    PhotoPrism\u00ae Plus includes advanced multi-user functionality and additional account roles. These roles are intended for situations where you want other people to have access to your library, such as giving family members access to your pictures without granting write permissions or exposing private content.

    It is recommended to set up additional instances if you have multiple users in a family, so that everyone can manage their own files independently. This way you can avoid problems with conflicting library settings, file permissions, and dealing with duplicates.

    In future versions, users will be able to share albums and other content in a decentralized way, regardless of where their library is hosted. We are also working on a dedicated web interface for managing multiple libraries and user accounts, which will be made available as a separate tool.

    Our ultimate goal is to make personal sharing compatible with other apps like Pixelfed and Mastodon.

    "},{"location":"user-guide/users/roles/","title":"Roles and Permissions","text":"Role Search Library View & Download Upload WebDAV Manage admin optional user optional viewer except private except private guest shared visitor shared"},{"location":"user-guide/users/roles/#admin","title":"Admin","text":"

    Admins have unrestricted access to all pictures, albums, and settings.

    Regular Admins can lose their privileges due to an intentional or accidental role change. However, accounts with the optional \"superadmin\" status (can be set with the -s flag) retain their admin privileges even if they are assigned a non-admin or invalid role. This is to prevent them from locking themselves out.

    When Super Admins change settings such as the language or theme, these automatically become the default settings for other users, unless they have explicitly made a different choice. In addition, global feature flags can only be changed by Super Admins.

    "},{"location":"user-guide/users/roles/#user","title":"User","text":"

    Users have full access to the library and can view, edit, and delete all pictures and albums. Unlike Admins, Users cannot view or change the Library and Advanced Settings, only personal preferences such as theme, language, and password. In addition, their WebDAV access can be disabled. Future releases may include more ways to customize user privileges, e.g. with individual account attributes.

    "},{"location":"user-guide/users/roles/#viewer","title":"Viewer","text":"

    Viewers are similar to regular Users, except that they do not have write access to the library and cannot see content that has been archived or marked private. They also cannot upload/import files or trigger indexing. Like all registered users, Viewers can change and save personal preferences such as theme, language, and password.

    "},{"location":"user-guide/users/roles/#guest","title":"Guest","text":"

    Guests have read-only access to view and download the resources that other users have shared with them. They can also change personal settings such as theme, language, and password.

    "},{"location":"user-guide/users/roles/#visitor","title":"Visitor","text":"

    Visitors cannot be added manually. This special role is tied to a system account that represents anonymous users who use links to view albums or other content that has been shared with them. Visitors can only access these resources and cannot log in with a username or password. Other than guests, they also cannot retain their personal settings for longer than their browsing session lasts.

    Some user account roles such as User and Viewer are currently only available with a membership to support development and maintenance.

    "},{"location":"user-guide/users/sharing/","title":"Sharing with Guests","text":"

    To share albums with guests, admins and users can create share links, just like when sharing with friends who don't have an account.

    When users with limited privileges open a share link while being logged in, they get permanent read-only access to the shared resources until the link is removed or expires.

    In a future release, you will be able to share content with local users directly from the web interface without having to create links first.

    "}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000000..22ed580d97 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,731 @@ + + + + https://docs.photoprism.app/ + 2024-10-21 + + + https://docs.photoprism.app/credits/ + 2024-10-21 + + + https://docs.photoprism.app/known-issues/ + 2024-10-21 + + + https://docs.photoprism.app/release-notes/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/code-quality/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/configuration/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/directories/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/documentation/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/faq/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/issues/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/pull-requests/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/setup/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/tests/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/translations-weblate/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/translations/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/api/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/api/auth/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/api/docs/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/api/oauth2/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/api/oidc/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/api/search/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/api/thumbnails/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/api/clients/go/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/database/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/database/migrations/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/database/schema/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/machine-learning/classification/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/machine-learning/face-recognition/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/machine-learning/models/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/media/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/media/colors/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/media/heif/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/media/import/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/media/live/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/media/raw/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/media/samples/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/media/storage/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/media/thumbnails/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/media/videos/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/metadata/cameras/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/metadata/colors/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/metadata/geocoding/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/metadata/orientation/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/metadata/perceptual-hashes/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/metadata/xmp/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/metadata/exif/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/metadata/exif/edit/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/native-apps/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/security/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/security/go/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/security/policy/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/technologies/broadway/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/technologies/docker/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/technologies/external-apis/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/technologies/golang/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/technologies/tensorflow/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/technologies/yaml/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/ui/browsers/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/ui/components/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/ui/design/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/ui/infinite-scrolling/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/ui/introduction/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/ui/maps/ + 2024-10-21 + + + https://docs.photoprism.app/developer-guide/ui/screenshots/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/config-options/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/docker-compose/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/docker/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/faq/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/raspberry-pi/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/updates/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/using-a-cdn/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/using-https/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/advanced/backups/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/advanced/caching/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/advanced/databases/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/advanced/docker-security/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/advanced/docker-volumes/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/advanced/kubernetes/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/advanced/nginx-proxy-setup/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/advanced/openid-connect/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/advanced/scalability/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/advanced/transcoding/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/advanced/migrations/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/advanced/migrations/mariadb-to-sqlite/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/advanced/migrations/sqlite-to-mariadb/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/cloud/digitalocean/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/cloud/pikapods/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/config-files/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/config-files/defaults/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/config-files/settings/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/nas/asustor/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/nas/openmediavault/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/nas/qnap/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/nas/synology/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/nas/unraid/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/portainer/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/ports/freebsd/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/proxies/apache-2/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/proxies/caddy-1/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/proxies/caddy-2/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/proxies/haproxy/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/proxies/nginx/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/proxies/swag/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/proxies/traefik/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/raspberry-pi/microsd-image/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/troubleshooting/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/troubleshooting/browsers/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/troubleshooting/docker/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/troubleshooting/firewall/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/troubleshooting/logs/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/troubleshooting/mariadb/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/troubleshooting/metadata/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/troubleshooting/performance/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/troubleshooting/raspberry-pi/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/troubleshooting/sqlite/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/troubleshooting/windows/ + 2024-10-21 + + + https://docs.photoprism.app/getting-started/vpn/tailscale/ + 2024-10-21 + + + https://docs.photoprism.app/license/agpl/ + 2024-10-21 + + + https://docs.photoprism.app/license/apache/ + 2024-10-21 + + + https://docs.photoprism.app/license/docs/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/faq/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/first-steps/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/navigate/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/pwa/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/backups/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/backups/export/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/backups/external-storage/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/backups/folders/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/backups/restore/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/library/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/library/duplicates/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/library/files/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/library/import/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/library/metadata/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/library/originals/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/library/upload/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/library/webdav/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/albums/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/archive/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/calendar/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/delete/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/download/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/edit/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/folders/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/labels/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/moments/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/panoramas/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/people/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/places/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/private/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/review/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/rotate/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/scans/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/stacks/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/organize/video/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/search/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/search/filters/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/search/views/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/settings/account/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/settings/advanced/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/settings/general/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/settings/library/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/settings/sync/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/share/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/share/services-share/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/sync/dropbox/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/sync/mobile-devices/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/sync/services-sync/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/sync/webdav/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/use-cases/apple/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/use-cases/flickr/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/use-cases/google/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/users/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/users/2fa/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/users/cli/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/users/client-credentials/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/users/libraries/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/users/roles/ + 2024-10-21 + + + https://docs.photoprism.app/user-guide/users/sharing/ + 2024-10-21 + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 0000000000..0625296dc8 Binary files /dev/null and b/sitemap.xml.gz differ diff --git a/team/index.html b/team/index.html new file mode 100644 index 0000000000..f712c5f471 --- /dev/null +++ b/team/index.html @@ -0,0 +1,15 @@ + + + + + + Redirecting... + + + + + + +Redirecting... + + diff --git a/terms/index.html b/terms/index.html new file mode 100644 index 0000000000..f43ecbb1c5 --- /dev/null +++ b/terms/index.html @@ -0,0 +1,15 @@ + + + + + + Redirecting... + + + + + + +Redirecting... + + diff --git a/user-guide/advanced/metadata/index.html b/user-guide/advanced/metadata/index.html new file mode 100644 index 0000000000..8a4e5374b8 --- /dev/null +++ b/user-guide/advanced/metadata/index.html @@ -0,0 +1,15 @@ + + + + + + Redirecting... + + + + + + +Redirecting... + + diff --git a/user-guide/backups/export/index.html b/user-guide/backups/export/index.html new file mode 100644 index 0000000000..6944f6a692 --- /dev/null +++ b/user-guide/backups/export/index.html @@ -0,0 +1,7758 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Metadata Exports - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Metadata Exports

    +

    Control over your data doesn't end with the ability to create and restore a database backup. +PhotoPrism additionally provides you with human-readable YAML files that allow you to view and restore the metadata of your albums and photos, even if you didn't create a regular database backup or have lost it.

    +

    If backups have not been disabled in the Advanced Settings, metadata exports of all your albums and photos are automatically created in your storage folder. They will also be updated when changes are made.

    +

    Be aware that the original data remains in your database. Any changes you make to the files therefore have no effect on it and will not become visible on the user interface, unless your database gets lost and the index is restored from the files.

    +

    Album Backups

    +

    Album backups are created for the following album types: album, folder, state, moment and month. +You find those backups inside your storage path in /albums.

    +

    Album Backups

    +

    For each album the following metadata is stored in the YAML file:

    +
      +
    • UID, Slug, Type, Title, Location, Category, Description, Sort Order, Country, CreatedAt, UpdatedAt, Photos (UID + date the photo was added to the album)
    • +
    +

    Folder Backups

    +

    For each folder the following metadata is stored in the YAML file:

    +
      +
    • UID, Slug, Type, Title, Location, Category, Description, Filter, Sort order, Country, Year, Month, Day, CreatedAt, UpdatedAt
    • +
    +

    Month Backups

    +

    For each month the following metadata is stored in the YAML file:

    +
      +
    • UID, Slug, Type, Title, Location, Category, Description, Filter, Sort Order, Country, Year, Month, CreatedAt, UpdatedAt
    • +
    +

    State Backups

    +

    For each state the following metadata is stored in the YAML file:

    +
      +
    • UID, Slug, Type, Title, Location, Category, Description, Filter, Sort Order, Country, CreatedAt, UpdatedAt
    • +
    +

    Moment Backups

    +

    For each moment the following metadata is stored in the YAML file:

    +
      +
    • UID, Slug, Type, Title, Location, Category, Description, Filter, Sort Order, Country, Year, CreatedAt, UpdatedAt
    • +
    +

    Photo Backups

    +

    PhotoPrism creates YAML backup files for each photo in your sidecar path.

    +

    The following metadata is stored:

    +
      +
    • TakenAt + Source, UID, Type, Title + Source, Description + Source, OriginalName, TimeZone, PlaceSrc, Altitude, + Lat, Lng, Year, Month, Day, Iso, Exposure, FNumber, FocalLength, Quality, Favorite, Private, Keywords + Source, + Notes + Source, Subject + Source, Artist + Source, Copyright + Source, License + Source, CreatedAt, UpdatedAt, EditedAt, DeletedAt (Archived)
    • +
    +

    Helpful information can be found on GitHub as well.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/backups/external-storage/index.html b/user-guide/backups/external-storage/index.html new file mode 100644 index 0000000000..5504127d54 --- /dev/null +++ b/user-guide/backups/external-storage/index.html @@ -0,0 +1,7510 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + External Storage - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Using External Storage

    +

    Due to their performance and because they can lose data over time, we do not recommend using conventional SD cards, USB sticks or external USB 2 hard disk drives to store your files, except for backups.

    +

    External Solid-State Drives (SSD) connected via USB 3 are generally reliable and fast enough to keep your originals, database, and storage folders. This way you can, for example, do the indexing on one computer, eject the drive, and then connect it to another computer to browse your pictures.

    +

    Note, though, that database files may not be binary compatible in some cases (e.g. if the version or computer architecture does not match) and could also get corrupted when you disconnect an external drive before all changes have been written to disk. We therefore recommend that you regularly create database backups, so you can easily restore your index if necessary.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/backups/folders/index.html b/user-guide/backups/folders/index.html new file mode 100644 index 0000000000..8b02e2d640 --- /dev/null +++ b/user-guide/backups/folders/index.html @@ -0,0 +1,7850 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Folder Overview - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Folder Overview

    + +

    The following gives you an overview of the most important folders that PhotoPrism uses:

    +

    Originals

    +

    The originals folder contains the original photo and video files of your library. PhotoPrism will not move or rename the files in this folder without a user requesting it. Unless read-only mode is enabled, new files can be added using the web upload dialog, the import functionality, or by mounting the folder via WebDAV.

    +

    Its location can be changed by setting the PHOTOPRISM_ORIGINALS_PATH environment variable.

    +

    Import

    +

    When importing, files are copied or moved from the import folder to the originals folder. In the process, duplicates are automatically skipped, and the imported files are given a unique file name and are sorted by year and month.

    +

    The location of the import folder can be changed by setting the PHOTOPRISM_IMPORT_PATH environment variable.

    +

    Storage

    +

    Unless you use a custom configuration, the storage folder is used to keep config, cache, backup, thumbnail, and sidecar files.

    +

    Its location can be changed by setting the PHOTOPRISM_STORAGE_PATH environment variable.

    +

    Learn more ›

    +
    +

    We recommend not to configure the storage folder to be inside the originals folder unless the name starts with a . to indicate that it is hidden.

    +
    +

    Cache

    +

    The cache folder contains the subdirectories json and thumbnails for storing ExifTool JSON files and thumbnail images.

    +

    Its location can be changed by setting the PHOTOPRISM_CACHE_PATH environment variable.

    +

    JSON

    +

    Unless you have disabled ExifTool in Settings > Advanced, it may used to create JSON files with the metadata of a file in this directory e.g. when indexing or importing new files.

    +

    Thumbnails

    +

    PhotoPrism creates thumbnails in different sizes for each photo. Those are stored in the thumbnails directory. +More information on thumbnails can be found here.

    +

    Sidecar

    +

    The sidecar folder contains YAML backup files for each picture as well as e.g. automatically generated JPEG versions of RAW images. +Both can be configured in Settings > Advanced.

    +

    Its location can be changed by setting the PHOTOPRISM_SIDECAR_PATH environment variable.

    +

    Config

    +

    The config folder contains configuration files and certificates.

    +

    Its location can be changed by setting the PHOTOPRISM_CONFIG_PATH environment variable.

    +

    Backup

    +

    The backup folder contains database dumps as well as album backup files and is located in the storage folder by default.

    +

    Its location can be changed by setting the PHOTOPRISM_BACKUP_PATH environment variable.

    +

    Temp

    +

    Uploads, downloads, and other temporary files may be temporarily stored in the temp folder.

    +

    Its location can be changed by setting the PHOTOPRISM_TEMP_PATH environment variable.

    +

    Assets

    +

    The assets folder contains static resources such as machine learning models, icons, and templates.

    +

    Its location can be changed by setting the PHOTOPRISM_ASSETS_PATH environment variable.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/backups/index.html b/user-guide/backups/index.html new file mode 100644 index 0000000000..9badcbc4cb --- /dev/null +++ b/user-guide/backups/index.html @@ -0,0 +1,7704 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Creating Backups - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Creating Backups

    +

    At a minimum, a backup of PhotoPrism should include the files in your originals folder and a copy of the index database. We also recommend backing up the storage folder so that you don't need to recreate any thumbnail or sidecar files, and your backup includes the complete configuration.

    +
    +

    The easiest way to create a full backup is to first run the backup command to generate a database dump as shown below. +Then back up your originals and storage folders using any standard file backup utility.

    +
    +

    Scheduled Backups

    +

    By default, PhotoPrism 240523-923ee0cf7 and newer versions automatically create daily database backups for you, with up to 3 copies being retained. The schedule, the type of backups, and the number of backups to be retained can be changed in the configuration.

    +

    Backup Command

    +

    You can run the following command in a terminal to manually create a new MariaDB or SQLite database backup:

    +
    docker compose exec photoprism photoprism backup -i -f
    +
    +

    If you are using Podman on a Red Hat-compatible Linux distribution:

    +
    podman-compose exec photoprism photoprism backup -i -f
    +
    +

    By default, a backup is created in storage/backup/mysql/[YYYY-MM-DD].sql. A custom backup base folder can be configured with PHOTOPRISM_BACKUP_PATH

    +

    Omit the -f flag if you do not want to overwrite existing files. You can also specify a custom filename as an argument (or - to write the SQL dump to stdout):

    +
    docker compose exec photoprism photoprism backup -i [filename]
    +
    +

    Alternative ways to create SQL dumps from SQLite are shown in our advanced backup guide.

    +
    +

    Note that our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    +
    +

    Important Folders

    +

    Originals

    +

    The originals folder contains your original photo and video files. You can back it up and restore it using any standard file backup program if you have not already set this up.

    +

    Storage

    +

    SQLite, config, cache, backup, thumbnail and sidecar files are saved in the storage folder. As with the originals folder, the exact path on your computer depends on your configuration.

    +

    We recommend that you back up this folder as well so that you don't need to recreate the thumbnails and have a complete backup of your configuration. As for the originals folder, you can use any standard file backup utility to do this.

    +
    +

    Depending on the resources available to us, a future version may include additional features so that you do not have to rely on external tools to perform file backups and can use a web interface.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/backups/restore/index.html b/user-guide/backups/restore/index.html new file mode 100644 index 0000000000..c045ea5ded --- /dev/null +++ b/user-guide/backups/restore/index.html @@ -0,0 +1,7594 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Restoring Backups - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Restoring Backups

    +

    To restore your instance, you must have the files in your originals folder and a copy of the index database. We also recommend having a backup of the storage folder so that you don't need to recreate any thumbnail or sidecar files, and your backup includes the complete configuration:

    +
      +
    • if you have a backup copy of your storage and originals folders, the easiest way is to restore those folders first and then run the restore command in case you are using MariaDB
    • +
    • otherwise, you additionally need to perform a complete rescan of your library to recreate missing sidecar and thumbnail files
    • +
    • some of the metadata and your albums can also be recovered from YAML sidecar files even if you don't have a copy of the index database, unless you have disabled this feature
    • +
    +

    Restore Command

    +

    To restore the index from an existing MariaDB dump, you can run the following command:

    +
    docker compose exec photoprism photoprism restore -i -f
    +
    +

    If you are using Podman on a Red Hat-compatible Linux distribution:

    +
    podman-compose exec photoprism photoprism restore -i -f
    +
    +

    This will automatically search the backup folder for the most recent index dump and restore it. A custom backup base folder can be configured with PHOTOPRISM_BACKUP_PATH.

    +

    Omit the -f flag to prevent overwriting an existing index. As with the backup command, you can also specify a specific dump filename as an argument:

    +
    docker compose exec photoprism photoprism restore -i [filename]
    +
    +

    When the database is restored, all user accounts and passwords are restored as well. If you have changed your password, you must thus use the old password to sign in.

    +
    +

    Note that our examples use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/faq/index.html b/user-guide/faq/index.html new file mode 100644 index 0000000000..493852ae67 --- /dev/null +++ b/user-guide/faq/index.html @@ -0,0 +1,8157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FAQ - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Frequently Asked Questions

    +

    General

    +
    +Does your software depend on any external services? +

    As explained in our Privacy Policy, reverse geocoding and interactive world maps depend on retrieving the necessary information from us and MapTiler AG, headquartered in Switzerland. Both services are provided with a very high level of privacy and confidentiality.

    +

    Your use of these services is fully covered by us. Depending on your usage, this can save you much more than the cost of a PhotoPrism+ Membership, since other providers generally charge usage-based fees and often don't allow you to cache the data they provide, compromising performance and your privacy with unnecessary requests.

    +

    View Privacy Policy › View Compliance FAQ ›

    +

    In order to successfully set up your installation and view location details in PhotoPrism, you must allow incoming requests as well as those to our Geocoding API and Docker if you have a firewall installed, and make sure that your Internet connection is working:

    +

    +

    Learn more ›

    +
    +
    +Why are some features only available to members? +

    PhotoPrism is 100% self-funded and independent. Voluntary donations do not cover the cost of a team working full time to provide you with updates, documentation, and support. It is your decision whether you want to sign up to enjoy additional benefits.

    +

    View Membership FAQ ›

    +
    +
    +What are the advantages of purchasing a commercial license? +

    A key difference between the public license and a commercial license agreement is that you get access to additional support and configuration options, as well as the right to customize functionality to your needs without having to publicly disclose your changes. Our Compliance FAQ gives answers to the most frequently asked questions about product compliance and scalability.

    +

    Compare Team Editions ›

    +
    +
    +When exactly will new features be released? +

    Our Project Roadmap shows what tasks are in progress and what features will be implemented next. You may give ideas you like a thumbs-up, so we know what's most popular.

    +

    Be aware that we have a zero-bug policy and do our best to help users when they need support or have other questions. This comes at a price though, as we can't give exact release dates for new features. Our team receives many more requests than can be implemented, so we want to emphasize that we are in no way obligated to implement the features, enhancements, or other changes you request. We do, however, appreciate your feedback and carefully consider all requests.

    +

    Since sustained funding is key to quickly releasing new features, we encourage all users to support our mission by signing up as a member or purchasing a commercial license.

    +
    +

    Membership

    +
    +How can I activate my membership? +

    To connect a new instance to your membership account, you will need to log in with the super admin user that is automatically created during setup (see your compose.yaml or docker-compose.yml file or the app store documentation), and then follow the steps described in our activation guide.

    +

    View Activation Guide ›

    +
    +
    +Are there alternatives to a recurring subscription? +

    Yes, our Plus members automatically receive a free Lifetime Essentials membership after 24 months. Likewise, Silver members receive a Lifetime Plus membership after 24 months, Gold members after 12 months, and Platinum members after only 6 months.

    +

    If you would like to sign up for a Silver, Gold or Platinum membership, you can do so either directly on our website or on Patreon. In addition, we are working on a Plus Feature Pack that includes just the features without support, so we can offer it to you at a lower price.

    +

    Note that as a lifetime member you will always receive updates and support for your personal use from us, unlike with so-called lifetime licenses, which may only be good until the next major version is released.

    +

    View Membership FAQ ›

    +
    +
    +What happens if I cancel my membership? +

    If you are eligible for a Lifetime Essentials or Plus membership, you can continue to use these features even if you decide to stop supporting us. Otherwise, you can continue to use all the freely available features. In no case will you lose access to your pictures.

    +

    Compare Features ›

    +
    +

    User Interface

    +
    +Can I select multiple pictures at once? +

    Yes, this is possible. How it works depends on what kind of device you use.

    +

    Desktop Browser

    +

    Select the first picture by clicking in the lower right corner.

    +

    The user interface is now in selection mode:

    +
      +
    • to additionally select individual pictures, click them anywhere except on the play/view icons in the corner
    • +
    • to select multiple pictures at once, use a shift+click to select all pictures between the last selected picture and the one you shift+click
    • +
    +

    Mobile Devices

    +

    Select the first picture with a long touch.

    +

    The user interface is now in selection mode:

    +
      +
    • to additionally select individual pictures, touch them anywhere except on the play/view icons in the corner
    • +
    • to select multiple pictures at once, use a long touch to select all pictures between the last selected picture and the one you long touch
    • +
    +
    +
    +Can I use trees for organizing my pictures and albums? +

    Except in Library > Originals and for object classification in Labels, PhotoPrism does not +support hierarchically organized content for a number of reasons:

    +

    First, there are many tools (including Windows Explorer and Mac OS Finder) that already browse folders in such a way.

    +

    A common UX challenge is dealing with namespaces. +For example, the album "Berlin" may exist 5 times in different parts of a tree. +To avoid ambiguities, simple input fields need to be replaced with a tree browser that +shows the complete context. +This is especially difficult on mobile screens.

    +

    Personal albums can typically be browsed by time, with optional filters for more specific results. +This is different in Enterprise asset management, where trees are required to manage +responsibilities & permissions. +We might do a special release for professional users later.

    +

    While you have complete freedom with organizing your original files and folders, +we don't think trees should be an integral part of our user interface. +Most users won't be able to sort their memories in a strictly hierarchical way +and prefer to explore them in multiple dimensions instead.

    +
    +

    Maps & Places

    +
    +Why are some pictures positioned at unvisited locations on the map? +

    PhotoPrism can estimate the location of pictures taken without GPS information by extrapolating it from the location of other pictures taken on the same day. These estimates can be disabled in the settings if you don't want them.

    +
    +
    +Are the keys for using interactive world maps provided free of charge? +

    All users have access to a high-resolution vector map that we host on our own infrastructure, so no commercial API key is required. It is based on data published by OpenStreetMap (OSM).

    +

    In addition, we automatically provide our members and business customers with an API key for MapTiler's commercial service, which includes satellite, outdoor and 3D maps. You can test these on our public demo.

    +

    Learn more ›

    +
    +
    +Why don't you use the free map tile service provided by OpenStreetMap? +

    Other free and open-source software sometimes uses the public maps that OpenStreetMap provides for development and testing. These are not intended for end-user applications like ours.

    +

    Using their service also means that their usage and privacy policies apply, as your request data is stored and used to generate publicly available reports. This differs from our services, which ensure a high level of privacy and provide a better user experience with faster loading times.

    +

    Learn more ›

    +
    +

    Media Library

    +
    +What media file types are supported? +

    PhotoPrism supports indexing, viewing, and converting most popular image, video and RAW formats, including JPEG, PNG, GIF, BMP, HEIF, HEIC, MP4, MOV, WebP, and WebM. +TIFF is partially supported +without extensions like GeoTIFF.

    +

    When indexing, a JPEG or PNG sidecar file is automatically created for videos and images in other formats, such as RAW or vector graphics. It is needed for thumbnail generation, image classification, and face detection. JPEG XL support is planned as soon as it is generally available and enough compatible tools exist.

    +

    If installed, converting RAW files is possible with the following converters (our Docker image includes both):

    + +

    On a Mac, RAW files can also be converted with Sips (supported cameras). +Our goal is to provide top-notch support for all RAW formats, regardless of camera make and model. +Please let us know about any issues with a particular camera or file format.

    +

    For maximum browser compatibility, video codecs and containers supported by FFmpeg can be transcoded to MPEG-4 AVC on demand, just as still images can be extracted for thumbnail creation.

    +

    Make sure you have JSON sidecar files enabled if you have videos, live photos, and/or animated GIFs so that video-specific metadata such as codec, frames, and duration can be extracted, indexed, and searched.

    +

    For a complete list of file formats and extensions, see our downloadable Feature Overview.

    +
    +
    +What metadata sidecar file types are supported? +

    Currently, three types of file formats are supported:

    +

    JSON

    +

    If not disabled via PHOTOPRISM_DISABLE_EXIFTOOL or --disable-exiftool, ExifTool is used +to automatically create a JSON sidecar for each media file. In this way, embedded XMP and video metadata can also be indexed. +Native metadata extraction is limited to common Exif headers. Note that this causes small amount of overhead when +indexing for the first time.

    +

    JSON files can also be useful for debugging, as they contain the full metadata and can be processed with common +development tools and text editors.

    +

    Metadata JSON files exported from Google Photos can be read as well. Support for more schemas may be added over time.

    +

    YAML

    +

    Unless disabled by setting the PHOTOPRISM_SIDECAR_YAML option to "false" in your configuration, PhotoPrism automatically creates/updates human-friendly YAML sidecar files during indexing and after manual editing of fields such as title, date, or location. They serve as a backup in case the database (index) is lost, or when folders are synchronized with a remote instance.

    +

    Like JSON, YAML files can be opened with common development tools and +text editors. However, changes are not synchronized with the original index, as this could overwrite existing data.

    +

    XMP

    +

    XMP (Extensible Metadata Platform) is an XML-based metadata container format developed by Adobe. +It provides many more fields (as part of embedded models like Dublin Core) than Exif. This also makes it difficult - if not +impossible - to provide full support. Reading title, copyright, artist, and description from XMP sidecar files is +implemented as a proof-of-concept, contributions are welcome. Indexing of +embedded XMP is only possible via ExifTool, see above.

    +
    +
    +Why are my video files not indexed? +

    In case FFmpeg is disabled or not installed, videos cannot be indexed because still images cannot be created. +You should also have ExifTool enabled to extract metadata such as duration, resolution, and codec.

    +
    +
    +Some files seem hidden, where are they? +

    If the quality filter is enabled, you might find them in Search > Review. Otherwise, their +format may not be supported, they may be corrupted, or they may be stacked with other files if their name, +exact date & location, or unique image ID indicate they belong to the same photo. You may then unstack +them if this happened by mistake e.g. because of bad metadata.

    +

    View Troubleshooting Checklist ›

    +
    +
    +For what reasons can files be stacked? +
      +
    1. Files that share the same file and folder name (except for the file extension) are always stacked, for example /2018/IMG_1234.jpg and /2018/IMG_1234.avi
    2. +
    3. Files with sequential names like /2018/IMG_1234 (2).jpg and /2018/IMG_1234 (3).jpg can be stacked as well (optional)
    4. +
    5. File metadata indicates that the pictures were taken at the same position within the same second (optional)
    6. +
    7. File metadata includes the same Unique Image ID or XMP Instance ID (optional)
    8. +
    +

    You can change your preferences for 2 - 4 in the Stacks section under Settings > Library.

    +

    Note that it is not possible to disable stacking of files with the same name as this would break important functionality, most notably support for Apple Live Photos (which consist of a photo and a video file), any other multi-file/hybrid formats like RAW/JPEG, and indexing of metadata from XMP/JSON sidecar files.

    +
    +
    +Are files automatically unstacked when I change the settings? +

    When you change the stacks-related settings under Settings > Library, files that are already stacked will not be unstacked automatically. This is because unstacking is a resource-intensive operation that requires each file to be re-indexed.

    +

    The result also depends on the exact order in which you unstack the files, as non-media sidecar files, for example, remain bound to the remaining media file in a stack. We consider providing a command for this in a future release and appreciate any contributions in this regard.

    +

    If you are new to PhotoPrism and want to re-index your library with different settings, you can run the photoprism reset command in a terminal to reset the index and start from scratch. Learn more ›

    +
    +
    +I already indexed some files. Why are Folders, Calendar and Moments still empty? +

    Folders, Calendar and Moments are populated at the end of the indexing process.

    +
    +
    +Why does the count in Search not match the count of files in Originals? +

    Library > Originals shows actual files, whereas Search counts unique photos and videos.

    +

    Photos and videos may have more than one file, for example:

    +
      +
    • A raw file + related jpg file + related xmp file = 3 files, 1 photo
    • +
    • A mp4 file + related jpg file = 2 files, 1 video
    • +
    +

    It is also possible that multiple .jpg files are stacked because they are related to each other.

    +
    +
    +When should I perform a complete rescan? +

    We recommend performing a complete rescan after major updates to take advantage of new search filters and sorting options. Be sure to read the notes for each release to see what changes have been made and if they might affect your library, for example, because of the file types you have or because new search features have been added. If you encounter problems that you cannot solve otherwise (i.e. before reporting a bug), please also try a rescan and see if it solves the problem.

    +

    You can start a rescan from the user interface by navigating to Library > Index, selecting "Complete Rescan", and then clicking "Start". Manually entered information such as labels, people, titles or descriptions will not be modified when indexing, even if you perform a "complete rescan". Be careful not to start multiple indexing processes at the same time, as this will lead to a high server load.

    +
    +
    +Can I use the web interface to permanently delete files? +

    Yes, you can permanently delete files.

    +
    +
    +In which cases could files in the originals folder get modified? +

    PhotoPrism generally does not write to the originals folder, with the following exceptions: (1) You rotate an image in the user interface, so its Exif header must be updated. (2) You unstack files that were stacked based on their name, so they must be renamed. (3) You add files using the import functionality or the web upload. (4) You manually delete files in the user interface. (5) You have configured the originals folder as your sidecar folder. (6) You access the originals folder with a WebDAV client to manage your files without having read-only mode enabled.

    +
    +

    RAW Images

    +
    +What is a RAW image file? +

    Professional and semi-professional photographers often keep their originals in a lossless RAW format, close to how they were taken with the physical sensor, rather than in a compressed image format like JPEG, especially if they shoot with a digital SLR camera. Newer mobile phones may also be able to capture images in RAW mode. Our goal is to provide top-notch support for all RAW images, regardless of camera make and model. A full list of file types and extensions can be found in our Knowledge Base.

    +

    Since web browsers generally cannot display RAW image files directly, they must be converted. This is done during import or initial indexing. It can also be triggered manually in a terminal with the photoprism convert command.

    +
    +
    +Will JPEGs be updated when the related RAW or XMP files change? +

    JPEGs are currently not regenerated when related RAW or XMP files change. RAW files are digital negatives by design. PhotoPrism therefore assumes that their image information is immutable.

    +

    XMP files can affect the appearance, but most of the metadata they contain, such as title and description, does not. Creating JPEGs from RAW files is a time-consuming task, and in most cases would cause a huge, unjustified amount of overhead. In addition, the rendering information in XMP files is not well standardized. For example, changes you make in Photoshop may not be compatible with Darktable.

    +

    We recommend manually updating existing JPEG sidecar files as needed or creating additional JPEGs, so you can choose between different versions. New files and other metadata changes are detected and reflected in the index as usual when your library is scanned.

    +
    +
    +Are edits preserved when converting a RAW image with an XMP sidecar file? +

    PhotoPrism currently supports Darktable and RawTherapee as RAW image converters. Darktable fully supports XMP sidecar files, RawTherapee might only partially. However, XMP is only a "container" format, so the fields (namespaces) used there to indicate how an image should be converted (as well as other metadata) differ between Lightroom/Photoshop, Darktable, and RawTherapee.

    +

    In other words, just because an application generally supports XMP that doesn't mean it can use metadata created with another application or by another vendor like Adobe. If you think that's confusing, well, that's because it is. You have an open format, but you still suffer from vendor lock-in - probably not entirely unintentional on Adobe's part.

    +

    From our experience, some basic edits done with Adobe tools - such as cropping - might be preserved when you convert the same RAW image with other software like Darktable. Advanced edits, such as lens or color corrections, will likely not be applied.

    +
    +

    Live Photos

    +
    +Why can't I play Live Photos or find stacks when I search for specific images? +

    Our search API and user interface perform a file search. This is intentional since "stacks" can contain files of different types and properties, such as color.

    +

    For example, there may be color and monochrome versions. Now, when you search for them or sort them by color, the user interface must display individual files. Otherwise, the results showing a color image/video when you filter by monochrome would make no sense.

    +

    Likewise, if you search for filename.mp4.*, you will find only JPEGs without video, because the video file extension is .mp4 without an extra dot at the end.

    +

    We recommend using the path: and/or name: filters with wildcards if searching for individual files limits the search results too much. Most users will want to find all related files so that they can be displayed together, e.g. as live photos consisting of a video and an image.

    +

    You can combine these filters with other filters such as live to ensure that the results include only pictures with a specific media type. Alternatively, you can use the filename: filter with a more permissive wildcard that excludes the file extension.

    +
    +

    Metadata

    +
    +Windows shows different metadata values. Could this be a bug in PhotoPrism? +

    We recommend that you use ExifTool to see all metadata fields and values, as Windows has limited functionality.

    +

    It might then become clear why there are differences. For example, it could be that Windows does not support some fields and therefore ignores them, or that the data shown is actually from the file system and not from the files. Should you still believe to have found a bug, please provide us with sample files so that we can reproduce the issue.

    +
    +
    +Why is the date of pictures without metadata displayed as Unknown in the search results? +

    If there is no date information available in the metadata or the original file names, the file system modification time is used to sort pictures in search results and to create canonical file names for them during import. However, this is usually not the actual date a photo was taken (or a graphic was created by the original author), but only the time you downloaded or copied it. As a result, the date of pictures without a reliable creation date will be displayed as "Unknown" until you manually set a date in the edit dialog.

    +
    +
    +Why do some pictures have 08/12/2002 as date if they were not taken on that day? +

    This is usually caused by a bug in Android that caused photos to be created with an incorrect CreateDate. While the date can easily be changed in the edit dialog, this only updates the index without modifying your originals. +To fix the date directly in your image or video files, please use other applications like Photoshop, or ExifTool, and re-index your library.

    +
    +
    +Why do some pictures have an odd date like 01/01/1980? +

    This may happen in case there was an issue with your camera's settings when the photo was taken. +While the date can easily be changed in the edit dialog, this only updates the index +without modifying your originals.

    +

    To fix the date directly in your image or video files, please use other applications +like Photoshop, or ExifTool, and re-index your library.

    +
    +
    +What's the difference between keywords and labels? +

    Keywords contain a list of search terms extracted from metadata, file names, and other sources +like geodata. Pictures with matching keywords automatically show up in related Labels.

    +

    Although related, keywords and labels serve different purposes:

    +
      +
    • +

      Labels may have parent categories and are primarily used for classification, like "animal", "cat", or "boat". Duplicates and ambiguities should be avoided.

      +
    • +
    • +

      Keywords are primarily used for searching. They may include similar terms and translations, like "kitten", "kitty", and "cat".

      +
    • +
    +
    +
    +How can I check the metadata of images and videos? +

    We recommend using Exiftool if some of your pictures are displayed incorrectly (stretched, distorted), information seems to be missing (e.g. title or description), or the wrong time and location are shown.

    +

    Learn more ›

    +
    +

    Thumbnails

    +
    +Isn't it insecure that thumbnail image URLs work even if you are not logged in? +

    Like most commercial image hosting services, we've chosen to use a cookie-free thumbnail API to minimize request latency and avoid unnecessary network traffic. If you were to copy private session cookies and use them in a different browser window, you would have a similar problem, except that they also work for other API endpoints, not just a single image.

    +

    Even if URLs were to become invalid every minute: Digital copies are as good as originals. Once shared and downloaded, such images should be considered "leaked" because they are cached and can be re-shared by the recipient at any time, with no sure way to get all copies back. Any form of protection we could provide would essentially be "snake oil", could be circumvented, and would have a negative impact on the user experience, such as disabling the browser cache or context menu.

    +

    For the highest level of protection, it is recommended to shield your private server from the public Internet. Always use HTTPS, a VPN and/or ideally TLS client certificates and make sure that only people you trust have access to your instance.

    +

    Visit docs.photoprism.app/developer-guide/media/thumbnails/ to learn more.

    +
    +

    WebDAV

    +
    +Why are files uploaded via WebDAV not indexed/imported immediately? +

    PHOTOPRISM_AUTO_INDEX and PHOTOPRISM_AUTO_IMPORT let you specify how long PhotoPrism should wait before indexing or importing newly uploaded files. The default setting is 300 seconds, or 5 minutes. This is a safety mechanism for users with slow uploads to avoid incomplete file sets, for example when uploading pictures with sidecar files. You can therefore reduce the delay if you have a fast connection and usually do not upload stacks of related files such as RAW images with sidecar JPEG and XMP files.

    +

    In some cases, it is also possible that the index is already being updated, so you will have to wait until the process is complete before indexing new files.

    +
    +
    +Why do I get an error when trying to add a remote server for syncing? +

    When adding a new remote server, PhotoPrism tests a number of +common endpoints. +Only when that fails, you'll see an error. There may be different reasons for this:

    +
      +
    • you are using HTTPS with an invalid certificate (not signed, outdated, domain doesn't match,...)
    • +
    • your server has permission issues, or an otherwise bad configuration. For example, Nextcloud blocks requests +if the host doesn't match trusted_domains in its config.php
    • +
    • the IP is not reachable from your PhotoPrism instance due to network settings, or a firewall
    • +
    • the internal hostname can not be resolved to an IP address
    • +
    • it's the wrong host or port
    • +
    • username or password are wrong
    • +
    +

    Curl is an excellent tool for +testing HTTP connections if you don't mind using a terminal:

    +
    curl -X PROPFIND -H "Depth: 1" -u user:pass https://example.org/webdav/
    +
    +

    To avoid overlooking issues, it's best to run it from the same Docker container, virtual machine, +or server environment where PhotoPrism is installed.

    +
    +
    +My file sync app fails with "unable to parse TLS packet headers" when trying to connect via WebDAV? +

    Because of security considerations, some backup tools and file sync apps like +FolderSync removed support for non-SSL HTTP communication.

    +

    If you install PhotoPrism on a public server outside your home network, always run it behind a secure +HTTPS reverse proxy. Your files and passwords will otherwise be transmitted in clear text and can be intercepted +by anyone, including your provider, hackers, and governments. Backup tools and file sync apps may refuse to +connect as well.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/first-steps/index.html b/user-guide/first-steps/index.html new file mode 100644 index 0000000000..106fb4e2c2 --- /dev/null +++ b/user-guide/first-steps/index.html @@ -0,0 +1,7621 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + First Steps 👣 - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    First Steps 👣

    +

    Once the initial setup is complete, there are only two more steps before you can start browsing your pictures:

    +
      +
    1. Configure your library and advanced settings according to your individual preferences.
    2. +
    3. Choose whether you want to index your originals directly, leaving all file and folder names unchanged, or use the optional import feature, which automatically removes duplicates, gives files a unique name, and sorts them by year and month.
    4. +
    +

    If you want to use folders that already exist on your computer, make sure you configured them as originals respectively import folders during setup.

    +

    To add new pictures, you can either copy them to the originals or import folder, for example using WebDAV, or upload them using a browser, which will automatically import them once uploaded.

    +

    Then start indexing or importing, depending on which strategy you have chosen.

    +
    +

    Ensure there is enough disk space available for creating thumbnails and verify filesystem permissions before starting to index: Files in the originals folder must be readable, while the storage folder including all subdirectories must be readable and writeable.

    +
    +

    While indexing is in progress...

    +

    Your photos and videos will +successively become visible in search results and other parts of the user interface. +Open the Logs tab in Library to watch the indexer working. +The counts in the navigation are constantly updated, so you can follow the progress.

    +

    In case some of your pictures are still missing after indexing has been completed, +they might be in Review due to low quality or incomplete metadata. +You can turn this and other features off in Settings, +depending on your specific use case.

    +

    Of course, you can continue using your favorite tools for processing RAW files, editing metadata, +or importing new shots. Go to Library and click Start to update the index after files have been +changed, added, or removed. This can also be automated by configuring a schedule for regular indexing using the PHOTOPRISM_INDEX_SCHEDULE config option

    +
    +

    While indexing, JPEG sidecar files may be created for originals in other formats such as RAW and HEIF. This is required for image classification, facial recognition, and for displaying them in a Web browser. Sidecar and thumbnail files will be added to the storage folder, so that your originals folder won't be modified.

    +
    +

    Setting up Your Devices

    +

    Finally, once indexing is complete and you're happy with the results, you can configure automatic syncing from your phone and install the Progressive Web App (PWA) on your desktop and mobile home screens as needed.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/img/android-1.jpg b/user-guide/img/android-1.jpg new file mode 100644 index 0000000000..ceec38db07 Binary files /dev/null and b/user-guide/img/android-1.jpg differ diff --git a/user-guide/img/android-2.jpg b/user-guide/img/android-2.jpg new file mode 100644 index 0000000000..a32e864095 Binary files /dev/null and b/user-guide/img/android-2.jpg differ diff --git a/user-guide/img/android-3.jpg b/user-guide/img/android-3.jpg new file mode 100644 index 0000000000..4ff315d097 Binary files /dev/null and b/user-guide/img/android-3.jpg differ diff --git a/user-guide/img/android-install-app.jpg b/user-guide/img/android-install-app.jpg new file mode 100644 index 0000000000..7e4df37c9e Binary files /dev/null and b/user-guide/img/android-install-app.jpg differ diff --git a/user-guide/img/ios-1.jpg b/user-guide/img/ios-1.jpg new file mode 100644 index 0000000000..f5db2a0247 Binary files /dev/null and b/user-guide/img/ios-1.jpg differ diff --git a/user-guide/img/ios-2.jpg b/user-guide/img/ios-2.jpg new file mode 100644 index 0000000000..541ac715e0 Binary files /dev/null and b/user-guide/img/ios-2.jpg differ diff --git a/user-guide/img/ios-3.jpg b/user-guide/img/ios-3.jpg new file mode 100644 index 0000000000..99f9a1c81d Binary files /dev/null and b/user-guide/img/ios-3.jpg differ diff --git a/user-guide/img/ios-4.jpg b/user-guide/img/ios-4.jpg new file mode 100644 index 0000000000..05992c6e3f Binary files /dev/null and b/user-guide/img/ios-4.jpg differ diff --git a/user-guide/img/nav1edited-dark.jpg b/user-guide/img/nav1edited-dark.jpg new file mode 100644 index 0000000000..d1149f0e3a Binary files /dev/null and b/user-guide/img/nav1edited-dark.jpg differ diff --git a/user-guide/img/nav1edited-light.jpg b/user-guide/img/nav1edited-light.jpg new file mode 100644 index 0000000000..8d64c394c9 Binary files /dev/null and b/user-guide/img/nav1edited-light.jpg differ diff --git a/user-guide/img/nav2edited-dark.jpg b/user-guide/img/nav2edited-dark.jpg new file mode 100644 index 0000000000..6e9b9d05e9 Binary files /dev/null and b/user-guide/img/nav2edited-dark.jpg differ diff --git a/user-guide/img/nav2edited-light.jpg b/user-guide/img/nav2edited-light.jpg new file mode 100644 index 0000000000..385b4ef959 Binary files /dev/null and b/user-guide/img/nav2edited-light.jpg differ diff --git a/user-guide/img/nav3edited-dark.jpg b/user-guide/img/nav3edited-dark.jpg new file mode 100644 index 0000000000..576b161856 Binary files /dev/null and b/user-guide/img/nav3edited-dark.jpg differ diff --git a/user-guide/img/nav3edited-light.jpg b/user-guide/img/nav3edited-light.jpg new file mode 100644 index 0000000000..ef467322e1 Binary files /dev/null and b/user-guide/img/nav3edited-light.jpg differ diff --git a/user-guide/index.html b/user-guide/index.html new file mode 100644 index 0000000000..b0bfc91f9c --- /dev/null +++ b/user-guide/index.html @@ -0,0 +1,7615 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Installation - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    User Guide

    +

    Step-by-step installation instructions for our self-hosted community edition can be found +in Getting Started. All you need is a Web browser and +Docker to run the server. +Progressive Web App +It is available for Mac, Linux, and Windows. PhotoPrism also runs on PikaPods, DigitalOcean, +Raspberry Pi, Portainer, FreeBSD, and many +NAS devices.

    +

    Once the initial setup is complete, our First Steps 👣 tutorial guides you through the user interface and settings to ensure your library is indexed according to your individual preferences.

    +

    PhotoPrism® Plus

    +

    Our members can activate additional features by logging in with the admin user created during setup and then following the steps described in our activation guide. Thank you for your support, which has been and continues to be essential to the success of the project!

    +

    Compare Memberships › View Membership FAQ ›

    +
    +

    We recommend that new users install our free Community Edition before signing up for a membership.

    +
    +

    Getting Support

    +

    If you need help installing our software at home, you are welcome to post your question in GitHub Discussions or ask in our Community Chat. +Common problems can be quickly diagnosed and solved using our Troubleshooting Checklists. Silver, Gold, and Platinum members are also welcome to email us for technical support and advice.

    +

    View Support Options ›

    +
    +

    We kindly ask you not to report bugs via GitHub Issues unless you are certain to have found a fully reproducible and previously unreported issue that must be fixed directly in the app. +Contact us or a community member if you need help, it could be a configuration problem, or a misunderstanding in how the software works.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/library/duplicates/index.html b/user-guide/library/duplicates/index.html new file mode 100644 index 0000000000..32b4522366 --- /dev/null +++ b/user-guide/library/duplicates/index.html @@ -0,0 +1,7612 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Duplicate Detection - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Duplicate Detection

    +

    Duplicate files are automatically detected and skipped while indexing your library, so that they only appear once in search results and albums. Their SHA1 checksums and sizes are used for comparison.

    +
    +

    Browsing and deleting duplicates through the web user interface is planned for a future release.

    +
    +

    File Import

    +

    When importing, files are first copied or moved from a temporary folder to the originals folder. In the process, duplicates are automatically skipped. "Move" also deletes them in the source directory as if they were successfully moved.

    + +

    In addition to exact duplicates, there may be similar pictures and related sidecar files (with the same filename) in your library, for example:

    +
      +
    • raw + jpg + xmp
    • +
    • jpg + json
    • +
    • original + edited version
    • +
    • original + compressed version
    • +
    • live and timelapse photos
    • +
    +

    Depending on your library settings, such files may be automatically grouped into stacks:

    +

    Learn more › Library Settings ›

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/library/files/index.html b/user-guide/library/files/index.html new file mode 100644 index 0000000000..d9f0f4dd2b --- /dev/null +++ b/user-guide/library/files/index.html @@ -0,0 +1,7614 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + File Browser - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Browsing Files and Folders

    +

    The Originals section displays all files of your originals directory.

    +

    Clicking on a folder opens it. Clicking on a file opens its edit dialog.

    +

    Screenshot

    +

    Screenshot

    +

    The context menu allows you to perform the following actions:

    +

    Download Files

    +
      +
    1. Select files
    2. +
    3. Open context menu
    4. +
    5. Click
    6. +
    +

    Create an Album from Files

    +
      +
    1. Select files
    2. +
    3. Open context menu
    4. +
    5. Click
    6. +
    7. Select existing album or enter new album name
    8. +
    9. Click add to album
    10. +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/library/img/before-import.jpg b/user-guide/library/img/before-import.jpg new file mode 100644 index 0000000000..acf7388afe Binary files /dev/null and b/user-guide/library/img/before-import.jpg differ diff --git a/user-guide/library/img/copy-import.jpg b/user-guide/library/img/copy-import.jpg new file mode 100644 index 0000000000..4541665773 Binary files /dev/null and b/user-guide/library/img/copy-import.jpg differ diff --git a/user-guide/library/img/files-2-dark.jpg b/user-guide/library/img/files-2-dark.jpg new file mode 100644 index 0000000000..f44f21ad3e Binary files /dev/null and b/user-guide/library/img/files-2-dark.jpg differ diff --git a/user-guide/library/img/files-2-light.jpg b/user-guide/library/img/files-2-light.jpg new file mode 100644 index 0000000000..a9a1570a40 Binary files /dev/null and b/user-guide/library/img/files-2-light.jpg differ diff --git a/user-guide/library/img/files-dark.jpg b/user-guide/library/img/files-dark.jpg new file mode 100644 index 0000000000..9579c8bfe7 Binary files /dev/null and b/user-guide/library/img/files-dark.jpg differ diff --git a/user-guide/library/img/files-light.jpg b/user-guide/library/img/files-light.jpg new file mode 100644 index 0000000000..40e4b02813 Binary files /dev/null and b/user-guide/library/img/files-light.jpg differ diff --git a/user-guide/library/img/import-dark.jpg b/user-guide/library/img/import-dark.jpg new file mode 100644 index 0000000000..359b2925dc Binary files /dev/null and b/user-guide/library/img/import-dark.jpg differ diff --git a/user-guide/library/img/import-light.jpg b/user-guide/library/img/import-light.jpg new file mode 100644 index 0000000000..208f45fe8b Binary files /dev/null and b/user-guide/library/img/import-light.jpg differ diff --git a/user-guide/library/img/index-dark-old.jpg b/user-guide/library/img/index-dark-old.jpg new file mode 100644 index 0000000000..2726061ec1 Binary files /dev/null and b/user-guide/library/img/index-dark-old.jpg differ diff --git a/user-guide/library/img/index-dark.jpg b/user-guide/library/img/index-dark.jpg new file mode 100644 index 0000000000..3b94929d1a Binary files /dev/null and b/user-guide/library/img/index-dark.jpg differ diff --git a/user-guide/library/img/index-light.jpg b/user-guide/library/img/index-light.jpg new file mode 100644 index 0000000000..7645a2f848 Binary files /dev/null and b/user-guide/library/img/index-light.jpg differ diff --git a/user-guide/library/img/move-import.jpg b/user-guide/library/img/move-import.jpg new file mode 100644 index 0000000000..31cb2f548f Binary files /dev/null and b/user-guide/library/img/move-import.jpg differ diff --git a/user-guide/library/img/originals-before-after.jpg b/user-guide/library/img/originals-before-after.jpg new file mode 100644 index 0000000000..6f6826cd9b Binary files /dev/null and b/user-guide/library/img/originals-before-after.jpg differ diff --git a/user-guide/library/img/upload-1-dark.jpg b/user-guide/library/img/upload-1-dark.jpg new file mode 100644 index 0000000000..6a5c4bc944 Binary files /dev/null and b/user-guide/library/img/upload-1-dark.jpg differ diff --git a/user-guide/library/img/upload-1-light.jpg b/user-guide/library/img/upload-1-light.jpg new file mode 100644 index 0000000000..fc2f45c37d Binary files /dev/null and b/user-guide/library/img/upload-1-light.jpg differ diff --git a/user-guide/library/img/upload-3-dark.jpg b/user-guide/library/img/upload-3-dark.jpg new file mode 100644 index 0000000000..57e95bf77c Binary files /dev/null and b/user-guide/library/img/upload-3-dark.jpg differ diff --git a/user-guide/library/img/upload-3-light.jpg b/user-guide/library/img/upload-3-light.jpg new file mode 100644 index 0000000000..0a32c9adaf Binary files /dev/null and b/user-guide/library/img/upload-3-light.jpg differ diff --git a/user-guide/library/img/upload-to-album-dark.jpg b/user-guide/library/img/upload-to-album-dark.jpg new file mode 100644 index 0000000000..1546dda6ca Binary files /dev/null and b/user-guide/library/img/upload-to-album-dark.jpg differ diff --git a/user-guide/library/img/upload-to-album-light.jpg b/user-guide/library/img/upload-to-album-light.jpg new file mode 100644 index 0000000000..60ab467865 Binary files /dev/null and b/user-guide/library/img/upload-to-album-light.jpg differ diff --git a/user-guide/library/img/webdav-1.jpg b/user-guide/library/img/webdav-1.jpg new file mode 100644 index 0000000000..9ee95e6ed6 Binary files /dev/null and b/user-guide/library/img/webdav-1.jpg differ diff --git a/user-guide/library/img/webdav-2.jpg b/user-guide/library/img/webdav-2.jpg new file mode 100644 index 0000000000..26db041ca9 Binary files /dev/null and b/user-guide/library/img/webdav-2.jpg differ diff --git a/user-guide/library/import/index.html b/user-guide/library/import/index.html new file mode 100644 index 0000000000..017b80a7b6 --- /dev/null +++ b/user-guide/library/import/index.html @@ -0,0 +1,7677 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Import to Originals - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Importing Files to Originals

    +
    +

    Most users with an existing library will want to index their originals directly without using the optional import feature, leaving the file and folder names unchanged. Importing first copies or moves from their source directory to the originals folder, which is optional.

    +
    +

    Manual Import

    +
      +
    1. +

      Add files to the import folder if not done already

      +
    2. +
    3. +

      Go to Library using the main navigation, and open the Import tab

      +
    4. +
    5. +

      Select a sub-folder or keep the default to import all files

      +
    6. +
    7. +

      Select Move Files if you want imported files to be removed from the import folder

      +
    8. +
    9. +

      Click on Import

      +
    10. +
    +

    Screenshot

    +
    +

    You may use WebDAV for adding files to the import folder. +This is especially helpful if PhotoPrism is running on a remote server.

    +
    +
    +

    Import is not possible in read-only mode because it requires write permissions to the folder of originals.

    +
    +

    When should "Move Files" be selected?

    +

    If you select this option, files that have been moved to the originals folder, or that already exist, will be automatically deleted from the import folder. +This way you save disk space if you don't want to keep them as backup or for other reasons.

    +

    Automatic Import

    +

    Automatic imports are disabled by default, as a wrong configuration or unsupported usage can cause files or sets of files to be imported incompletely, e.g. if you are using a slow or unreliable Internet connection, which is of particular concern with large video or RAW files.

    +

    If you enable automatic imports by setting the config option PHOTOPRISM_AUTO_IMPORT to a positive number indicating the safety delay in seconds, an import is automatically triggered after the safety delay when files are added to the import folder via WebDAV.

    +
    +

    Can I use PhotoPrism to sort files into a configurable folder structure?

    +

    You have complete freedom in how you organize your originals. If you don't like the unique names and +folders used by the import function, you can resort to external batch renaming tools, for example:

    + +

    Configurable import folders may be available in a later version. This is because - depending on the specific +pattern - appropriate conflict resolution is required and the patterns must be well understood and validated +to avoid typos or other misconfigurations that lead to undesired results for which we do not want to be responsible.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/library/index.html b/user-guide/library/index.html new file mode 100644 index 0000000000..bf7cac1cc4 --- /dev/null +++ b/user-guide/library/index.html @@ -0,0 +1,7743 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Overview - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Indexing Your Library

    +

    Most users with an existing library will want to index their originals directly without using the optional import feature, leaving the file and folder names unchanged.

    +

    When importing, files are first transferred from a temporary folder to the originals folder. In the process, duplicates are automatically skipped, and the imported files are given a unique file name and are sorted by year and month.

    +

    Importing is also an efficient way to add files, since PhotoPrism does not need to search your originals folder to find new files.

    +
    +

    Hidden files and folders that start with a ., @, _., or __ like __MACOSX are automatically ignored. Other names to be +ignored can be added to a .ppignore file in the originals or import folder it should affect. +You can put it either in the main folder or in a subfolder to limit the scope.

    +
    +

    Indexing Originals

    +

    Use index if you want to index your photos and videos directly in the originals folder, leaving the file and folder names unchanged.

    +

    Your folder structure in originals might look like this:

    +

    Screenshot

    +

    During indexing:

    +
      +
    • files will not be renamed or moved
    • +
    • your existing folder structure is preserved, so you can later choose to have your folders appear as albums
    • +
    • metadata from your files is read to create captions, titles, and locations for your photos
    • +
    • thumbnails and optionally JSON and/or YAML files containing metadata are created
    • +
    +

    After indexing, the originals folder has not been changed in any way:

    +

    Screenshot

    +

    Advantages

    +
      +
    • existing file and folder names remain unchanged.
    • +
    • you can search your images by their current name and location
    • +
    • indexing is usually faster because no files have to be copied or moved
    • +
    +
    +

    You can move media files between the different directories within your originals folder. The indexer detects this and updates the path automatically when running the next time.

    +
    +

    Importing Files

    +

    Importing is more efficient when adding files as you don't need to re-index all originals to find new photos and videos. +Uploads will also be treated as import, you can't directly upload to originals (yet).

    +

    Your initial folder structure in import might look like this:

    +

    Screenshot

    +

    During import:

    +
      +
    • files are copied or moved from their source directory to the originals folder
    • +
    • duplicates are automatically skipped, "Move" also deletes them in the source directory as if they were successfully moved
    • +
    • imported files are given a unique file name and are sorted by year and month
    • +
    • the original file name is indexed as a file property
    • +
    • all imported files are indexed, the rest remains in the import folder
    • +
    +

    After importing with "Copy" (default setting), your folders might look like this::

    +

    Screenshot

    +

    After importing with "Move" your folders might look like this:

    +

    Screenshot

    +

    Advantages

    +
      +
    • unsupported files stay untouched in the import directory
    • +
    • no duplicates in your originals directory
    • +
    +
    +

    The original file and folder names are used to extract keywords. For example, when you index a folder with the path "Vacation/Africa", all files from this folder will get the keywords "vacation" and "africa".

    +
    +

    Conclusion

    +

    In case your picture library is not well organized and/or you have many duplicates, you may consider importing your files as this will remove duplicates. Be aware that imported files are given a unique file name and are sorted by year and month.

    +

    Provided you have a well-organized library with meaningful file and folder names, it is best to index your originals directly and leave the file and folder names unchanged.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/library/metadata/index.html b/user-guide/library/metadata/index.html new file mode 100644 index 0000000000..19065b70dd --- /dev/null +++ b/user-guide/library/metadata/index.html @@ -0,0 +1,7689 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Metadata Support - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Metadata Support

    +

    Original media and sidecar files are scanned for Exif and XMP data, as well as proprietary metadata, including Google Photos JSON. +For this, PhotoPrism has a built-in Exif parser, a simple XMP reader and can also use ExifTool to extract metadata in various formats such as Exif, XMP and IPTC:

    +

    View Supported Tags ›

    +

    The combined information is then normalized, merged, and enriched with additional information.

    +
    +

    Feel free to submit a feature or pull request for Exif or XMP metadata that is not supported yet.

    +
    +

    External Changes

    +

    If you update one of these tags with external tools such as ExifTool or Digikam, PhotoPrism reads the changes the next time it indexes the file, provided the file's modification date has been updated.

    +

    XMP Sidecar Files

    +

    When a field is populated with data from an XMP sidecar file, that data is the only source for the field. This means that keywords from XMP override other keywords from PhotoPrism, such as those derived from colors or folder names.

    +

    Cloud Migration

    +

    PhotoPrism also reads metadata from Google Photos JSON and Apple XMP files:

    +

    Migrate from Google Photos ›

    +

    Migrate from Apple Photos ›

    +

    Enrichment

    +

    In addition to reading metadata from your original and sidecar files, PhotoPrism enriches the metadata of your photos with additional information:

    +
      +
    • dates or keywords from folder or filenames
    • +
    • keywords derived from image classification, color detection and facial recognition
    • +
    • GPS information from location estimates
    • +
    • keywords derived from location details
    • +
    +

    Export

    +

    We want you to be able to access your metadata independently of PhotoPrism and its database. That's why the indexer additionally creates human-readable YAML sidecar files that you can open with a text editor or other tools if needed.

    +
    +

    Except for the image orientation, PhotoPrism does not yet offer the ability to write changed metadata back to the original files to avoid possible data loss and conflicts with third-party apps. See GitHub Discussions.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/library/originals/index.html b/user-guide/library/originals/index.html new file mode 100644 index 0000000000..3a36706e27 --- /dev/null +++ b/user-guide/library/originals/index.html @@ -0,0 +1,7737 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Indexing Originals - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Indexing Your Originals

    +
    +

    When using PhotoPrism for the first time, please make sure that the directory containing your photo and video collection has been configured as originals folder, and that the library settings match your individual preferences.

    +
    +

    Manual Indexing

    +
      +
    1. +

      Go to Library using the main navigation

      +
    2. +
    3. +

      Select a sub-folder or keep the default to index all files

      +
    4. +
    5. +

      Select Complete Rescan to re-index all originals, including already indexed and unchanged files

      +
    6. +
    7. +

      Press Start to start indexing

      +
    8. +
    +

    Screenshot

    +
    +

    You can use WebDAV-compatible apps, including Microsoft's Windows Explorer and Apple's Finder, +to add files to your originals folders from a remote computer or mobile device.

    +
    +
    +

    NSFW

    +

    An NSFW detector can be enabled to automatically mark images with potentially objectionable content as +private. Note that this feature is only partially reliable.

    +

    Images that have already been indexed before the NSFW detector is activated will not be scanned by the detector.

    +
    +

    When should "Complete Rescan" be selected?

    +

    If you select this option, all files in the originals folder will be re-indexed, including already indexed and unchanged files.

    +

    We recommend performing a complete rescan after major updates to take advantage of new search filters and sorting options. Be sure to read the notes for each release to see what changes have been made and if they might affect your library, for example, because of the file types you have or because new search features have been added. If you encounter problems that you cannot solve otherwise (i.e. before reporting a bug), please also try a rescan and see if it solves the problem.

    +
    +

    Manually entered information such as labels, people, titles or descriptions will not be modified when indexing, even if you perform a "complete rescan".

    +
    +

    Cleanup Option

    +

    Admins can optionally enable the cleanup option to delete unused thumbnails from the cache folder and remove orphaned index entries. If you do this from time to time, it can speed up indexing and reduce storage usage.

    +

    Scheduled and Automatic Indexing

    +

    PhotoPrism 240523-923ee0cf7 and newer versions can optionally perform scheduled rescans of your library. This feature can be enabled by setting a schedule in your configuration. If you are using an external scheduler, please be careful not to start several indexing processes at the same time, as this not only causes a high server load, but may also lead to unexpected indexing results.

    +

    By default, a library rescan is also triggered automatically after a safety delay of 5 minutes when originals are added or modified via WebDAV. You can change the safety delay or disable this feature through the PHOTOPRISM_AUTO_INDEX config option.

    +
    +

    Be aware that automatic indexing may cause files or sets of files to be incompletely indexed if you are using a slow or unreliable internet connection, which is of particular concern with large video or RAW files.

    +
    +

    Ignoring Files and Folders

    +

    Hidden files and folders that start with a ., @, _., or __ like __MACOSX will be automatically ignored when indexing your library.

    +

    Other file and folder names that should be ignored can be added to a .ppignore file in the originals or import folder, e.g.:

    +
    # ignore a directory by its name
    +foo
    +# ignore all folders starting with a #
    +[#]*
    +# ignore all files
    +*.*
    +# ignore all files with gif extension
    +*.gif
    +# ignore videos which name start with MVI
    +MVI_*.MOV
    +# or
    +MVI_*.*
    +
    +

    Ignore files can be placed either in the main directory or in a subfolder to limit their scope, as only matching files in the same directory and any subdirectories will be ignored. To match specific file extensions or other naming patterns, * can be used as a wildcard.

    +

    Note that files and folders that have already been indexed cannot be retroactively removed from the index with a .ppignore file, i.e. they remain indexed and visible in the user interface, even if you later add their name or a matching name pattern.

    +
    +

    If you are a new user and files or folders have already been indexed, it is generally easiest to reset the database and start with a new index by running photoprism reset in a terminal.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/library/upload/index.html b/user-guide/library/upload/index.html new file mode 100644 index 0000000000..421678bc53 --- /dev/null +++ b/user-guide/library/upload/index.html @@ -0,0 +1,7556 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Web Upload - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    File Upload Using the Web UI

    +
    +
    +
    +
      +
    1. +

      Click in the upper right corner

      +

      Screenshot

      +
    2. +
    3. +

      In case you want to upload the files directly to an album select one

      +
    4. +
    5. +

      Click Upload

      +

      Screenshot

      +
    6. +
    7. +

      Select files

      +
    8. +
    9. +

      Click Upload to start uploading and importing the selected files

      +
    10. +
    +
    +
    +
      +
    1. +

      Go to Library using the main navigation, and open the Import tab

      +
    2. +
    3. +

      Click Upload

      +

      Screenshot

      +
    4. +
    5. +

      In case you want to upload the files directly to an album select one

      +
    6. +
    7. +

      Click Upload

      +

      Screenshot

      +
    8. +
    9. +

      Select files

      +
    10. +
    11. +

      Click Upload to start uploading and importing the selected files

      +
    12. +
    +
    +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/library/webdav/index.html b/user-guide/library/webdav/index.html new file mode 100644 index 0000000000..b0a5eb6a24 --- /dev/null +++ b/user-guide/library/webdav/index.html @@ -0,0 +1,7521 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WebDAV Sync - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    WebDAV File Upload

    +

    WebDAV-compatible apps and clients such as PhotoSync, Microsoft's Windows Explorer, +and Apple's Finder can connect directly to PhotoPrism:

    +

    Connect via WebDAV ›

    +

    After files have been transferred, you can index or import them as usual. +By default, indexing and importing start automatically after a safety delay when files have been uploaded using WebDAV.

    +
    +

    You can disable WebDAV in the advanced settings. Since it requires write permissions and authentication, the built-in WebDAV server is automatically disabled when running in read-only or public mode.

    +
    +
    +

    You can also use WebDAV to download files from your library: Simply connect to +http://server-ip:2342/originals/ (local server without HTTPS) or +https://yourdomain/originals/ (public server with HTTPS enabled), and then copy the files to +a folder on your local device.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/navigate/index.html b/user-guide/navigate/index.html new file mode 100644 index 0000000000..3409538a64 --- /dev/null +++ b/user-guide/navigate/index.html @@ -0,0 +1,7828 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + User Interface - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Navigating the User Interface

    +

    The user interface for browsing and searching your pictures is based on the following components. Screenshots in this documentation generally show pages in a medium-resolution desktop browser. All pages are fully responsive, so they may look different on mobile devices.

    +

    Screenshot

    +

    Screenshot

    +

    1. Main Navigation

    +

    Located on the left, minimized on mobile devices. +Click on the links to switch between different pages like Photos, Albums, Places, or Settings.

    +

    2. Toolbar

    +

    Located on the top. Find photos or videos by entering search terms like cats and filters like label:cat. A list with possible search filters can be found here.

    +

    3. Reload Button

    +

    reloads search results without reloading the full page.

    +

    4. View Button

    +

    Click to switch to a different search result view (cards: , mosaic: , or list: ).

    +

    5. Upload Button

    +

    opens the upload dialog. Available on most pages, unless read-only mode is enabled or upload is disabled in Settings.

    +

    6. Expanded Toolbar

    +

    The expanded toolbar contains additional options and search filters for country, year, month, camera, color, and category.

    +

    Context Menu

    +

    When photos or videos are selected, the context menu appears in the lower right corner. +The number displayed is the count of currently selected items. +It also contains the following buttons:

    +
      +
    • Archive photos
    • +
    • Add photos to album
    • +
    • Download photos
    • +
    • Mark photos as private
    • +
    • Open edit dialog
    • +
    • Share photos
    • +
    +

    To unselect all items, click the cross at the top:

    +

    Screenshot

    +

    Selection Mode and Multi-Select

    +

    Desktop Browser

    +

    Select the first picture by clicking in the lower right corner.

    +

    The user interface is now in selection mode:

    +
      +
    • to additionally select individual pictures, click them anywhere except on the play/view icons in the corner
    • +
    • to select multiple pictures at once, use a shift+click to select all pictures between the last selected picture and the one you shift+click
    • +
    +

    Mobile Devices

    +

    Select the first picture with a long touch.

    +

    The user interface is now in selection mode:

    +
      +
    • to additionally select individual pictures, touch them anywhere except on the play/view icons in the corner
    • +
    • to select multiple pictures at once, use a long touch to select all pictures between the last selected picture and the one you long touch
    • +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/albums/index.html b/user-guide/organize/albums/index.html new file mode 100644 index 0000000000..3d2831a52c --- /dev/null +++ b/user-guide/organize/albums/index.html @@ -0,0 +1,7701 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Albums - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Albums

    +

    Create New Album

    +
      +
    1. Go to Albums
    2. +
    3. +

      In the upper right corner click

      +

      Screenshot

      +
    4. +
    5. +

      A new album with name "Month Year" is created

      +

      Screenshot

      +
    6. +
    +

    Edit Album Details

    +

    Go to Albums and open the album edit dialog

    +
    +
    +
    +

    Click on the album title + Screenshot

    +
    +
    +

    Select album, open context menu and click

    +

    Screenshot

    +
    +
    +

    Open album and click in the upper right corner

    +

    Screenshot

    +
    +
    +
    +

    Edit album details and click save

    +

    Screenshot

    +

    Add Photos to Album

    +
      +
    1. Select photos and videos
    2. +
    3. Click context menu
    4. +
    5. +

      Click

      +

      Screenshot

      +
    6. +
    7. +

      Select album

      +

      Screenshot

      +
    8. +
    9. +

      Click add to album

      +

      Screenshot

      +
    10. +
    +
    +

    You can select many photos at once using shift.

    +
    +

    Remove Photos from Album

    +
      +
    1. Go to your album
    2. +
    3. Select photos/videos you want to remove
    4. +
    5. Click context menu
    6. +
    7. +

      Click

      +

      Screenshot

      +
    8. +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/archive/index.html b/user-guide/organize/archive/index.html new file mode 100644 index 0000000000..3729597035 --- /dev/null +++ b/user-guide/organize/archive/index.html @@ -0,0 +1,7617 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Archive - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Archive

    +

    You can move photos and videos you do not want to keep in your collection to Archive. +Content that is archived is not deleted but it will not appear in any section apart from Archive.

    +

    Archive Photos

    +
      +
    1. Select photos/videos
    2. +
    3. Click context menu
    4. +
    5. +

      Click

      +

      Screenshot

      + +
    6. +
    +

    Restore Photos from Archive

    +
      +
    1. Go to Archive
    2. +
    3. Select photos/videos
    4. +
    5. Click context menu
    6. +
    7. +

      Click

      +

      Screenshot

      +
    8. +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/browse/index.html b/user-guide/organize/browse/index.html new file mode 100644 index 0000000000..904579a9e3 --- /dev/null +++ b/user-guide/organize/browse/index.html @@ -0,0 +1,15 @@ + + + + + + Redirecting... + + + + + + +Redirecting... + + diff --git a/user-guide/organize/calendar/index.html b/user-guide/organize/calendar/index.html new file mode 100644 index 0000000000..2409e1a2d4 --- /dev/null +++ b/user-guide/organize/calendar/index.html @@ -0,0 +1,7621 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Calendar - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Calendar

    +

    Our Calendar enables you to browse through your photos by year and month.

    +

    Screenshot

    +

    The context menu allows you to perform the following actions:

    + + +

    Download Months

    +
      +
    1. Select month
    2. +
    3. Open context menu
    4. +
    5. Click
    6. +
    +

    Create Albums from Months

    +
      +
    1. Select month
    2. +
    3. Open context menu
    4. +
    5. Click
    6. +
    7. Select existing album or enter new album name
    8. +
    9. Click add to album
    10. +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/delete/index.html b/user-guide/organize/delete/index.html new file mode 100644 index 0000000000..392764b8bd --- /dev/null +++ b/user-guide/organize/delete/index.html @@ -0,0 +1,7614 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Delete - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Removing Files Permanently

    +

    You can permanently delete photo and video files you do not want to keep from your filesystem. +Photos and videos must be archived before they can be deleted permanently.

    +

    Before you start, make sure the Delete feature is enabled in Settings.

    +

    Delete Selected Files

    +
      +
    1. Select the photos and videos your want to delete
    2. +
    3. Go to Archive
    4. +
    5. Click context menu
    6. +
    7. Click
    8. +
    9. Confirm
    10. +
    +

    Screenshot

    +

    Delete All Archived Photos

    +
      +
    1. Go to Archive
    2. +
    3. Click
    4. +
    5. Click Delete All
    6. +
    +

    Screenshot

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/download/index.html b/user-guide/organize/download/index.html new file mode 100644 index 0000000000..84f08b58ab --- /dev/null +++ b/user-guide/organize/download/index.html @@ -0,0 +1,7547 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Download - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Downloading Files

    +
    +
    +
    +
      +
    1. Select photos
    2. +
    3. Click on the context menu
    4. +
    5. +

      Click . Photos will be downloaded in original size

      +

      Screenshot

      +
    6. +
    +
    +

    You can select many photos at once using shift.

    +
    +
    +

    You can configure which files should be downloaded for each photo.

    +
    +
    +
    +
      +
    1. Click on the photo
    2. +
    3. +

      In fullscreen mode click

      +

      Screenshot

      +
    4. +
    +
    +

    You can configure which files should be downloaded for each photo.

    +
    +
    +
    +
      +
    1. Open the photo's edit dialog
    2. +
    3. Open the Files Tab
    4. +
    5. Click Download
    6. +
    +

    Screenshot

    +
    +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/edit/index.html b/user-guide/organize/edit/index.html new file mode 100644 index 0000000000..50286bf55f --- /dev/null +++ b/user-guide/organize/edit/index.html @@ -0,0 +1,7687 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Edit Metadata - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Viewing and Editing Picture Details

    +

    When you click on a title in the cards view or in the full screen viewer, you can see all the information related to a picture and perform changes to it if you have permission to do so.

    +
    +
    +
    +
      +
    1. Click on the title, date/time, or camera details of a picture
    2. +
    +

    Screenshot

    +
    +
    +
      +
    1. Click on in the upper right corner
    2. +
    +

    Screenshot

    +
    +
    +
      +
    1. Select one or multiple pictures
    2. +
    3. Click on context menu
    4. +
    5. Click
    6. +
    +

    Screenshot

    +
    +
    +
    +

    Details

    +

    In the Details tab, you can view and edit general metadata such as title, date, location, camera, lens, description, and copyright:

    +

    Screenshot

    +

    Much of this information is automatically recognized and updated while indexing. If you edit these fields, the changed values will be preserved and are not overwritten even when you reindex your library.

    +

    To quickly set new coordinates, you can paste them into Latitude or Longitude if they have the format 48.265684, 7.721380.

    +

    Clicking the Apply button saves the changes you have made, but does not close the dialog, while the Done button saves your changes and closes the dialog.

    +
    +

    When performing a search, text in the Title, Description, and Keywords fields can be found, while Notes are private and will be ignored.

    +
    +

    Geolocation Plugin

    +

    Our community has contributed a browser plugin that allows you to easily change the latitude and longitude of a picture by selecting its location on a map:

    +

    Screenshot

    +

    This browser plugin can be installed through the Chrome Web Store.

    +

    Labels

    +

    In the Labels tab, you can view, add and edit labels and see whether they have been recognized automatically or added manually.

    +

    People

    +

    Open the People tab to see whose face has been recognized in the picture and assign names to faces that have not yet been recognized.

    +

    Files

    +

    The Files tab shows you all the files that belong to a picture. If it is a RAW image, you might for example also see a JPEG version of it and an XMP sidecar file:

    +

    Screenshot

    +

    Click on to see additional details such as file size, type, and codec:

    +

    Screenshot

    +

    If there is more than one JPEG or PNG file, a button in the file details will allow you to change the primary image displayed as a preview in albums and search results. You can also delete non-primary files or unstack files by clicking on the action buttons.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/folders/index.html b/user-guide/organize/folders/index.html new file mode 100644 index 0000000000..4dd1fc51c5 --- /dev/null +++ b/user-guide/organize/folders/index.html @@ -0,0 +1,7613 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Folders - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Folders

    +

    We automatically display all folders of your originals directory in the Folders section. +In case you add new files to your originals directory your folders will be updated.

    +

    Screenshot

    +

    The context menu allows you to perform the following actions:

    +

    Download Folders

    +
      +
    1. Select folder
    2. +
    3. Open context menu
    4. +
    5. Click
    6. +
    +

    Create Albums from Folders

    +
      +
    1. Select folder
    2. +
    3. Open context menu
    4. +
    5. Click
    6. +
    7. Select existing album or enter new album name
    8. +
    9. Click add to album
    10. +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/img/add-label-light.jpg b/user-guide/organize/img/add-label-light.jpg new file mode 100644 index 0000000000..b5498d50b8 Binary files /dev/null and b/user-guide/organize/img/add-label-light.jpg differ diff --git a/user-guide/organize/img/add-name-edit-new.jpg b/user-guide/organize/img/add-name-edit-new.jpg new file mode 100644 index 0000000000..e1811a15c1 Binary files /dev/null and b/user-guide/organize/img/add-name-edit-new.jpg differ diff --git a/user-guide/organize/img/add-name-new-new.jpg b/user-guide/organize/img/add-name-new-new.jpg new file mode 100644 index 0000000000..ac353beac2 Binary files /dev/null and b/user-guide/organize/img/add-name-new-new.jpg differ diff --git a/user-guide/organize/img/add-photo-album-1-dark.jpg b/user-guide/organize/img/add-photo-album-1-dark.jpg new file mode 100644 index 0000000000..618789f845 Binary files /dev/null and b/user-guide/organize/img/add-photo-album-1-dark.jpg differ diff --git a/user-guide/organize/img/add-photo-album-1-light.jpg b/user-guide/organize/img/add-photo-album-1-light.jpg new file mode 100644 index 0000000000..4cd6b00213 Binary files /dev/null and b/user-guide/organize/img/add-photo-album-1-light.jpg differ diff --git a/user-guide/organize/img/add-photo-album-2-dark.jpg b/user-guide/organize/img/add-photo-album-2-dark.jpg new file mode 100644 index 0000000000..215263420f Binary files /dev/null and b/user-guide/organize/img/add-photo-album-2-dark.jpg differ diff --git a/user-guide/organize/img/add-photo-album-2-light.jpg b/user-guide/organize/img/add-photo-album-2-light.jpg new file mode 100644 index 0000000000..f945d14147 Binary files /dev/null and b/user-guide/organize/img/add-photo-album-2-light.jpg differ diff --git a/user-guide/organize/img/add-photo-album-3-dark.jpg b/user-guide/organize/img/add-photo-album-3-dark.jpg new file mode 100644 index 0000000000..95e796ad76 Binary files /dev/null and b/user-guide/organize/img/add-photo-album-3-dark.jpg differ diff --git a/user-guide/organize/img/add-photo-album-3-light.jpg b/user-guide/organize/img/add-photo-album-3-light.jpg new file mode 100644 index 0000000000..f15cc0fa1e Binary files /dev/null and b/user-guide/organize/img/add-photo-album-3-light.jpg differ diff --git a/user-guide/organize/img/album-edit-2-dark.jpg b/user-guide/organize/img/album-edit-2-dark.jpg new file mode 100644 index 0000000000..4bd413f3e2 Binary files /dev/null and b/user-guide/organize/img/album-edit-2-dark.jpg differ diff --git a/user-guide/organize/img/album-edit-2-light.jpg b/user-guide/organize/img/album-edit-2-light.jpg new file mode 100644 index 0000000000..4d35105a6a Binary files /dev/null and b/user-guide/organize/img/album-edit-2-light.jpg differ diff --git a/user-guide/organize/img/album-edit-dark.jpg b/user-guide/organize/img/album-edit-dark.jpg new file mode 100644 index 0000000000..b1e9922dc5 Binary files /dev/null and b/user-guide/organize/img/album-edit-dark.jpg differ diff --git a/user-guide/organize/img/album-edit-light.jpg b/user-guide/organize/img/album-edit-light.jpg new file mode 100644 index 0000000000..7fed2a9e8d Binary files /dev/null and b/user-guide/organize/img/album-edit-light.jpg differ diff --git a/user-guide/organize/img/album-edit-menu-dark.jpg b/user-guide/organize/img/album-edit-menu-dark.jpg new file mode 100644 index 0000000000..27eca566d8 Binary files /dev/null and b/user-guide/organize/img/album-edit-menu-dark.jpg differ diff --git a/user-guide/organize/img/album-edit-menu-light.jpg b/user-guide/organize/img/album-edit-menu-light.jpg new file mode 100644 index 0000000000..4a5658fe20 Binary files /dev/null and b/user-guide/organize/img/album-edit-menu-light.jpg differ diff --git a/user-guide/organize/img/album-edit-title-dark.jpg b/user-guide/organize/img/album-edit-title-dark.jpg new file mode 100644 index 0000000000..d8293c8e84 Binary files /dev/null and b/user-guide/organize/img/album-edit-title-dark.jpg differ diff --git a/user-guide/organize/img/album-edit-title-light.jpg b/user-guide/organize/img/album-edit-title-light.jpg new file mode 100644 index 0000000000..5264e7b1d3 Binary files /dev/null and b/user-guide/organize/img/album-edit-title-light.jpg differ diff --git a/user-guide/organize/img/album-edit-toolbar-dark.jpg b/user-guide/organize/img/album-edit-toolbar-dark.jpg new file mode 100644 index 0000000000..56d0826d29 Binary files /dev/null and b/user-guide/organize/img/album-edit-toolbar-dark.jpg differ diff --git a/user-guide/organize/img/album-edit-toolbar-light.jpg b/user-guide/organize/img/album-edit-toolbar-light.jpg new file mode 100644 index 0000000000..a50b826518 Binary files /dev/null and b/user-guide/organize/img/album-edit-toolbar-light.jpg differ diff --git a/user-guide/organize/img/album-name-1-light.jpg b/user-guide/organize/img/album-name-1-light.jpg new file mode 100644 index 0000000000..fab08094f5 Binary files /dev/null and b/user-guide/organize/img/album-name-1-light.jpg differ diff --git a/user-guide/organize/img/album-namee-1-dark.jpg b/user-guide/organize/img/album-namee-1-dark.jpg new file mode 100644 index 0000000000..cea1b6e81d Binary files /dev/null and b/user-guide/organize/img/album-namee-1-dark.jpg differ diff --git a/user-guide/organize/img/albums-section-dark.jpg b/user-guide/organize/img/albums-section-dark.jpg new file mode 100644 index 0000000000..52e6402fa1 Binary files /dev/null and b/user-guide/organize/img/albums-section-dark.jpg differ diff --git a/user-guide/organize/img/albums-section-light.jpg b/user-guide/organize/img/albums-section-light.jpg new file mode 100644 index 0000000000..c12d5c0aca Binary files /dev/null and b/user-guide/organize/img/albums-section-light.jpg differ diff --git a/user-guide/organize/img/albums.jpg b/user-guide/organize/img/albums.jpg new file mode 100644 index 0000000000..8a48c51b5e Binary files /dev/null and b/user-guide/organize/img/albums.jpg differ diff --git a/user-guide/organize/img/archive-dark.jpg b/user-guide/organize/img/archive-dark.jpg new file mode 100644 index 0000000000..313db9cafa Binary files /dev/null and b/user-guide/organize/img/archive-dark.jpg differ diff --git a/user-guide/organize/img/archive-light.jpg b/user-guide/organize/img/archive-light.jpg new file mode 100644 index 0000000000..7d5db7db79 Binary files /dev/null and b/user-guide/organize/img/archive-light.jpg differ diff --git a/user-guide/organize/img/calendar-light.jpg b/user-guide/organize/img/calendar-light.jpg new file mode 100644 index 0000000000..522ed02d29 Binary files /dev/null and b/user-guide/organize/img/calendar-light.jpg differ diff --git a/user-guide/organize/img/calendar-section-dark.jpg b/user-guide/organize/img/calendar-section-dark.jpg new file mode 100644 index 0000000000..043c064e07 Binary files /dev/null and b/user-guide/organize/img/calendar-section-dark.jpg differ diff --git a/user-guide/organize/img/calendar-section-light.jpg b/user-guide/organize/img/calendar-section-light.jpg new file mode 100644 index 0000000000..e54726cd75 Binary files /dev/null and b/user-guide/organize/img/calendar-section-light.jpg differ diff --git a/user-guide/organize/img/calendar.jpg b/user-guide/organize/img/calendar.jpg new file mode 100644 index 0000000000..aac94a940a Binary files /dev/null and b/user-guide/organize/img/calendar.jpg differ diff --git a/user-guide/organize/img/card-dark.jpg b/user-guide/organize/img/card-dark.jpg new file mode 100644 index 0000000000..f49061d6ee Binary files /dev/null and b/user-guide/organize/img/card-dark.jpg differ diff --git a/user-guide/organize/img/card-light.jpg b/user-guide/organize/img/card-light.jpg new file mode 100644 index 0000000000..cfe9af5d96 Binary files /dev/null and b/user-guide/organize/img/card-light.jpg differ diff --git a/user-guide/organize/img/create-album-dark.jpg b/user-guide/organize/img/create-album-dark.jpg new file mode 100644 index 0000000000..08ca0345ec Binary files /dev/null and b/user-guide/organize/img/create-album-dark.jpg differ diff --git a/user-guide/organize/img/create-album-light.jpg b/user-guide/organize/img/create-album-light.jpg new file mode 100644 index 0000000000..394e241e4d Binary files /dev/null and b/user-guide/organize/img/create-album-light.jpg differ diff --git a/user-guide/organize/img/delete-all-dark.jpg b/user-guide/organize/img/delete-all-dark.jpg new file mode 100644 index 0000000000..48463017f0 Binary files /dev/null and b/user-guide/organize/img/delete-all-dark.jpg differ diff --git a/user-guide/organize/img/delete-dark.jpg b/user-guide/organize/img/delete-dark.jpg new file mode 100644 index 0000000000..ae4246ffa7 Binary files /dev/null and b/user-guide/organize/img/delete-dark.jpg differ diff --git a/user-guide/organize/img/delete-label-1-light.jpg b/user-guide/organize/img/delete-label-1-light.jpg new file mode 100644 index 0000000000..9a548cb775 Binary files /dev/null and b/user-guide/organize/img/delete-label-1-light.jpg differ diff --git a/user-guide/organize/img/delete-label-2-light.jpg b/user-guide/organize/img/delete-label-2-light.jpg new file mode 100644 index 0000000000..9bbf507bd9 Binary files /dev/null and b/user-guide/organize/img/delete-label-2-light.jpg differ diff --git a/user-guide/organize/img/delete-light.jpg b/user-guide/organize/img/delete-light.jpg new file mode 100644 index 0000000000..cb80eedda7 Binary files /dev/null and b/user-guide/organize/img/delete-light.jpg differ diff --git a/user-guide/organize/img/download-1-dark.jpg b/user-guide/organize/img/download-1-dark.jpg new file mode 100644 index 0000000000..5dd814f753 Binary files /dev/null and b/user-guide/organize/img/download-1-dark.jpg differ diff --git a/user-guide/organize/img/download-2-dark.jpg b/user-guide/organize/img/download-2-dark.jpg new file mode 100644 index 0000000000..c458b15e0f Binary files /dev/null and b/user-guide/organize/img/download-2-dark.jpg differ diff --git a/user-guide/organize/img/download-3-light.jpg b/user-guide/organize/img/download-3-light.jpg new file mode 100644 index 0000000000..a0cf281b31 Binary files /dev/null and b/user-guide/organize/img/download-3-light.jpg differ diff --git a/user-guide/organize/img/edit-details-light.jpg b/user-guide/organize/img/edit-details-light.jpg new file mode 100644 index 0000000000..fbc0f15011 Binary files /dev/null and b/user-guide/organize/img/edit-details-light.jpg differ diff --git a/user-guide/organize/img/edit-files-1-light.jpg b/user-guide/organize/img/edit-files-1-light.jpg new file mode 100644 index 0000000000..e54abe5ede Binary files /dev/null and b/user-guide/organize/img/edit-files-1-light.jpg differ diff --git a/user-guide/organize/img/edit-files-2-light.jpg b/user-guide/organize/img/edit-files-2-light.jpg new file mode 100644 index 0000000000..28ee2c0bf1 Binary files /dev/null and b/user-guide/organize/img/edit-files-2-light.jpg differ diff --git a/user-guide/organize/img/edit-label-1-light.jpg b/user-guide/organize/img/edit-label-1-light.jpg new file mode 100644 index 0000000000..ce76515d78 Binary files /dev/null and b/user-guide/organize/img/edit-label-1-light.jpg differ diff --git a/user-guide/organize/img/edit-label-2-light.jpg b/user-guide/organize/img/edit-label-2-light.jpg new file mode 100644 index 0000000000..01063e8da0 Binary files /dev/null and b/user-guide/organize/img/edit-label-2-light.jpg differ diff --git a/user-guide/organize/img/edit-label-3-light.jpg b/user-guide/organize/img/edit-label-3-light.jpg new file mode 100644 index 0000000000..c1e0b13688 Binary files /dev/null and b/user-guide/organize/img/edit-label-3-light.jpg differ diff --git a/user-guide/organize/img/edit-label-4-light.jpg b/user-guide/organize/img/edit-label-4-light.jpg new file mode 100644 index 0000000000..9ea2d71543 Binary files /dev/null and b/user-guide/organize/img/edit-label-4-light.jpg differ diff --git a/user-guide/organize/img/edit-label-title-light.jpg b/user-guide/organize/img/edit-label-title-light.jpg new file mode 100644 index 0000000000..c6108094eb Binary files /dev/null and b/user-guide/organize/img/edit-label-title-light.jpg differ diff --git a/user-guide/organize/img/edit-open-1-light.jpg b/user-guide/organize/img/edit-open-1-light.jpg new file mode 100644 index 0000000000..499a89c6d1 Binary files /dev/null and b/user-guide/organize/img/edit-open-1-light.jpg differ diff --git a/user-guide/organize/img/edit-open-2-light.jpg b/user-guide/organize/img/edit-open-2-light.jpg new file mode 100644 index 0000000000..7908686b0b Binary files /dev/null and b/user-guide/organize/img/edit-open-2-light.jpg differ diff --git a/user-guide/organize/img/edit-open-2.jpg b/user-guide/organize/img/edit-open-2.jpg new file mode 100644 index 0000000000..ccdd0208e0 Binary files /dev/null and b/user-guide/organize/img/edit-open-2.jpg differ diff --git a/user-guide/organize/img/edit-open-3-light.jpg b/user-guide/organize/img/edit-open-3-light.jpg new file mode 100644 index 0000000000..d924f4001c Binary files /dev/null and b/user-guide/organize/img/edit-open-3-light.jpg differ diff --git a/user-guide/organize/img/face-hide.jpg b/user-guide/organize/img/face-hide.jpg new file mode 100644 index 0000000000..6165ad0d37 Binary files /dev/null and b/user-guide/organize/img/face-hide.jpg differ diff --git a/user-guide/organize/img/face-recover.jpg b/user-guide/organize/img/face-recover.jpg new file mode 100644 index 0000000000..c86c0094e6 Binary files /dev/null and b/user-guide/organize/img/face-recover.jpg differ diff --git a/user-guide/organize/img/face-show-all.jpg b/user-guide/organize/img/face-show-all.jpg new file mode 100644 index 0000000000..04e4cd9598 Binary files /dev/null and b/user-guide/organize/img/face-show-all.jpg differ diff --git a/user-guide/organize/img/favorites.jpg b/user-guide/organize/img/favorites.jpg new file mode 100644 index 0000000000..f404cc9794 Binary files /dev/null and b/user-guide/organize/img/favorites.jpg differ diff --git a/user-guide/organize/img/files-1.jpg b/user-guide/organize/img/files-1.jpg new file mode 100644 index 0000000000..7729c1e534 Binary files /dev/null and b/user-guide/organize/img/files-1.jpg differ diff --git a/user-guide/organize/img/filter-bar-new.jpg b/user-guide/organize/img/filter-bar-new.jpg new file mode 100644 index 0000000000..b68175bf62 Binary files /dev/null and b/user-guide/organize/img/filter-bar-new.jpg differ diff --git a/user-guide/organize/img/folder-section-dark.jpg b/user-guide/organize/img/folder-section-dark.jpg new file mode 100644 index 0000000000..d42670b786 Binary files /dev/null and b/user-guide/organize/img/folder-section-dark.jpg differ diff --git a/user-guide/organize/img/folder-section-light.jpg b/user-guide/organize/img/folder-section-light.jpg new file mode 100644 index 0000000000..ab90a443e6 Binary files /dev/null and b/user-guide/organize/img/folder-section-light.jpg differ diff --git a/user-guide/organize/img/folders-light.jpg b/user-guide/organize/img/folders-light.jpg new file mode 100644 index 0000000000..5572255f4d Binary files /dev/null and b/user-guide/organize/img/folders-light.jpg differ diff --git a/user-guide/organize/img/fulltext-search-1.jpg b/user-guide/organize/img/fulltext-search-1.jpg new file mode 100644 index 0000000000..568e12fd9e Binary files /dev/null and b/user-guide/organize/img/fulltext-search-1.jpg differ diff --git a/user-guide/organize/img/hide-face.jpg b/user-guide/organize/img/hide-face.jpg new file mode 100644 index 0000000000..f94eecb569 Binary files /dev/null and b/user-guide/organize/img/hide-face.jpg differ diff --git a/user-guide/organize/img/labels-1-dark.jpg b/user-guide/organize/img/labels-1-dark.jpg new file mode 100644 index 0000000000..07e94c725a Binary files /dev/null and b/user-guide/organize/img/labels-1-dark.jpg differ diff --git a/user-guide/organize/img/labels-1-light.jpg b/user-guide/organize/img/labels-1-light.jpg new file mode 100644 index 0000000000..3a94350059 Binary files /dev/null and b/user-guide/organize/img/labels-1-light.jpg differ diff --git a/user-guide/organize/img/labels-2-light.jpg b/user-guide/organize/img/labels-2-light.jpg new file mode 100644 index 0000000000..3b52d188ed Binary files /dev/null and b/user-guide/organize/img/labels-2-light.jpg differ diff --git a/user-guide/organize/img/labels-3-light.jpg b/user-guide/organize/img/labels-3-light.jpg new file mode 100644 index 0000000000..8e12615f3d Binary files /dev/null and b/user-guide/organize/img/labels-3-light.jpg differ diff --git a/user-guide/organize/img/labels-section-dark.jpg b/user-guide/organize/img/labels-section-dark.jpg new file mode 100644 index 0000000000..0708d16bda Binary files /dev/null and b/user-guide/organize/img/labels-section-dark.jpg differ diff --git a/user-guide/organize/img/labels-section-light.jpg b/user-guide/organize/img/labels-section-light.jpg new file mode 100644 index 0000000000..e24cc34128 Binary files /dev/null and b/user-guide/organize/img/labels-section-light.jpg differ diff --git a/user-guide/organize/img/labels.jpg b/user-guide/organize/img/labels.jpg new file mode 100644 index 0000000000..fd6f5f9429 Binary files /dev/null and b/user-guide/organize/img/labels.jpg differ diff --git a/user-guide/organize/img/list-dark.jpg b/user-guide/organize/img/list-dark.jpg new file mode 100644 index 0000000000..b70b32bdcf Binary files /dev/null and b/user-guide/organize/img/list-dark.jpg differ diff --git a/user-guide/organize/img/list-light.jpg b/user-guide/organize/img/list-light.jpg new file mode 100644 index 0000000000..dc3844f423 Binary files /dev/null and b/user-guide/organize/img/list-light.jpg differ diff --git a/user-guide/organize/img/live-photo-dark.jpg b/user-guide/organize/img/live-photo-dark.jpg new file mode 100644 index 0000000000..7f9a7d9902 Binary files /dev/null and b/user-guide/organize/img/live-photo-dark.jpg differ diff --git a/user-guide/organize/img/live-photo-light.jpg b/user-guide/organize/img/live-photo-light.jpg new file mode 100644 index 0000000000..073f98d251 Binary files /dev/null and b/user-guide/organize/img/live-photo-light.jpg differ diff --git a/user-guide/organize/img/moments-light.jpg b/user-guide/organize/img/moments-light.jpg new file mode 100644 index 0000000000..77249bfd41 Binary files /dev/null and b/user-guide/organize/img/moments-light.jpg differ diff --git a/user-guide/organize/img/moments-section-dark.jpg b/user-guide/organize/img/moments-section-dark.jpg new file mode 100644 index 0000000000..3a052ef4d2 Binary files /dev/null and b/user-guide/organize/img/moments-section-dark.jpg differ diff --git a/user-guide/organize/img/moments-section-light.jpg b/user-guide/organize/img/moments-section-light.jpg new file mode 100644 index 0000000000..a7076b914d Binary files /dev/null and b/user-guide/organize/img/moments-section-light.jpg differ diff --git a/user-guide/organize/img/moments.jpg b/user-guide/organize/img/moments.jpg new file mode 100644 index 0000000000..d1417206a9 Binary files /dev/null and b/user-guide/organize/img/moments.jpg differ diff --git a/user-guide/organize/img/mosaic-dark.jpg b/user-guide/organize/img/mosaic-dark.jpg new file mode 100644 index 0000000000..88167cfeed Binary files /dev/null and b/user-guide/organize/img/mosaic-dark.jpg differ diff --git a/user-guide/organize/img/mosaic-light.jpg b/user-guide/organize/img/mosaic-light.jpg new file mode 100644 index 0000000000..49da581510 Binary files /dev/null and b/user-guide/organize/img/mosaic-light.jpg differ diff --git a/user-guide/organize/img/new-new.jpg b/user-guide/organize/img/new-new.jpg new file mode 100644 index 0000000000..c42cfb00c2 Binary files /dev/null and b/user-guide/organize/img/new-new.jpg differ diff --git a/user-guide/organize/img/new.jpg b/user-guide/organize/img/new.jpg new file mode 100644 index 0000000000..ef9ae9f18b Binary files /dev/null and b/user-guide/organize/img/new.jpg differ diff --git a/user-guide/organize/img/originals.jpg b/user-guide/organize/img/originals.jpg new file mode 100644 index 0000000000..8c8d85db91 Binary files /dev/null and b/user-guide/organize/img/originals.jpg differ diff --git a/user-guide/organize/img/originals2.jpg b/user-guide/organize/img/originals2.jpg new file mode 100644 index 0000000000..344577c75f Binary files /dev/null and b/user-guide/organize/img/originals2.jpg differ diff --git a/user-guide/organize/img/panorama-1-light.jpg b/user-guide/organize/img/panorama-1-light.jpg new file mode 100644 index 0000000000..f2d0bc9d8c Binary files /dev/null and b/user-guide/organize/img/panorama-1-light.jpg differ diff --git a/user-guide/organize/img/panorama-2-light.jpg b/user-guide/organize/img/panorama-2-light.jpg new file mode 100644 index 0000000000..526565f724 Binary files /dev/null and b/user-guide/organize/img/panorama-2-light.jpg differ diff --git a/user-guide/organize/img/people-context-menu-new.jpg b/user-guide/organize/img/people-context-menu-new.jpg new file mode 100644 index 0000000000..fab96f2210 Binary files /dev/null and b/user-guide/organize/img/people-context-menu-new.jpg differ diff --git a/user-guide/organize/img/people-context-menu.jpg b/user-guide/organize/img/people-context-menu.jpg new file mode 100644 index 0000000000..ba241961f6 Binary files /dev/null and b/user-guide/organize/img/people-context-menu.jpg differ diff --git a/user-guide/organize/img/people-search.jpg b/user-guide/organize/img/people-search.jpg new file mode 100644 index 0000000000..6945429896 Binary files /dev/null and b/user-guide/organize/img/people-search.jpg differ diff --git a/user-guide/organize/img/person-hide-new.jpg b/user-guide/organize/img/person-hide-new.jpg new file mode 100644 index 0000000000..4414313f4b Binary files /dev/null and b/user-guide/organize/img/person-hide-new.jpg differ diff --git a/user-guide/organize/img/person-hide.jpg b/user-guide/organize/img/person-hide.jpg new file mode 100644 index 0000000000..c0d25a9d87 Binary files /dev/null and b/user-guide/organize/img/person-hide.jpg differ diff --git a/user-guide/organize/img/person-recover-new.jpg b/user-guide/organize/img/person-recover-new.jpg new file mode 100644 index 0000000000..e22545ecc6 Binary files /dev/null and b/user-guide/organize/img/person-recover-new.jpg differ diff --git a/user-guide/organize/img/person-recover.jpg b/user-guide/organize/img/person-recover.jpg new file mode 100644 index 0000000000..14267f7adf Binary files /dev/null and b/user-guide/organize/img/person-recover.jpg differ diff --git a/user-guide/organize/img/person-show-all-new.jpg b/user-guide/organize/img/person-show-all-new.jpg new file mode 100644 index 0000000000..ef4092e0d8 Binary files /dev/null and b/user-guide/organize/img/person-show-all-new.jpg differ diff --git a/user-guide/organize/img/person-show-all.jpg b/user-guide/organize/img/person-show-all.jpg new file mode 100644 index 0000000000..6df930b5d7 Binary files /dev/null and b/user-guide/organize/img/person-show-all.jpg differ diff --git a/user-guide/organize/img/places-1-dark-old.jpg b/user-guide/organize/img/places-1-dark-old.jpg new file mode 100644 index 0000000000..cfb7878502 Binary files /dev/null and b/user-guide/organize/img/places-1-dark-old.jpg differ diff --git a/user-guide/organize/img/places-1-dark.jpg b/user-guide/organize/img/places-1-dark.jpg new file mode 100644 index 0000000000..cd08a3ee1b Binary files /dev/null and b/user-guide/organize/img/places-1-dark.jpg differ diff --git a/user-guide/organize/img/places-2-dark.jpg b/user-guide/organize/img/places-2-dark.jpg new file mode 100644 index 0000000000..618d6671ad Binary files /dev/null and b/user-guide/organize/img/places-2-dark.jpg differ diff --git a/user-guide/organize/img/places-2-section-dark.jpg b/user-guide/organize/img/places-2-section-dark.jpg new file mode 100644 index 0000000000..a68e4ca8df Binary files /dev/null and b/user-guide/organize/img/places-2-section-dark.jpg differ diff --git a/user-guide/organize/img/places-2-section-light.jpg b/user-guide/organize/img/places-2-section-light.jpg new file mode 100644 index 0000000000..81d647f825 Binary files /dev/null and b/user-guide/organize/img/places-2-section-light.jpg differ diff --git a/user-guide/organize/img/places-3-dark.jpg b/user-guide/organize/img/places-3-dark.jpg new file mode 100644 index 0000000000..59203bb906 Binary files /dev/null and b/user-guide/organize/img/places-3-dark.jpg differ diff --git a/user-guide/organize/img/places-animation-1-dark.jpg b/user-guide/organize/img/places-animation-1-dark.jpg new file mode 100644 index 0000000000..28a3b74f3c Binary files /dev/null and b/user-guide/organize/img/places-animation-1-dark.jpg differ diff --git a/user-guide/organize/img/places-animation-2-dark.jpg b/user-guide/organize/img/places-animation-2-dark.jpg new file mode 100644 index 0000000000..e18af3fb6d Binary files /dev/null and b/user-guide/organize/img/places-animation-2-dark.jpg differ diff --git a/user-guide/organize/img/places-cluster-dark-1.jpg b/user-guide/organize/img/places-cluster-dark-1.jpg new file mode 100644 index 0000000000..39804d7371 Binary files /dev/null and b/user-guide/organize/img/places-cluster-dark-1.jpg differ diff --git a/user-guide/organize/img/places-cluster-dark-2.jpg b/user-guide/organize/img/places-cluster-dark-2.jpg new file mode 100644 index 0000000000..1c2d1d5e95 Binary files /dev/null and b/user-guide/organize/img/places-cluster-dark-2.jpg differ diff --git a/user-guide/organize/img/places-cluster-dark-3.jpg b/user-guide/organize/img/places-cluster-dark-3.jpg new file mode 100644 index 0000000000..975698a1c4 Binary files /dev/null and b/user-guide/organize/img/places-cluster-dark-3.jpg differ diff --git a/user-guide/organize/img/places-search-1.jpg b/user-guide/organize/img/places-search-1.jpg new file mode 100644 index 0000000000..7cf82425e1 Binary files /dev/null and b/user-guide/organize/img/places-search-1.jpg differ diff --git a/user-guide/organize/img/places-section-dark.jpg b/user-guide/organize/img/places-section-dark.jpg new file mode 100644 index 0000000000..16d5b4a7bd Binary files /dev/null and b/user-guide/organize/img/places-section-dark.jpg differ diff --git a/user-guide/organize/img/places-section-light.jpg b/user-guide/organize/img/places-section-light.jpg new file mode 100644 index 0000000000..3f95c12c9f Binary files /dev/null and b/user-guide/organize/img/places-section-light.jpg differ diff --git a/user-guide/organize/img/places.jpg b/user-guide/organize/img/places.jpg new file mode 100644 index 0000000000..fb11d899bc Binary files /dev/null and b/user-guide/organize/img/places.jpg differ diff --git a/user-guide/organize/img/private-context-menu-dark.jpg b/user-guide/organize/img/private-context-menu-dark.jpg new file mode 100644 index 0000000000..d80536338e Binary files /dev/null and b/user-guide/organize/img/private-context-menu-dark.jpg differ diff --git a/user-guide/organize/img/private-context-menu-light.jpg b/user-guide/organize/img/private-context-menu-light.jpg new file mode 100644 index 0000000000..4411f81ba8 Binary files /dev/null and b/user-guide/organize/img/private-context-menu-light.jpg differ diff --git a/user-guide/organize/img/private-list-dark.jpg b/user-guide/organize/img/private-list-dark.jpg new file mode 100644 index 0000000000..74041a030f Binary files /dev/null and b/user-guide/organize/img/private-list-dark.jpg differ diff --git a/user-guide/organize/img/private-list-light.jpg b/user-guide/organize/img/private-list-light.jpg new file mode 100644 index 0000000000..f4d6766fb4 Binary files /dev/null and b/user-guide/organize/img/private-list-light.jpg differ diff --git a/user-guide/organize/img/recognized-new.jpg b/user-guide/organize/img/recognized-new.jpg new file mode 100644 index 0000000000..ed514316db Binary files /dev/null and b/user-guide/organize/img/recognized-new.jpg differ diff --git a/user-guide/organize/img/recognized.jpg b/user-guide/organize/img/recognized.jpg new file mode 100644 index 0000000000..ce38d353fa Binary files /dev/null and b/user-guide/organize/img/recognized.jpg differ diff --git a/user-guide/organize/img/reject-new.jpg b/user-guide/organize/img/reject-new.jpg new file mode 100644 index 0000000000..5ef72a16ba Binary files /dev/null and b/user-guide/organize/img/reject-new.jpg differ diff --git a/user-guide/organize/img/reject.jpg b/user-guide/organize/img/reject.jpg new file mode 100644 index 0000000000..28c1ffe418 Binary files /dev/null and b/user-guide/organize/img/reject.jpg differ diff --git a/user-guide/organize/img/remove-face.jpg b/user-guide/organize/img/remove-face.jpg new file mode 100644 index 0000000000..953d9863fa Binary files /dev/null and b/user-guide/organize/img/remove-face.jpg differ diff --git a/user-guide/organize/img/remove-from-album-1-dark.jpg b/user-guide/organize/img/remove-from-album-1-dark.jpg new file mode 100644 index 0000000000..b627dd2022 Binary files /dev/null and b/user-guide/organize/img/remove-from-album-1-dark.jpg differ diff --git a/user-guide/organize/img/remove-from-album-1-light.jpg b/user-guide/organize/img/remove-from-album-1-light.jpg new file mode 100644 index 0000000000..9ce7c76c83 Binary files /dev/null and b/user-guide/organize/img/remove-from-album-1-light.jpg differ diff --git a/user-guide/organize/img/remove-label-1-light.jpg b/user-guide/organize/img/remove-label-1-light.jpg new file mode 100644 index 0000000000..028e0c6120 Binary files /dev/null and b/user-guide/organize/img/remove-label-1-light.jpg differ diff --git a/user-guide/organize/img/remove-label-2-light.jpg b/user-guide/organize/img/remove-label-2-light.jpg new file mode 100644 index 0000000000..90e838a5a3 Binary files /dev/null and b/user-guide/organize/img/remove-label-2-light.jpg differ diff --git a/user-guide/organize/img/rename-edit.jpg b/user-guide/organize/img/rename-edit.jpg new file mode 100644 index 0000000000..40b189e084 Binary files /dev/null and b/user-guide/organize/img/rename-edit.jpg differ diff --git a/user-guide/organize/img/rename-recognized-0-new.jpg b/user-guide/organize/img/rename-recognized-0-new.jpg new file mode 100644 index 0000000000..155fabdd1a Binary files /dev/null and b/user-guide/organize/img/rename-recognized-0-new.jpg differ diff --git a/user-guide/organize/img/rename-recognized-0.jpg b/user-guide/organize/img/rename-recognized-0.jpg new file mode 100644 index 0000000000..46b5be975b Binary files /dev/null and b/user-guide/organize/img/rename-recognized-0.jpg differ diff --git a/user-guide/organize/img/rename-recognized-new.jpg b/user-guide/organize/img/rename-recognized-new.jpg new file mode 100644 index 0000000000..90351b07ce Binary files /dev/null and b/user-guide/organize/img/rename-recognized-new.jpg differ diff --git a/user-guide/organize/img/rename-recognized.jpg b/user-guide/organize/img/rename-recognized.jpg new file mode 100644 index 0000000000..5283067fb8 Binary files /dev/null and b/user-guide/organize/img/rename-recognized.jpg differ diff --git a/user-guide/organize/img/restore-dark.jpg b/user-guide/organize/img/restore-dark.jpg new file mode 100644 index 0000000000..4791d799dc Binary files /dev/null and b/user-guide/organize/img/restore-dark.jpg differ diff --git a/user-guide/organize/img/restore-light.jpg b/user-guide/organize/img/restore-light.jpg new file mode 100644 index 0000000000..f11c8e0106 Binary files /dev/null and b/user-guide/organize/img/restore-light.jpg differ diff --git a/user-guide/organize/img/review-2-light.jpg b/user-guide/organize/img/review-2-light.jpg new file mode 100644 index 0000000000..5daa38ea38 Binary files /dev/null and b/user-guide/organize/img/review-2-light.jpg differ diff --git a/user-guide/organize/img/review-3-light.jpg b/user-guide/organize/img/review-3-light.jpg new file mode 100644 index 0000000000..1ede7e0e25 Binary files /dev/null and b/user-guide/organize/img/review-3-light.jpg differ diff --git a/user-guide/organize/img/review-light.jpg b/user-guide/organize/img/review-light.jpg new file mode 100644 index 0000000000..8eb8eba6fd Binary files /dev/null and b/user-guide/organize/img/review-light.jpg differ diff --git a/user-guide/organize/img/rotate-1.jpg b/user-guide/organize/img/rotate-1.jpg new file mode 100644 index 0000000000..4d0bfe43ba Binary files /dev/null and b/user-guide/organize/img/rotate-1.jpg differ diff --git a/user-guide/organize/img/rotate-2.jpg b/user-guide/organize/img/rotate-2.jpg new file mode 100644 index 0000000000..00c3b7bb96 Binary files /dev/null and b/user-guide/organize/img/rotate-2.jpg differ diff --git a/user-guide/organize/img/scans-light.jpg b/user-guide/organize/img/scans-light.jpg new file mode 100644 index 0000000000..dcc0ef4fdf Binary files /dev/null and b/user-guide/organize/img/scans-light.jpg differ diff --git a/user-guide/organize/img/search-filters.jpg b/user-guide/organize/img/search-filters.jpg new file mode 100644 index 0000000000..4b776f61cc Binary files /dev/null and b/user-guide/organize/img/search-filters.jpg differ diff --git a/user-guide/organize/img/search-section-dark.jpg b/user-guide/organize/img/search-section-dark.jpg new file mode 100644 index 0000000000..7fda0a9281 Binary files /dev/null and b/user-guide/organize/img/search-section-dark.jpg differ diff --git a/user-guide/organize/img/search-section-light.jpg b/user-guide/organize/img/search-section-light.jpg new file mode 100644 index 0000000000..3834ad398d Binary files /dev/null and b/user-guide/organize/img/search-section-light.jpg differ diff --git a/user-guide/organize/img/search-section.jpg b/user-guide/organize/img/search-section.jpg new file mode 100644 index 0000000000..e235f1e2d7 Binary files /dev/null and b/user-guide/organize/img/search-section.jpg differ diff --git a/user-guide/organize/img/sequential1-dark.jpg b/user-guide/organize/img/sequential1-dark.jpg new file mode 100644 index 0000000000..ad501152ae Binary files /dev/null and b/user-guide/organize/img/sequential1-dark.jpg differ diff --git a/user-guide/organize/img/sequential3.jpg b/user-guide/organize/img/sequential3.jpg new file mode 100644 index 0000000000..e0a28a1f00 Binary files /dev/null and b/user-guide/organize/img/sequential3.jpg differ diff --git a/user-guide/organize/img/sequential4.jpg b/user-guide/organize/img/sequential4.jpg new file mode 100644 index 0000000000..e544083b9f Binary files /dev/null and b/user-guide/organize/img/sequential4.jpg differ diff --git a/user-guide/organize/img/slideshow-dark.jpg b/user-guide/organize/img/slideshow-dark.jpg new file mode 100644 index 0000000000..dd79078a49 Binary files /dev/null and b/user-guide/organize/img/slideshow-dark.jpg differ diff --git a/user-guide/organize/img/slideshow.jpg b/user-guide/organize/img/slideshow.jpg new file mode 100644 index 0000000000..9600a403d5 Binary files /dev/null and b/user-guide/organize/img/slideshow.jpg differ diff --git a/user-guide/organize/img/stack-page-dark.jpg b/user-guide/organize/img/stack-page-dark.jpg new file mode 100644 index 0000000000..6cc85b4640 Binary files /dev/null and b/user-guide/organize/img/stack-page-dark.jpg differ diff --git a/user-guide/organize/img/stacks-edit-dark.jpg b/user-guide/organize/img/stacks-edit-dark.jpg new file mode 100644 index 0000000000..b60ad6a79c Binary files /dev/null and b/user-guide/organize/img/stacks-edit-dark.jpg differ diff --git a/user-guide/organize/img/stacks-edit-light.jpg b/user-guide/organize/img/stacks-edit-light.jpg new file mode 100644 index 0000000000..688caed6ab Binary files /dev/null and b/user-guide/organize/img/stacks-edit-light.jpg differ diff --git a/user-guide/organize/img/states-section-dark.jpg b/user-guide/organize/img/states-section-dark.jpg new file mode 100644 index 0000000000..a6ac31c35d Binary files /dev/null and b/user-guide/organize/img/states-section-dark.jpg differ diff --git a/user-guide/organize/img/states-section-light.jpg b/user-guide/organize/img/states-section-light.jpg new file mode 100644 index 0000000000..abe1a2b743 Binary files /dev/null and b/user-guide/organize/img/states-section-light.jpg differ diff --git a/user-guide/organize/img/terrain-maps-1.jpg b/user-guide/organize/img/terrain-maps-1.jpg new file mode 100644 index 0000000000..773aebfd4b Binary files /dev/null and b/user-guide/organize/img/terrain-maps-1.jpg differ diff --git a/user-guide/organize/img/undo-remove-face.jpg b/user-guide/organize/img/undo-remove-face.jpg new file mode 100644 index 0000000000..1fcbd59006 Binary files /dev/null and b/user-guide/organize/img/undo-remove-face.jpg differ diff --git a/user-guide/organize/img/unsorted-section-dark.jpg b/user-guide/organize/img/unsorted-section-dark.jpg new file mode 100644 index 0000000000..72849101f4 Binary files /dev/null and b/user-guide/organize/img/unsorted-section-dark.jpg differ diff --git a/user-guide/organize/img/unsorted-section-light.jpg b/user-guide/organize/img/unsorted-section-light.jpg new file mode 100644 index 0000000000..5d93ab2cc3 Binary files /dev/null and b/user-guide/organize/img/unsorted-section-light.jpg differ diff --git a/user-guide/organize/img/unstacked-dark.jpg b/user-guide/organize/img/unstacked-dark.jpg new file mode 100644 index 0000000000..1fa5bf4014 Binary files /dev/null and b/user-guide/organize/img/unstacked-dark.jpg differ diff --git a/user-guide/organize/img/video-dark.jpg b/user-guide/organize/img/video-dark.jpg new file mode 100644 index 0000000000..ce504ae00a Binary files /dev/null and b/user-guide/organize/img/video-dark.jpg differ diff --git a/user-guide/organize/img/video-light.jpg b/user-guide/organize/img/video-light.jpg new file mode 100644 index 0000000000..3f53184a43 Binary files /dev/null and b/user-guide/organize/img/video-light.jpg differ diff --git a/user-guide/organize/img/videos.jpg b/user-guide/organize/img/videos.jpg new file mode 100644 index 0000000000..a9c4d5f57a Binary files /dev/null and b/user-guide/organize/img/videos.jpg differ diff --git a/user-guide/organize/img/view-person-2-new.jpg b/user-guide/organize/img/view-person-2-new.jpg new file mode 100644 index 0000000000..d121f1c618 Binary files /dev/null and b/user-guide/organize/img/view-person-2-new.jpg differ diff --git a/user-guide/organize/img/view-person-2.jpg b/user-guide/organize/img/view-person-2.jpg new file mode 100644 index 0000000000..1e89e21799 Binary files /dev/null and b/user-guide/organize/img/view-person-2.jpg differ diff --git a/user-guide/organize/img/view-person-new.jpg b/user-guide/organize/img/view-person-new.jpg new file mode 100644 index 0000000000..dfc02561a2 Binary files /dev/null and b/user-guide/organize/img/view-person-new.jpg differ diff --git a/user-guide/organize/img/view-person.jpg b/user-guide/organize/img/view-person.jpg new file mode 100644 index 0000000000..bea8bbc7ba Binary files /dev/null and b/user-guide/organize/img/view-person.jpg differ diff --git a/user-guide/organize/labels/index.html b/user-guide/organize/labels/index.html new file mode 100644 index 0000000000..a3031d2bb0 --- /dev/null +++ b/user-guide/organize/labels/index.html @@ -0,0 +1,7752 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Labels - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Labels

    +

    PhotoPrism uses labels to classify images. +Other tools use the term tags instead of labels. +Labels are set automatically when adding new photos. +You can manually add new labels or edit/remove the ones that have been created by us.

    +

    In Labels you find all labels of your photos and videos. +You can star labels by clicking . Starred labels will be listed first.

    +

    PhotoPrism also attaches each generated label to a broader group (or category) of labels. For example, there is a general category 'vehicle' which will include labels such as 'cab', 'catamaran', 'lifeboat' and 'bullet train'. These broad label categories cannot be edited, but can be used in a search, in an identical way to all other labels.

    +

    You may wish to see these broad label categories in addition to the usual labels in the Labels pages. Clicking the icon in the upper right-hand corner will switch between turning them on ('Show More') and off ('Show Less'). You can also include these label categories as part of a more complex search filter - the label categories from your photos will appear in the search filter bar as a drop-down selection under the option 'All Categories'.

    +

    Screenshot

    +

    View all Photos with a Label

    +
      +
    1. Go to Labels
    2. +
    3. +

      Click on the label you are interested in

      +

      Screenshot +Screenshot

      +
    4. +
    +
    +

    Alternatively you can use the search field in Photos/Videos. You search for photos with a special label like this: label:dog.

    +
    +

    Add Labels to Photos

    +
      +
    1. Go to the photo edit dialog
    2. +
    3. Go to Labels tab
    4. +
    5. Click on the label field in the last row of the label table
    6. +
    7. Enter a label name
    8. +
    9. +

      Click on the right side of this row

      +

      Screenshot

      +
    10. +
    +

    Remove/Delete Labels from Photos

    +

    Labels that have been set automatically can be removed. +Manually added labels can be deleted.

    +
      +
    1. Go to the photo edit dialog
    2. +
    3. Go to Labels tab
    4. +
    5. +

      Click the / button of the label you want to remove/delete

      +

      Screenshot

      +
    6. +
    +
    +

    Removed labels have a confidence of 0% and can be activated again at any time by clicking add.

    +
    +

    Screenshot

    +
    +

    You can hide Labels in Settings

    +
    +

    Rename Labels

    +
      +
    1. +

      Go to the photo edit dialog of any photo that has the label you want to rename

      +

      Screenshot

      +
    2. +
    3. +

      Go to Labels tab

      +
    4. +
    5. Click on the label name you want to change
    6. +
    7. +

      Change the name and click enter

      +

      Screenshot

      +

      Screenshot

      +
    8. +
    9. +

      The changes will be applied to all photos with this label after the next indexing

      +

      Screenshot

      +
    10. +
    +
    +

    Be aware this change applies to all photos that have this label.

    +
    +

    Delete Labels

    +

    You can permanently delete a label. No file will get a deleted label set during indexing.

    +
      +
    1. Go to Labels
    2. +
    3. Select the label you want to delete
    4. +
    5. Open the context menu
    6. +
    7. +

      Click

      +

      Screenshot

      +
    8. +
    9. +

      Confirm

      +

      Screenshot

      +
    10. +
    +
    +

    In case you want a deleted label to appear again, you need to add it to one photo and then index all files again.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/moments/index.html b/user-guide/organize/moments/index.html new file mode 100644 index 0000000000..78875c80f1 --- /dev/null +++ b/user-guide/organize/moments/index.html @@ -0,0 +1,7648 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Moments - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Moments

    +

    PhotoPrism creates moments out of your memories. +Moments get constantly updated in case you add new photos.

    +
    +

    Moments can be based on location and time e.g. Germany 2020 or on labels like Nature & Landscape or Pets.

    +
    +

    Screenshot

    +

    The context menu allows you to perform the following actions:

    +

    Remove Moments

    +
      +
    1. Select moment
    2. +
    3. Open context menu
    4. +
    5. Click
    6. +
    7. Confirm
    8. +
    +
    +

    Only the moment will be deleted. Your files stay untouched.

    +
    +

    Download Moments

    +
      +
    1. Select moment
    2. +
    3. Open context menu
    4. +
    5. Click
    6. +
    +

    Create Albums from Moments

    +
      +
    1. Select moment
    2. +
    3. Open context menu
    4. +
    5. Click
    6. +
    7. Select existing album or enter new album name
    8. +
    9. Click add to album
    10. +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/panoramas/index.html b/user-guide/organize/panoramas/index.html new file mode 100644 index 0000000000..9038188d2c --- /dev/null +++ b/user-guide/organize/panoramas/index.html @@ -0,0 +1,7583 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Panoramas - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Panoramas

    +

    PhotoPrism automatically marks photos with an aspect ratio of 2/1 or higher as panorama. +You can view all your panorama images in the Panorama section.

    +

    Screenshot

    +

    Edit Panorama Flags

    +
      +
    1. Open the photo edit dialog
    2. +
    3. Click
    4. +
    5. Set or unset the panorama flag
    6. +
    +

    Screenshot

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/people/index.html b/user-guide/organize/people/index.html new file mode 100644 index 0000000000..a79a1ff52f --- /dev/null +++ b/user-guide/organize/people/index.html @@ -0,0 +1,8139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + People - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Face Recognition

    +

    Our latest version includes facial recognition that lets you find pictures +of your family and friends. Be ready to discover long forgotten shots! New faces are detected as +you scan your library. They are then grouped by similarity, so you can quickly match them to people.

    +
    +

    Recognition does not start until your library has been fully scanned. Searching and updating faces +temporarily causes a high CPU load and may take a while, depending on your hardware and the number of +images you have.

    +
    +
    +

    Existing clusters are automatically optimized in the background, for example, when new +faces are detected, you have reported a bad match, or new files are added to your library.

    +
    +

    Recognized & New People

    +

    The people section shows you recognized people as well as new face clusters.

    +

    To star a person click . Starred persons appear first.

    +

    Screenshot +Screenshot

    +

    Why doesn't the New Faces page show all faces?

    +

    The 'New Faces' page only shows automatically recognized face clusters, as there may be thousands +of unknown faces in your library, including random movie actors or faces on shampoo bottles.

    +

    You can use the face:new search filter to find images with unknown people. +We recommend combining this filter with other filters like year or location +when searching for specific people. The People tab in the photo edit dialog +shows all faces, so you can name them or report a bad match by pressing the button.

    +

    When a face was not detected...

    +

    There can be several reasons why a face was not detected:

    +
      +
    • You may need to wait until indexing is complete, as face recognition will not begin until your library has been scanned
    • +
    • Only the primary file in stacks will be searched for faces
    • +
    • Faces can be smaller than the minimum size configured
    • +
    • Our face detection did not scan the image thoroughly enough
    • +
    • Reducing the resolution or quality of generated thumbnails negatively impacts face detection and recognition results, just like when you cannot see properly
    • +
    • Contrast plays a major role, so a bright face with gray hair on a gray background may be less obvious to our face detection than it is to you
    • +
    • In very rare cases, an actual face may be considered a false positive and thus be ignored
    • +
    +
    +

    Recognition compares the similarity of faces. The similarity threshold for a face is reduced when +you report a bad match.

    +
    +

    Assign Names to Faces

    +
    +
    +
    +
      +
    1. Go to People
    2. +
    3. Go to New
    4. +
    5. Click on the input field
    6. +
    7. Start typing a name
    8. +
    9. +

      Press enter

      +

      Screenshot

      +
    10. +
    +
    +
    +
      +
    1. Open the photo edit dialog
    2. +
    3. Go to the People tab
    4. +
    5. Click on the input field
    6. +
    7. Start typing a name
    8. +
    9. +

      Press enter

      +

      Screenshot

      +
    10. +
    +
    +
    +
    +

    The person you just added will appear under Recognized

    +

    Hiding People

    +

    You can hide a person in the Recognized section by clicking in the upper right corner. +Pictures of this person continue to be visible in search results and albums.

    +

    Screenshot

    +

    To see all people including hidden ones click .

    +

    Screenshot

    +

    Hidden people can be recovered by clicking

    +

    Screenshot

    +

    Hiding Faces

    +

    You can hide face clusters from the New section, in the same way you hide people from the Recognized section.

    +

    View all Photos of a Person

    +
    +
    +
    +
      +
    1. Go to People
    2. +
    3. Go to Recognized
    4. +
    5. +

      Click on the person you want to view

      +

      Screenshot

      +
    6. +
    +
    +
    +
      +
    1. Go to Search
    2. +
    3. +

      Search for person:"jane-doe"

      +

      Screenshot

      +
    4. +
    +
    +
    +
    +

    Rename People

    +

    To rename all photos of a person:

    +
      +
    1. Go to People
    2. +
    3. Go to Recognized
    4. +
    5. Click on the persons name
    6. +
    7. Type in a new name
    8. +
    9. Press enter
    10. +
    +

    Screenshot

    +

    Screenshot

    +

    Change People Assignments

    +

    You may report bad matches by pressing the button underneath a face in the People tab. +This will remove the name. You can either leave it blank or enter the name of a different person.

    +
    +

    When you reject a match, the corresponding face cluster will be updated in the background so that similar +issues can be resolved automatically.

    +
    +
      +
    1. Open the photo edit dialog
    2. +
    3. Go to the People tab
    4. +
    5. Click
    6. +
    7. Then enter a new name or leave it empty
    8. +
    +

    Screenshot

    +

    Remove Faces

    +

    In case PhotoPrism detected something wrong as face (false positives), or in case you just don't want to keep a face on the people tab you're not interested in, you can remove it.

    +
      +
    1. Open the photo edit dialogue
    2. +
    3. Go to the People tab
    4. +
    5. Click
    6. +
    +

    Screenshot

    +

    You might undo this action before a reload.

    +

    Screenshot

    +

    Download all Photos of a Person

    +
      +
    1. Go to People
    2. +
    3. Select a person
    4. +
    5. Open context menu
    6. +
    7. Click
    8. +
    +

    Create Albums from People

    +
      +
    1. Go to People
    2. +
    3. Select a person
    4. +
    5. Open context menu
    6. +
    7. Click
    8. +
    9. Select existing album or enter new album name
    10. +
    11. Click add to album
    12. +
    +

    Screenshot

    + +

    You can find photos with people on it using the following queries:

    +
      +
    • people, faces or faces:true will result in all photos with people
    • +
    • faces:false will show all photos without people
    • +
    • faces:3 will show all photos with at least 3 people on it
    • +
    • person:"John Doe" or subject:"John Doe" will show all photos of the person with the exact name John Doe
    • +
    • people:"John" or subjects:"John" will show all photos of people with a name like John e.g. John Doe and John Smith
    • +
    +

    The person/subject and people/subjects filters can be used with & and | (see search for more details). Filters may be combined.

    +

    person:"John Doe&Jane Doe" faces:3 will show all photos with John and Jane Doe and one other person.

    +

    Screenshot

    +

    Known Issues

    +

    For all known issues, see Getting Started > Known Issues > Face Recognition.

    +

    Legacy Hardware

    +

    Face recognition can be slow (or even crash) on old devices due to insufficient resources.

    +

    Like most applications, PhotoPrism has certain requirements and our development process does not include testing on unsupported or unusual hardware.

    +

    Asian Faces and Children

    +

    It is a known issue that children and Asian-looking faces cannot be recognized reliably. Detection without automatic recognition should not be affected by that.

    +

    This is because the model we use was trained with North American images, which unfortunately do not include many Asians. The absence of children in the training data comes from the fact that parents do not usually share such images under a public license (and may not have the right to do so).

    +

    We will continue to improve our models over time as our resources allow.

    +

    Background Worker

    +

    Face recognition was developed and tested under the assumption that the background worker runs every 15 minutes, unless the backend is busy with other tasks like indexing. It has not been tested with much longer intervals and is not designed for that.

    +

    PhotoPrism's background worker groups new faces by similarity, compares faces with clusters, and optimizes existing clusters as needed. Without these routine tasks, the number of faces to be processed becomes too large. The first and next time the worker runs, it can then cause a heavy server load until all the faces, face clusters, and related pictures have been updated. The longer you wait, the more CPU is required and the longer it takes.

    +

    An important reason for the worker to run independently of actual changes in the main instance is that some users change the database content directly or run additional instances, for example for indexing. It is a problem that can be solved, but it takes time. If we were to ignore this and don't run the worker at all times, it could lead to many additional support requests, further reducing the amount of time we can spend on development.

    +

    The handling of changes in multiple instances will be improved over time so that the worker can be run less frequently in future releases.

    +
    +

    Upcoming Features

    +
      +
    • manual tagging of faces
    • +
    • importing of XMP face tags
    • +
    • excluding people when browsing your pictures
    • +
    • automatic backup of tagged people in YAML files
    • +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/places/index.html b/user-guide/organize/places/index.html new file mode 100644 index 0000000000..ff2c38b089 --- /dev/null +++ b/user-guide/organize/places/index.html @@ -0,0 +1,7637 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Places - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Places

    +

    Places displays all photos with GPS information on a world map.

    +

    Screenshot

    +

    You can set a default map style in settings or choose between different styles by clicking .

    +

    Clicking on a cluster, opens the cluster overlay.

    +

    Screenshot

    +

    Screenshot

    +

    To open photos from this location in the search click .

    +

    To clear the location filter click .

    +

    Screenshot

    + +

    When using the search only photos matching the search term are shown on the map. You can use most of our search filters on the map as well.

    +

    Screenshot

    +

    Enable Terrain Mode

    +

    Our "Satellite", "Outdoor" and "Topography" maps can also be viewed in 3D.

    +

    To enable terrain mode click . To change the perspective, you can hold down the right mouse button and move it.

    +

    Screenshot

    +

    Open Photo from Search in Places

    +

    To navigate directly from the cards results view to the location of a picture on the world map, click on its location.

    +

    Screenshot + Screenshot

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/private/index.html b/user-guide/organize/private/index.html new file mode 100644 index 0000000000..50a3b2c077 --- /dev/null +++ b/user-guide/organize/private/index.html @@ -0,0 +1,7640 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Private - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Hiding Private Photos

    +

    What does private mean?

    +

    Some of your photos might be private for personal reasons. +Our private functionality provides you with a solution to hide private photos or videos from some sections. +This way you can let family and friends browse through your photos without risking that they see photos you do not want them to see.

    +

    By default, photos marked as private will not appear in the following sections:

    +
      +
    • Search
    • +
    • Videos
    • +
    • People
    • +
    • Favorites
    • +
    • Places
    • +
    • Labels
    • +
    • Autogenerated Albums (Moments, Calendar, States, Folders)
    • +
    • Shared Albums
    • +
    +

    Private photos will be displayed in the private section, in user generated albums and within the file browser.

    +
    +

    In case you want private content to appear everywhere you can configure that in Settings.

    +
    +

    Toggle Private Flag

    +
    +
    +
    +
      +
    1. Go to Search
    2. +
    3. Select photos/videos
    4. +
    5. Click context menu
    6. +
    7. +

      Click

      +

      Screenshot

      +
    8. +
    +
    +
    +
      +
    1. Go to Search
    2. +
    3. Make sure you are in list view
    4. +
    5. +

      Click or on the right

      +

      Screenshot

      +
    6. +
    +
    +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/review/index.html b/user-guide/organize/review/index.html new file mode 100644 index 0000000000..0baac5bac4 --- /dev/null +++ b/user-guide/organize/review/index.html @@ -0,0 +1,7624 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Review - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Reviewing Non-Photographic and Low-Quality Images

    +

    When adding new photos a quality score from 1 to 5 is created automatically. +Photos with a quality score lower than 3 do not appear in Search until you approve them (unless the quality filter was disabled +in Settings)

    +

    The quality score depends on the following:

    +
      +
    • Known date and/or GPS coordinates
    • +
    • At least 2 MP resolution if taken after 2012
    • +
    • Photo not classified as info or screenshot
    • +
    • Photo is favorite, was edited or approved
    • +
    +
    +

    In case you do not need the review mechanism you can turn it off in Settings

    +
    +

    Approve Photos

    +
    +
    +
    +
      +
    1. Go to Review
    2. +
    3. Select photos and open the context menu
    4. +
    5. +

      Click

      +

      Screenshot

      +
    6. +
    +
    +
    +
      +
    1. Go to Review and make sure you are in cards view
    2. +
    3. +

      Click

      +

      Screenshot

      +
    4. +
    +
    +
    +
      +
    1. Open the photo's edit dialog
    2. +
    3. +

      Click approve

      +

      Screenshot

      +
    4. +
    +
    +
    +
    +
    +

    The quality score is constantly updated. +If you add date or location information to a photo or like it, the quality score increases automatically. +In case the new score is equal or greater than 3 the photo is approved automatically.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/rotate/index.html b/user-guide/organize/rotate/index.html new file mode 100644 index 0000000000..129312295d --- /dev/null +++ b/user-guide/organize/rotate/index.html @@ -0,0 +1,7516 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Rotate Image - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Rotate Image

    +

    PhotoPrism allows you to rotate JPG and PNG images.

    +
      +
    1. Open the photo edit dialog
    2. +
    3. Go to the Files tab
    4. +
    5. Click on the orientation icon and choose the correct orientation
    6. +
    +

    Screenshot

    +

    Screenshot

    +

    The updated orientation is stored in the metadata of your original file. Therefore, it is not possible to rotate images in read-only mode at this time.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/scans/index.html b/user-guide/organize/scans/index.html new file mode 100644 index 0000000000..d948c01e12 --- /dev/null +++ b/user-guide/organize/scans/index.html @@ -0,0 +1,7582 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Scans - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Scans

    +

    You can mark scanned photos as scans within the photo edit dialog.

    +

    We aim to automatically mark scans in the future.

    +

    Set Scan Flags

    +
      +
    1. Open the photo edit dialog
    2. +
    3. Click
    4. +
    5. Set or unset the scan flag
    6. +
    +

    Screenshot

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/search/index.html b/user-guide/organize/search/index.html new file mode 100644 index 0000000000..32764d5223 --- /dev/null +++ b/user-guide/organize/search/index.html @@ -0,0 +1,15 @@ + + + + + + Redirecting... + + + + + + +Redirecting... + + diff --git a/user-guide/organize/stacks/index.html b/user-guide/organize/stacks/index.html new file mode 100644 index 0000000000..0b53729fe8 --- /dev/null +++ b/user-guide/organize/stacks/index.html @@ -0,0 +1,7799 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Stacks - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Stacks

    +

    Stacks are groups of files that have the same origin but differ in quality, format, size, or color. Go to Settings > Library to change the stacks-related settings for your library.

    +

    To see all images with a group of related files, open Stacks in the expanded Search navigation:

    +

    Screenshot

    +

    Since videos and Live Photos are always stacked with a still image, they are not included in the search results when you navigate to Search > Stacks.

    +

    For what reasons can files be stacked?

    +
      +
    1. Files that share the same file and folder name (except for the file extension) are always stacked, for example /2018/IMG_1234.jpg and /2018/IMG_1234.avi
    2. +
    3. Files with sequential names like /2018/IMG_1234 (2).jpg and /2018/IMG_1234 (3).jpg can be stacked as well (optional)
    4. +
    5. File metadata indicates that the pictures were taken at the same position within the same second (optional)
    6. +
    7. File metadata includes the same Unique Image ID or XMP Instance ID (optional)
    8. +
    +

    You can change your preferences for 2 - 4 in the Stacks section under Settings > Library.

    +
    +

    Note that it is not possible to disable stacking of files with the same name as this would break important functionality, most notably support for Apple Live Photos (which consist of a photo and a video file), any other multi-file/hybrid formats like RAW/JPEG, and indexing of metadata from XMP/JSON sidecar files.

    +
    +

    Are files automatically unstacked when I change the settings?

    +

    When you change the stacks-related settings under Settings > Library, files that are already stacked will not be unstacked automatically. This is because unstacking is a resource-intensive operation that requires each file to be re-indexed.

    +

    The result also depends on the exact order in which you unstack the files, as non-media sidecar files, for example, remain bound to the remaining media file in a stack. We consider providing a command for this in a future release and appreciate any contributions in this regard.

    +
    +

    If you are new to PhotoPrism and want to re-index your library with different settings, you can run the photoprism reset command in a terminal to reset the index and start from scratch. Learn more ›

    +
    +

    Which sequential naming patterns are supported?

    +

    If stacking by Sequential Name has been enabled, files with e.g. the following names would be stacked with the file /2018/IMG_1234.jpg:

    +
      +
    • /2018/IMG_1234 (2).jpg /2018/IMG_1234 (3).jpg
    • +
    • /2018/IMG_1234 copy.jpg /2018/IMG_1234 copy 1.jpg /2018/IMG_1234 copy 2.jpg
    • +
    • /2018/IMG_1234 (-2.7) /2018/IMG_1234 (+3.3).jpg /2018/IMG_1234(-2.7).jpg /2018/IMG_1234(+3.3).jpg
    • +
    + +
      +
    1. +

      Click on

      +

      Screenshot

      +
    2. +
    3. +

      Use arrows to see all photos of the sequence

      +

      Screenshot Screenshot

      +
    4. +
    +

    Change Primary Files

    +

    The JPEG file marked as primary is used in our views. It is listed first in the files tab.

    +

    To change the primary file:

    +
      +
    1. +

      Open the photos edit dialog

      +
    2. +
    3. +

      Open Files tab

      +
    4. +
    5. +

      Click of the file you want to set as primary

      +
    6. +
    7. +

      Click primary

      +

      Screenshot

      +
    8. +
    +

    Unstack Files

    +
      +
    1. +

      Open the photos edit dialog

      +
    2. +
    3. +

      Open Files tab

      +
    4. +
    5. +

      Click of the JPEG file that is not marked as primary

      +
    6. +
    7. +

      Click unstack

      +
    8. +
    +

    Screenshot

    +

    Now, both files appear in our views.

    +

    Screenshot

    +

    Delete Non-Primary Files

    +
      +
    1. +

      Open the photos edit dialog

      +
    2. +
    3. +

      Open Files tab

      +
    4. +
    5. +

      Click of the JPEG file that is not marked as primary

      +
    6. +
    7. +

      Click delete

      +
    8. +
    9. +

      Confirm

      +
    10. +
    +

    Screenshot

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/video/index.html b/user-guide/organize/video/index.html new file mode 100644 index 0000000000..2d90674f2b --- /dev/null +++ b/user-guide/organize/video/index.html @@ -0,0 +1,7631 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Videos - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Browsing and Playing Videos

    +

    Navigate to Videos to browse all your videos. To play a video, click .

    +

    Please note that not all video and audio formats can be played with every browser. For example, AAC - the default audio codec for MPEG-4 AVC / H.264 - is supported natively in Chrome, Safari, and Edge, while it is only optionally supported by the OS in Firefox and Opera.

    +

    Screenshot

    +
    +

    In case FFmpeg is disabled or not installed, videos cannot be indexed because still images cannot be created. +You should also have ExifTool enabled to extract metadata such as duration, resolution, and codec.

    +
    +

    Live Photos

    +

    Short videos up to 3 seconds are categorized and displayed as Live Photos, regardless of your phone's make and model. +You can recognize this by the icon that appears in the upper left corner.

    +

    Move the mouse cursor over the thumbnail to play the video without changing the view. +You can limit a search to Live Photos by using the type:live filter or the keyword live.

    +

    Screenshot

    +

    Transcoding

    +

    For maximum browser compatibility, PhotoPrism can transcode video codecs and containers supported by FFmpeg to MPEG-4 AVC, as well as extract still images for thumbnail creation:

    +
      +
    • if FFmpeg is disabled or not installed, indexing and importing videos is not possible because still images cannot be created
    • +
    • if ExifTool is disabled or not installed, indexing and importing videos is only partially possible because the video metadata cannot be extracted and thus the duration, resolution, and codec are unknown
    • +
    • MPEG-4 AVC videos can be played natively by most modern browsers and are not re-encoded, even if they exceed the configured bitrate limit; to reduce the size of AVC videos, you can manually replace the original files with a smaller version or wait for a future release that offers this functionality
    • +
    • OGV, VP8, VP9, AV1, WebM, and HEVC videos will be streamed directly if they are supported by your browser and do not exceed the configured bitrate limit
    • +
    • other formats must always be transcoded
    • +
    +

    If necessary, videos are transcoded on demand. This can cause unacceptable delays when large video files are played for the first time.

    +

    In this case, you can run the following command in a terminal to pre-transcode all video files as needed:

    +
    docker compose exec photoprism photoprism convert
    +
    +

    Our setup guide for advanced users explains how to configure hardware video transcoding.

    +
    +

    Make sure that there is enough disk space available on your server before transcoding all video files, as this may +require a significant amount of extra storage.

    +
    +
    +

    HEVC/H.265 video files can have a .mp4 file extension too, which is often associated with AVC only. This is because +MP4 is a container format, meaning that the actual video content may be compressed with H.264, H.265, or something +else. The file extension doesn't really tell you anything other than that it's probably a video file.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/organize/views/index.html b/user-guide/organize/views/index.html new file mode 100644 index 0000000000..3165a5a010 --- /dev/null +++ b/user-guide/organize/views/index.html @@ -0,0 +1,15 @@ + + + + + + Redirecting... + + + + + + +Redirecting... + + diff --git a/user-guide/pwa/index.html b/user-guide/pwa/index.html new file mode 100644 index 0000000000..dbaf2630ff --- /dev/null +++ b/user-guide/pwa/index.html @@ -0,0 +1,7640 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Mobile App (PWA) - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Mobile App (PWA)

    +

    PhotoPrism currently does not include a native app that can be installed through an app store. However, there are many compatible apps, and you can conveniently install our Progressive Web App (PWA) on your desktop or home screen for an almost native app-like experience.

    +

    Installation Requirements

    +

    The compatibility of our PWA has been tested with Apple Safari and Google Chrome, but other modern browsers like Firefox or Microsoft Edge may generally be compatible as well.

    +
    +

    When self-hosting PhotoPrism, please make sure the site URL is configured correctly. In addition, PWAs must be hosted on a dedicated domain with HTTPS in order to be installed. If that is not possible, you can still choose "Create Shortcut...", "Add to Home Screen...", or a similarly named action from the browser menu to make the app accessible from your home screen.

    +
    +

    Step-by-Step Instructions

    +
    +
    +
    +
      +
    1. Open PhotoPrism in Safari
    2. +
    3. +

      Click

      +

      Screenshot

      +
    4. +
    5. +

      Click Add to Home Screen

      +

      Screenshot

      +
    6. +
    7. +

      Choose a name and click Add

      +

      Screenshot

      +
    8. +
    +
    +
    +
      +
    1. Open PhotoPrism in Chrome
    2. +
    3. +

      Click

      +

      Screenshot

      +
    4. +
    5. +

      Click Install app

      +

      Screenshot

      +
    6. +
    7. +

      Choose a name and click Add

      +

      Screenshot

      +
    8. +
    +
    +
    +
    +

    The PWA is now installed on the home screen of your device and can be launched from there.

    +

    Screenshot

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/search/filters/index.html b/user-guide/search/filters/index.html new file mode 100644 index 0000000000..14f7811b04 --- /dev/null +++ b/user-guide/search/filters/index.html @@ -0,0 +1,8189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Search Filters - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Using Search Filters

    +

    Powerful search filters let you easily find specific photos and videos, for example:

    +
      +
    • Persons visible on a picture
    • +
    • Objects that are displayed on a picture
    • +
    • The main color of a picture
    • +
    • The file or folder name of a picture
    • +
    • Location where a picture has been taken
    • +
    • Other metadata such as camera, lens, chroma...
    • +
    +

    Just give it a try!

    +

    Screenshot

    +

    Introduction

    +

    The following filters can be set via dropdowns in the search toolbar:

    +
      +
    • Country, Year, Month, Order, Camera, Color, Category.
    • +
    +

    If you set multiple filters, only pictures that meet all filter criteria will be displayed in the search result. Filters can generally be combined unless they contradict each other.

    +

    Screenshot

    +

    In addition, these and many other filters can be entered into the toolbar search box as follows:

    +
    label:cat color:green type:live
    +
    +

    A complete overview of the available search filters can be found below.

    +

    Screenshot

    + +

    To combine different filters use a space as separator:

    +
    mono:true review:false
    +
    +

    The search result shows pictures that are monochrome and not in review.

    +

    Additionally some filters can be combined with & as follows:

    +
    keywords:buffalo&water
    +
    +

    or:

    +
    keywords:"buffalo & water"
    +
    +

    This query will show all photos that have the keywords water and buffalo.

    +

    & is supported by the following filters:

    +
      +
    • albums, keywords, subject/person, subjects/people.
    • +
    +
    +

    The label filter does not support &. You can use the keywords filter instead, as all labels are keywords as well.

    +
    + +

    An OR search is possible using |:

    +
    label:cat|dog
    +
    +

    This will show all photos that have either the label cat or dog.

    +

    The following filters work with |:

    +
      +
    • albums, color, country, state, city, day, month, year, keywords, label, path, subject/person, subjects/people, title, type, name, filename, original, hash
    • +
    +

    Wildcard

    +

    The * character will act as a wildcard:

    +
    name:"IMG_23*"
    +
    +

    This will show all photos which name start with IMG_23.

    +
    name:"*_23*"
    +
    +

    This will show all photos which name contain _23, like IMG_2356.MOV , 2021_02_23.jpg, etc.

    +
    +

    Wildcards can be combined with & or |: filename:"*IMG123*|*_22F6FC19.jpg"

    +
    +

    Filter Reference

    +

    This is a complete list of supported search filters with examples. Filters can generally be combined unless they contradict each other, e.g. results cannot be monochrome and have high color saturation at the same time.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FilterTypeExamplesNotes
    distdecimaldist:50Distance to Position (km)
    latdecimallat:41.894043GPS Position (Latitude)
    lngdecimallng:-87.62448GPS Position (Longitude)
    chromanumberchroma:70Chroma (0-100)
    diffnumberdiff:-1 diff:2Differential Perceptual Hash (000000-FFFFFF)
    qualitynumberquality:0 quality:3Minimum quality score (1-7)
    albumstringalbum:berlinAlbum UID or Name, supports * wildcards
    albumsstringalbums:"South Africa & Birds"Album Names (combinable with & and |)
    altstringalt:300-500GPS Altitude (m)
    camerastringcamera:canonCamera Make/Model Name
    categorystringcategory:airportLocation Category
    citystringcity:"Berlin"Location City (separate with |)
    colorstringcolor:"red|blue"Color Name (purple, magenta, pink, red, orange, gold, yellow, lime, green, teal, cyan, blue, brown, white, grey, black) (separate with |)
    countrystringcountry:"de|us"Location Country Code (separate with |)
    daystringday:3|13Day of Month (1-31, separate with |)
    fstringf:2.8-4.5Aperture (f-number)
    facestringface:PN6QO5INYTUSAATOFL43LL2ABAV5ACZGFace ID, yes, no, new, or kind
    facesstringfaces:yes faces:3Minimum number of Faces (yes = 1)
    favoritestringfavorite:true favorite:falseFinds images by favorite status
    filenamestringfilename:"2021/07/12345.jpg"File Name with path and extension (separate with |)
    folderstringfolder:"*/2020"Path Name (separate with |), supports * wildcards
    geostringgeo:yesFinds pictures with or without coordinates
    hashstringhash:2fd4e1c67a2dSHA1 File Hash (separate with |)
    idstringid:123e4567-e89b-...Finds pictures by Exif UID, XMP Document ID or Instance ID
    isostringiso:200-400ISO Number (light sensitivity)
    keywordsstringkeywords:"sand&water"Keywords (combinable with & and |)
    labelstringlabel:cat|dogLabel Names (separate with |)
    latlngstringlatlng:"name"GPS Bounding Box (Lat N, Lng E, Lat S, Lng W)
    lensstringlens:ef24Lens Make/Model Name
    mmstringmm:28-35Focal Length (35mm equivalent)
    monthstringmonth:7|10Month (1-12, separate with |)
    mpstringmp:3-6Resolution in Megapixels (MP)
    namestringname:"IMG_9831-112*"File Name without path and extension (separate with |)
    nearstringnear:pqbcf5j446s0futyFinds nearby pictures (UID)
    olcstringolc:8FWCHX7W+OLC Position (Open Location Code)
    originalstringoriginal:"IMG_9831-112*"Original file name of imported files (separate with |)
    pathstringpath:2020/HolidayPath Name (separate with |), supports * wildcards
    peoplestringpeople:"Jane & John"Subject Names (combinable with & and |)
    personstringperson:"Jane Doe & John Doe"Subject Names, exact matches (combinable with & and |)
    s2strings2:4799e370ca54c8b9S2 Position (Cell ID)
    scanstringscan:true scan:falseFinds scanned photos and documents
    statestringstate:"Baden-Württemberg"Location State (separate with |)
    subjectstringsubject:"Jane Doe & John Doe"Alias for person
    subjectsstringsubjects:"Jane & John"Alias for people
    titlestringtitle:"Lake*"Title (separate with |)
    typestringtype:rawMedia Type (image, video, raw, live, animated); separate with |
    uidstringuid:pqbcf5j446s0futyLimits results to the specified internal unique IDs
    yearstringyear:1990|2003Year (separate with |)
    animatedswitchanimated:yesFinds animated GIFs
    archivedswitcharchived:yesFinds archived pictures
    errorswitcherror:yesFinds pictures with errors
    hiddenswitchhidden:yesFinds hidden pictures (broken or unsupported)
    landscapeswitchlandscape:yesFinds pictures in landscape format
    liveswitchlive:yesFinds Live Photos and short videos
    monoswitchmono:yesFinds pictures with few or no colors
    panoramaswitchpanorama:yesFinds pictures with an aspect ratio > 1.9:1
    photoswitchphoto:yesFinds only photos, no videos
    portraitswitchportrait:yesFinds pictures in portrait format
    primaryswitchprimary:yesFinds primary JPEG files only
    privateswitchprivate:yesFinds private pictures
    publicswitchpublic:yesExcludes private pictures
    rawswitchraw:yesFinds pictures with RAW image file
    reviewswitchreview:yesFinds pictures in review
    squareswitchsquare:yesFinds images with an aspect ratio of 1:1
    stackswitchstack:yesFinds pictures with more than one media file
    stackableswitchstackable:yesFinds pictures that can be stacked with additional media files
    unsortedswitchunsorted:yesFinds pictures not in an album
    unstackedswitchunstacked:yesFinds pictures with a file that has been removed from a stack
    vectorswitchvector:yesFinds vector graphics only
    videoswitchvideo:yesFinds video files only
    aftertimestampafter:"2022-01-30"Finds pictures taken after this date
    beforetimestampbefore:"2022-01-30"Finds pictures taken before this date
    +
    +

    Why can't I play live photos or find stacks when I search for specific images?

    +

    Our search API and user interface perform a file search. This is intentional since "stacks" can contain files of different types and properties, such as color.

    +

    For example, there may be color and monochrome versions. Now, when you search for them or sort them by color, the user interface must display individual files. Otherwise, the results showing a color image/video when you filter by monochrome would make no sense.

    +

    Likewise, if you search for filename.mp4.*, you will find only JPEGs without video, because the video file extension is .mp4 without an extra dot at the end.

    +

    We recommend using the path: and/or name: filters with wildcards if searching for individual files limits the search results too much. Most users will want to find all related files so that they can be displayed together, e.g. as live photos consisting of a video and an image.

    +

    You can combine these filters with other filters such as live to ensure that the results include only pictures with a specific media type. Alternatively, you can use the filename: filter with a more permissive wildcard that excludes the file extension.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/search/index.html b/user-guide/search/index.html new file mode 100644 index 0000000000..3bd15c5c1d --- /dev/null +++ b/user-guide/search/index.html @@ -0,0 +1,8155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Search Your Library - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    + +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Browsing and Searching Your Library

    +

    PhotoPrism offers many views and search filters so you can explore your photo collection in multiple dimensions instead of just scrolling through it day by day.

    +

    This helps you rediscover long-forgotten shots, find specific pictures, or quickly create albums based on search results.

    +

    Views and Filters

    +

    Using the main navigation you can visit the different sections of your photo library:

    + +

    Shows all photos and videos that are not in review or archived or private.

    +
    +

    In case the review, private or archive functions are turned off - Search displays those photos and videos as well.

    +
    +

    Screenshot

    +

    Monochrome

    +

    Shows all monochrome photos and videos.

    +

    Panoramas

    +

    Shows all panorama photos.

    +

    Stacks

    +

    Shows all stacked photos.

    +

    Scans

    +

    Shows all scans.

    +

    Review

    +

    Shows all photos that are in review.

    +

    Archive

    +

    Shows archived photos.

    +

    Albums

    +

    Shows albums you created.

    +

    Screenshot

    +

    Unsorted

    +

    Shows all photos that are not part of an album.

    +

    Screenshot

    +

    Videos

    +

    Shows videos that are not in review or archived or private.

    +

    Live Photos

    +

    Shows all short videos up to 3 seconds.

    +

    People

    +

    Shows photos and videos grouped by people on it.

    +

    Favorites

    +

    Shows all photos and videos you liked.

    +

    Moments

    +

    Discover albums we automatically create for you.

    +

    Screenshot

    +

    Calendar

    +

    Organizes your photos due to time taken.

    +

    Screenshot

    +

    Places

    +

    Displays all photos and videos with location information on a worldmap.

    +

    Screenshot

    +

    States

    +

    Shows your photos grouped by location.

    +

    Screenshot

    +

    Labels

    +

    Shows your photos and videos grouped by labels like cat, dog or beach.

    +

    Screenshot

    +

    Folders

    +

    Displays all folders of your originals directory.

    +

    Screenshot

    +

    Private

    +

    Shows photos and videos marked as private.

    +

    Originals

    +

    Hierarchical view of your originals directory.

    +

    Screenshot

    +

    Screenshot

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/search/views/index.html b/user-guide/search/views/index.html new file mode 100644 index 0000000000..9d14ba1db7 --- /dev/null +++ b/user-guide/search/views/index.html @@ -0,0 +1,7530 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Search Result Views - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Search Result Views

    +

    PhotoPrism offers you three different views to browse your photos and videos. In addition you can choose between multiple light and dark themes.

    +
    +
    +
    +

    The cards view displays important metadata like title, time and location next to the photos

    +

    Screenshot

    +

    Screenshot

    +
    +
    +

    The mosaic view lets you enjoy your photos without distraction

    +

    Screenshot

    +

    Screenshot

    +
    +
    +

    The list view provides you photos and metadata in a well-arranged list

    +

    Screenshot

    +

    Screenshot

    +
    +
    +
    +

    To switch between views you can either use the filter in the filter bar or the view button (, , ) in the upper right corner.

    +

    Additionally, you can open your photos/videos in fullscreen mode and start a slideshow ().

    +

    Screenshot

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/settings/account/index.html b/user-guide/settings/account/index.html new file mode 100644 index 0000000000..09b09a1961 --- /dev/null +++ b/user-guide/settings/account/index.html @@ -0,0 +1,7672 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Account - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Account Settings

    +
    +

    For security reasons, changing account-related settings through the user interface requires password authentication, so these settings will not be available to you when public mode is enabled.

    +
    +

    Screenshot

    +

    Change Password

    +
      +
    1. Go to Settings
    2. +
    3. Open Account tab
    4. +
    5. Click Change Password
    6. +
    7. Enter your current password
    8. +
    9. Enter your new password twice
    10. +
    11. Click Change
    12. +
    +

    Screenshot

    +

    2-Factor Authentication

    +

    Two-factor authentication (2FA) can add an extra layer of security to your account in case someone gains access to your password. If enabled, you will need a randomly generated verification code in addition to your password to log in.

    +

    Learn more ›

    +

    Apps and Devices

    +

    If 2FA is enabled for your account, other apps and services will no longer be able to use your password as they do not have access to the verification codes.

    +

    You can therefore generate app-specific passwords for them by navigating to Settings > Account and then clicking the Apps and Devices button. We also recommend using app-specific passwords in case 2FA is not enabled for your account.

    +

    Example for generating an app password that you can use with WebDAV-compatible file synchronization apps like PhotoSync:

    +

    Screenshot

    +
    +

    By selecting the WebDAV scope, you ensure that the app password cannot be used to log in through the regular user interface or for other actions. Apps will also not be able to change your password or manage user accounts, even if you grant them Full Access.

    +
    +

    Connect via WebDAV

    +

    To open a dialog that shows you the URLs required to connect an app or computer via WebDAV:

    +
      +
    1. Go to Settings
    2. +
    3. Open Account tab
    4. +
    5. Click Connect via WebDAV
    6. +
    +

    Screenshot

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/settings/advanced/index.html b/user-guide/settings/advanced/index.html new file mode 100644 index 0000000000..2f09f2efa1 --- /dev/null +++ b/user-guide/settings/advanced/index.html @@ -0,0 +1,8774 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Advanced - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Advanced Settings

    +

    System config options such as the image quality can be changed +on the advanced settings page. You can also disable specific features and enable the debug or read-only mode.

    +
    +

    Since they are not safe to use without authentication, these settings are not available when running in public mode. Changing config options is still possible via configuration files and with command parameters.

    +
    +
    +

    Changing advanced settings always requires a restart to take effect. Selecting a different thumbnail +quality or size won't replace existing thumbnails. You can regenerate them using the +command-line interface.

    +
    +

    +

    All config options can be set in your compose.yaml or docker-compose.yml or +via command-line parameters as well. Manually changed values are saved in a config file. It is stored in +the storage/config folder by default.

    +

    Global Options

    +

    Debug Logs

    +

    When enabled, debug logs are shown in Library>Logs. +Requires restart.

    +

    The corresponding config toggle is PHOTOPRISM_DEBUG.

    +

    Experimental Features

    +

    When enabled, your instance will be updated with experimental features.

    +

    The corresponding config toggle is PHOTOPRISM_EXPERIMENTAL.

    +

    Read-only Mode

    +

    When enabled, importing, uploading and deleting files is not possible.

    +

    The corresponding config toggle is PHOTOPRISM_READONLY.

    +

    Disable Backups

    +

    This option prevents the creation of database, album and YAML sidecar file backups.

    +

    The corresponding config toggle is PHOTOPRISM_DISABLE_BACKUPS.

    + + +

    Disable WebDAV

    +

    This option prevents building WebDav connections. +Requires restart for changes to be applied.

    +

    The corresponding config toggle is PHOTOPRISM_DISABLE_WEBDAV.

    +

    Disable Places

    +

    When selected, geo-information (latitude, longitude) will still be read (and indexed) +from your files metadata, however PhotoPrism will not use reverse lookup to +determine place names using those coordinates as it normally would.

    +

    The Places section will not be visible.

    +

    The corresponding config toggle is PHOTOPRISM_DISABLE_PLACES.

    +

    Disable ExifTool

    +

    This option prevents the creation of json files with Exif data in storage/sidecar. +Note that you must have ExifTool enabled to extract video metadata such as duration, resolution, and codec.

    +

    The corresponding config toggle is PHOTOPRISM_DISABLE_EXIFTOOL.

    +

    Disable TensorFlow

    +

    When selected, image classification and facial recognition will be disabled as both rely on tensorflow.

    +

    The corresponding config toggle is PHOTOPRISM_DISABLE_TENSORFLOW.

    +

    Backups

    +

    Database Backups

    +

    When selected, database backups are created based on the configured schedule.

    +

    The corresponding config toggle is PHOTOPRISM_BACKUP_DATABASE. +The schedule as well as the number of backups that will be retained can be configured with PHOTOPRISM_BACKUP_SCHEDULE and PHOTOPRISM_BACKUP_RETAIN.

    +

    Album Backups

    +

    When selected, YAML files that back up album metadata will be created based on the configured schedule.

    +

    The corresponding config toggle is PHOTOPRISM_BACKUP_ALBUMS. +The backup schedule can be configured with PHOTOPRISM_BACKUP_SCHEDULE.

    +

    Sidecar Files

    +

    When selected, YAML files that back up picture metadata will be created.

    +

    The corresponding config toggle is PHOTOPRISM_SIDECAR_YAML.

    +

    Preview Images

    +

    This section controls how JPEG preview and thumbnail images are rendered. These are high-quality, scaled-down versions of your originals.

    +

    Thumbnails are necessary because web browsers are bad at resizing large images to fit the screen. Using full-resolution originals for slideshows and in search results would also consume a lot of browser memory and significantly reduce indexing performance.

    +

    Downscaling Filter

    +

    This lets you select the algorithm used to resize your original images when creating thumbnails. +A detailed description of the available filters can be found in the section below.

    +

    For a good trade-off between quality and performance, we recommend choosing the lanczos filter. It may be a little slower in creating thumbnails, but produces very high quality images. In comparison, the less sophisticated cubic filter may be 30% faster.

    +

    The corresponding config option is PHOTOPRISM_THUMB_FILTER.

    +
    +

    This option is only available if the native imaging image processing library has been enabled by setting PHOTOPRISM_THUMB_LIBRARY to “imaging” in your compose.yaml or docker-compose.yml configuration file.

    +
    +

    Static and Dynamic Size Limits

    +

    Static Size Limit: During initial indexing or import (as thumbnails are generated), +no thumbnails will be created above this size. +The corresponding config option is PHOTOPRISM_THUMB_SIZE.

    +

    Dynamic Size Limit: During dynamic (on-demand) thumbnail generation, +no thumbnails will be created above this size. +The corresponding config option is PHOTOPRISM_THUMB_SIZE_UNCACHED.

    +
    +

    Reducing the Static Size Limit of thumbnails has a significant impact on face recognition and image classification results. Simply put, it means that the indexer can no longer see properly.

    +
    +
    +

    If the configured size limit is exceeded (for example, if users have a larger screen), a sufficiently large thumbnail can't be created, and the photo viewer may be forced to display the original image instead. +Downscaling images in browsers typically results in poor quality, and they may also be displayed in the wrong orientation.

    +
    +

    The smallest configurable size is 720px for consumption by the indexer to perform color detection, face detection, +and image classification. Recreating them every time they are needed is too demanding for even the most powerful +servers. Unless you only have a few small images, it would render the app unusable.

    +

    It is recommended that you set these limits high so that browsing pictures is as smooth as possible. +However, if the amount of disk storage required is a serious problem, and you are +willing to increase server load instead, it is possible to set the +Static Size Limit to the minimum of 720px in combination with a higher Dynamic Size Limit. +This allows the server to generate larger thumbnails on demand. It may also result in a noticeable delay +when viewing pictures in full-screen mode.

    +
    +

    To view original images, enable Dynamic Previews, and configure Dynamic Size Limit and Static Size Limit to a small value like 720. When viewing images exceeding that limit, the original files will be displayed.

    +
    +

    Which thumbnails will be generated?

    +

    The smallest configurable static and dynamic size limit is 720px, so most sizes up to fit_720 are always generated by default. +Higher size limits generate thumbnails with more detail at higher resolutions - either statically (pre-generated while indexing) or on demand if the configuration permits.

    +

    Optional thumbnail sizes cannot be pre-generated and are only rendered on request, for example when sharing an image on Instagram.

    +

    The following overview shows the name, dimensions, and aspect ratio for each thumbnail size as well as a description of how it is used:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameWidthHeightAspect RatioAvailableUsage
    colors331:1AlwaysColor Detection
    tile_5050501:1AlwaysList View
    tile_1001001001:1AlwaysPlaces View
    left_2242242241:1On-DemandTensorFlow
    right_2242242241:1On-DemandTensorFlow
    tile_2242242241:1AlwaysTensorFlow, Mosaic View
    tile_5005005001:1AlwaysCards View
    fit_720720720PreservedAlwaysSD TV, Mobile
    tile_1080108010801:1OptionalInstagram
    fit_128012801024PreservedOn-DemandHD TV, SXGA
    fit_16001600900PreservedOptionalSocial Media
    fit_192019201200PreservedOn-DemandFull HD
    fit_204820482048PreservedOptionalDCI 2K, Tablets
    fit_256025601600PreservedOn-DemandQuad HD, Notebooks
    fit_384038402400PreservedOptional4K Ultra HD
    fit_409640964096PreservedOn-DemandDCI 4K, Retina 4K
    fit_768076804320PreservedOn-Demand8K Ultra HD 2
    +
    +

    Generated thumbnail files are stored in the storage/cache/thumbnails folder, where the path and file name depend on the size and file hash, e.g. storage/cache/thumbnails/1/a/3/1a30c1f...9_100x100_center.jpg.

    +
    +

    Dynamic Previews

    +

    Enable generating thumbnails on-the-fly as they're required +(either for viewing or analysing with TensorFlow). +This saves disk space, but is more processor-intensive and so not recommended +when hosting on less powerful devices (such as Raspberry Pi).

    +

    The corresponding config toggle is PHOTOPRISM_THUMB_UNCACHED.

    +

    Image Quality

    +

    JPEG Quality

    +

    Choose a value above 90 to display your images in the best possible quality. Note that higher values +require more space in the storage folder for less compressed thumbnail files, which may also take longer to create.

    +

    Lower quality thumbnails, on the other hand, are smaller, load faster on slow Internet connections, +and require less space in the storage folder and in the browser cache.

    +
      +
    • Quality levels of 90% or higher are generally considered high quality
    • +
    • 80% to 90% is considered medium quality
    • +
    • 70% to 80% is considered low quality, as you might see with highly compressed content on social media
    • +
    +

    Anything below 70% is generally of very low quality.

    +

    Example: If a quality of 95 results in a thumbnail file size of 500kB, then reducing the quality +to 80 reduces the file size to about 100kB.

    +

    The corresponding config option is PHOTOPRISM_JPEG_QUALITY.

    +
    +

    The actual compression depends on how much information an image contains. Empty areas and skies, +for example, are easier to compress. Images with a lot of details suffer the most. +For this reason, reducing the quality of thumbnails also negatively impacts face recognition +and image classification results. Simply put, this means that the indexer sees fewer details.

    +
    +

    JPEG Size Limit

    +

    This sets the maximum size of the JPEG files created when converting original RAW images.

    +

    The corresponding config toggle is PHOTOPRISM_JPEG_SIZE.

    +
    +

    RawTherapee and "heif-convert" cannot limit the resolution of JPEG files when converting files from other formats such as RAW, DNG, HEIC or AVIF.

    +
    +

    PNG Size Limit

    +

    This sets the maximum size of the PNG files created when converting original images.

    +

    The corresponding config toggle is PHOTOPRISM_PNG_SIZE.

    +

    File Conversion

    +

    Many photographers keep their originals in some sort of lossless RAW format instead of compressed JPEG, especially when shooting with a Digital SLR. Some mobile phones also support RAW or use HEIC/HEIF for a similar purpose. PhotoPrism aims at providing excellent support for all RAW formats, independent of camera brand and model. Please let us know when there is an issue with your specific device.

    +

    Web browsers in general cannot display RAW image files. They need to be converted, which is what our import and convert commands do. You'll also find a checkbox for this step in our Web UI.

    +

    In addition, PhotoPrism also supports TIFF, PNG, BMP and GIF files. Be aware that files in those formats often don't contain useful metadata and are typically used for screenshots, charts, graphs and icons only.

    +

    +
    +

    Generated sidecar files will be stored outside your originals folder by default, so that +RAW to JPEG conversion also works in read-only mode.

    +
    +

    Disable Darktable

    +

    If this feature is disabled, Darktable will not be used for RAW conversion.

    +

    The corresponding config toggle is PHOTOPRISM_DISABLE_DARKTABLE.

    +

    Disable RawTherapee

    +

    If this feature is disabled, RawTherapee is not used for RAW conversion.

    +

    The corresponding config toggle is PHOTOPRISM_DISABLE_RAWTHERAPEE.

    +

    Use Presets

    +

    Disables simultaneous conversion of RAW files to apply Darktable presets.

    +

    The corresponding config toggle is PHOTOPRISM_RAW_PRESETS.

    +

    Disable ImageMagick

    +

    If this feature is disabled, ImageMagick is not used for conversion.

    +

    The corresponding config toggle is PHOTOPRISM_DISABLE_IMAGEMAGICK.

    +

    Disable FFmpeg

    +

    If this feature is disabled, FFmpeg is not used to transcode videos or extract still images for thumbnail creation, and indexing or importing video files is not possible.

    +

    The corresponding config toggle is PHOTOPRISM_DISABLE_FFMPEG.

    +
    +

    To prevent inexperienced users from accidentally disabling the creation of thumbnails for videos FFmpeg can only be disabled when Experimental Features are enabled.

    +
    +

    Downscaling Filters

    +

    Linear

    +

    Bilinear interpolation takes a weighted average of the four +neighborhood pixels to calculate its final interpolated +value. The result is a much smoother image than the original +image. When all known pixel distances are equal, then the +interpolated value is simply their sum divided by four. +This technique performs interpolation in both directions, +horizontal and vertical. This technique gives better result +than nearest neighbor interpolation and take less +computation time compared to bicubic interpolation.

    +

    Cubic

    +

    Catmull-Rom is a local interpolating spline developed for +computer graphics purposes. Its initial use was in design +of curves and surfaces, and has recently been used in +several applications. Catmull-Rom splines are a family of +cubic interpolating splines formulated such that the +tangent at each point is calculated using the previous and +next point on the spline. The results are similar to ones +produced by bicubic interpolation with regards to +sharpness, but the Catmull-Rom reconstruction is clearly +superior in smooth signal region.

    +

    Lanczos

    +

    The Lanczos interpolation function is a mathematical formula +used to smoothly interpolate the value of a digital +image between its samples. It maps each sample of the +given image to a translated and scaled copy of the Lanczos +kernel, which is a sinc function windowed by the central +hump of a dilated sinc function. The sum of these +translated and scaled kernels is then evaluated at the +desired pixel. Lanczos interpolation has the best +properties in terms of detail preservation and minimal +generation of aliasing artifacts for geometric +transformations not involving strong down sampling. +However higher order Lanczos interpolation requires high +computational time, which makes it unsuitable for +most commercial software.

    +

    Blackman

    +

    Blackman is a modification of Lanczos that has better control of ringing artifacts.

    +

    Examples

    +

    Original image:

    +

    +

    The same image resized from 600x400px to 150x100px using different resampling filters. +From faster (lower quality) to slower (higher quality):

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    FilterResize result
    Nearest Neighbor
    Bilinear
    Sharp Bicubic
    Lanczos
    +

    Source: A Comparative Analysis of Image Interpolation Algorithms

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/settings/general/index.html b/user-guide/settings/general/index.html new file mode 100644 index 0000000000..0ec76d6fc0 --- /dev/null +++ b/user-guide/settings/general/index.html @@ -0,0 +1,8089 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + General - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    General Settings

    +

    In the General settings tab, you can configure basic user interface settings as well as the maps in Places:

    +

    +

    User Interface

    +

    You can change the theme and language of the User interface.

    +

    To make PhotoPrism suit your individual needs, the following sections and functionalities can be en- or disabled. +Disabled sections do not appear in the main navigation.

    +

    Upload

    +

    When disabled, uploading files via upload is not possible. +This might be useful when you grant others access to your PhotoPrism but do not want them to upload photos.

    +

    Download

    +

    When disabled, no files can be downloaded using the PhotoPrism UI. Please note that it may still be possible to download files using the integrated browser functionalities.

    +

    Share

    +

    When disabled, album sharing and upload to remote services like ownCloud is not possible.

    +

    People

    +

    When disabled, the people section is hidden. To disable face detection while indexing, you may set PHOTOPRISM_DISABLE_FACES and/or PHOTOPRISM_DISABLE_TENSORFLOW to "true" in your config.

    +

    Hide Private

    +

    Excludes content marked as private from search results, shared albums, labels and places.

    +

    Archive

    +

    When disabled, there is no Archive. Photos that have been archived beforehand will appear again in search results.

    +

    Edit

    +

    When disabled, it is not possible to edit photo details.

    +

    Delete

    +

    When disabled, permanent deletion of files from the archive is not possible.

    +

    Originals

    +

    When disabled, there is no Originals section.

    +

    Moments

    +

    When disabled, there is no Moments section.

    +

    Labels

    +

    When disabled, there is no Labels section and you cannot add or edit labels.

    +

    Library

    +

    When disabled, there is no Library section.

    +

    Import

    +

    When disabled, there is no possibility to import photos. You need to use index instead to add new photos.

    +

    Logs

    +

    When disabled, server logs are not shown.

    +

    Places

    +

    When disabled, there is no Places section.

    +

    Places

    +

    At the bottom of the General settings tab, you may choose your preferred map style and animation length for Places. +PhotoPrism includes four high-resolution world maps to see where you've been, and for rediscovering long-forgotten shots.

    +

    To enhance your photos with location data such as state, city and category, we've also launched our own geo-information service based on OpenStreetMap. +A future release will additionally provide public events' data, so that albums of popular music festivals, or sports gatherings, can be created automatically.

    +

    Downloads

    +
    +

    Note that your choice will not affect ZIP archives when you download complete albums. However, we may add settings for this in a future release.

    +
    +

    Originals

    +

    Only the files in the originals folder will be downloaded, but not any files that were automatically created in the sidecar folder. This is the recommended default.

    +

    RAW

    +

    Download RAW image files.

    +

    Sidecar

    +

    Download sidecar files as used for XMP metadata. This is generally not recommended except for some professional workflows.

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/settings/img/branches.jpg b/user-guide/settings/img/branches.jpg new file mode 100644 index 0000000000..ad59dd431e Binary files /dev/null and b/user-guide/settings/img/branches.jpg differ diff --git a/user-guide/settings/img/change-password-light.jpg b/user-guide/settings/img/change-password-light.jpg new file mode 100644 index 0000000000..e36baead49 Binary files /dev/null and b/user-guide/settings/img/change-password-light.jpg differ diff --git a/user-guide/settings/img/editPhoto.jpg b/user-guide/settings/img/editPhoto.jpg new file mode 100644 index 0000000000..856ce62ef7 Binary files /dev/null and b/user-guide/settings/img/editPhoto.jpg differ diff --git a/user-guide/settings/img/out_resize_catrom.jpg b/user-guide/settings/img/out_resize_catrom.jpg new file mode 100644 index 0000000000..217f1c88e4 Binary files /dev/null and b/user-guide/settings/img/out_resize_catrom.jpg differ diff --git a/user-guide/settings/img/out_resize_lanczos.jpg b/user-guide/settings/img/out_resize_lanczos.jpg new file mode 100644 index 0000000000..9c2b31527a Binary files /dev/null and b/user-guide/settings/img/out_resize_lanczos.jpg differ diff --git a/user-guide/settings/img/out_resize_linear.jpg b/user-guide/settings/img/out_resize_linear.jpg new file mode 100644 index 0000000000..5891aee658 Binary files /dev/null and b/user-guide/settings/img/out_resize_linear.jpg differ diff --git a/user-guide/settings/img/out_resize_nearest.jpg b/user-guide/settings/img/out_resize_nearest.jpg new file mode 100644 index 0000000000..02ec6b46c8 Binary files /dev/null and b/user-guide/settings/img/out_resize_nearest.jpg differ diff --git a/user-guide/settings/img/placesPrivacy.jpeg b/user-guide/settings/img/placesPrivacy.jpeg new file mode 100644 index 0000000000..fe6406f138 Binary files /dev/null and b/user-guide/settings/img/placesPrivacy.jpeg differ diff --git a/user-guide/settings/img/services-connect-1-light.jpg b/user-guide/settings/img/services-connect-1-light.jpg new file mode 100644 index 0000000000..4d697bdc1e Binary files /dev/null and b/user-guide/settings/img/services-connect-1-light.jpg differ diff --git a/user-guide/settings/img/services-connect-2-light.jpg b/user-guide/settings/img/services-connect-2-light.jpg new file mode 100644 index 0000000000..60b6cc75c6 Binary files /dev/null and b/user-guide/settings/img/services-connect-2-light.jpg differ diff --git a/user-guide/settings/img/services-edit-light.jpg b/user-guide/settings/img/services-edit-light.jpg new file mode 100644 index 0000000000..ca25d8cc0e Binary files /dev/null and b/user-guide/settings/img/services-edit-light.jpg differ diff --git a/user-guide/settings/img/services-sync-1-light.jpg b/user-guide/settings/img/services-sync-1-light.jpg new file mode 100644 index 0000000000..31ce7710f4 Binary files /dev/null and b/user-guide/settings/img/services-sync-1-light.jpg differ diff --git a/user-guide/settings/img/services-sync-2-light.jpg b/user-guide/settings/img/services-sync-2-light.jpg new file mode 100644 index 0000000000..d9165f9f92 Binary files /dev/null and b/user-guide/settings/img/services-sync-2-light.jpg differ diff --git a/user-guide/settings/img/services-upload-1-light.jpg b/user-guide/settings/img/services-upload-1-light.jpg new file mode 100644 index 0000000000..7faef46ea1 Binary files /dev/null and b/user-guide/settings/img/services-upload-1-light.jpg differ diff --git a/user-guide/settings/img/services-upload-2-light.jpg b/user-guide/settings/img/services-upload-2-light.jpg new file mode 100644 index 0000000000..a490f6e19b Binary files /dev/null and b/user-guide/settings/img/services-upload-2-light.jpg differ diff --git a/user-guide/settings/img/settings-account-light.jpg b/user-guide/settings/img/settings-account-light.jpg new file mode 100644 index 0000000000..d43b190fdf Binary files /dev/null and b/user-guide/settings/img/settings-account-light.jpg differ diff --git a/user-guide/settings/img/settings-advanced-light.jpg b/user-guide/settings/img/settings-advanced-light.jpg new file mode 100644 index 0000000000..8a61e4ce9c Binary files /dev/null and b/user-guide/settings/img/settings-advanced-light.jpg differ diff --git a/user-guide/settings/img/settings-advanced.jpg b/user-guide/settings/img/settings-advanced.jpg new file mode 100644 index 0000000000..7129a1d533 Binary files /dev/null and b/user-guide/settings/img/settings-advanced.jpg differ diff --git a/user-guide/settings/img/settings-general-light.jpg b/user-guide/settings/img/settings-general-light.jpg new file mode 100644 index 0000000000..4a3a6621ea Binary files /dev/null and b/user-guide/settings/img/settings-general-light.jpg differ diff --git a/user-guide/settings/img/settings-general.jpg b/user-guide/settings/img/settings-general.jpg new file mode 100644 index 0000000000..83824877b1 Binary files /dev/null and b/user-guide/settings/img/settings-general.jpg differ diff --git a/user-guide/settings/img/settings-library-light.jpg b/user-guide/settings/img/settings-library-light.jpg new file mode 100644 index 0000000000..c077cee110 Binary files /dev/null and b/user-guide/settings/img/settings-library-light.jpg differ diff --git a/user-guide/settings/img/show-webdav-light.jpg b/user-guide/settings/img/show-webdav-light.jpg new file mode 100644 index 0000000000..d7ef5d76a9 Binary files /dev/null and b/user-guide/settings/img/show-webdav-light.jpg differ diff --git a/user-guide/settings/library/index.html b/user-guide/settings/library/index.html new file mode 100644 index 0000000000..a59180bcdb --- /dev/null +++ b/user-guide/settings/library/index.html @@ -0,0 +1,7774 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Library - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    Library Settings

    +

    +
    +

    Library Settings are only available for Super Admins.

    +
    +

    Index

    +

    Estimates

    +

    Estimates the location of pictures taken without GPS information by extrapolating it from the location of other pictures taken on the same day.

    +
    +

    Be aware that, if you have pictures from unrelated events at different locations, the GPS coordinates of pictures from one event will be applied/extrapolated to pictures of the other event that lack coordinates (even if these are in different folders).

    +
    +

    Quality Filter

    +

    Requires a review of non-photographic and low-quality images before they appear in search results.

    +

    Preview Images

    +

    Automatically creates JPEG or PNG preview images for other file types so they can be displayed in search results and in the full-screen viewer.

    +
    +

    Preview Images should be enabled, otherwise PhotoPrism cannot index file types other than JPEG or PNG unless a preview sidecar file with the same filename prefix already exists. See Stacks to learn more about naming conventions of sidecar files.

    +
    +
    +

    To prevent inexperienced users from accidentally disabling the creation of thumbnails Preview Images can only be disabled when Experimental Features are enabled.

    +
    +

    Stacks

    +

    Stacks are groups of files that have the same origin but may differ in quality, format, size, or color. You can navigate to Search > Stacks to find pictures with stacked media files.

    +

    PhotoPrism offers you the following optional stacking methods, which you can choose to enable based on your personal preferences:

    +
      +
    • Place & Time stacks pictures taken at same GPS position and second
    • +
    • Unique ID, matches the ImageUniqueID (Exif) or Instance ID (XMP)
    • +
    • Sequential Name, for example /2018/IMG_1234 (2).jpg and /2018/IMG_1234 (3).jpg
    • +
    +

    Files that share the same file and folder name (except for the file extension) are always stacked, for example /2018/IMG_1234.jpg and /2018/IMG_1234.avi.

    +
    +

    Note that it is not possible to disable stacking of files with the same name as this would break important functionality, most notably support for Apple Live Photos (which consist of a photo and a video file), any other multi-file/hybrid formats like RAW/JPEG, and indexing of metadata from XMP/JSON sidecar files.

    +
    +

    Are files automatically unstacked when I change the settings?

    +

    When you change these settings, files that are already stacked will not be unstacked automatically. This is because unstacking is a resource-intensive operation that requires each file to be re-indexed.

    +

    The result also depends on the exact order in which you unstack the files, as non-media sidecar files, for example, remain bound to the remaining media file in a stack. We consider providing a command for this in a future release and appreciate any contributions in this regard.

    +
    +

    If you are new to PhotoPrism and want to re-index your library with different settings, you can run the photoprism reset command in a terminal to reset the index and start from scratch.

    +
    +

    Which sequential naming patterns are supported?

    +

    If stacking by Sequential Name has been enabled, files with e.g. the following names would be stacked with the file /2018/IMG_1234.jpg:

    +
      +
    • /2018/IMG_1234 (2).jpg /2018/IMG_1234 (3).jpg
    • +
    • /2018/IMG_1234 copy.jpg /2018/IMG_1234 copy 1.jpg /2018/IMG_1234 copy 2.jpg
    • +
    • /2018/IMG_1234 (-2.7) /2018/IMG_1234 (+3.3).jpg /2018/IMG_1234(-2.7).jpg /2018/IMG_1234(+3.3).jpg
    • +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/settings/sync/index.html b/user-guide/settings/sync/index.html new file mode 100644 index 0000000000..0366329c62 --- /dev/null +++ b/user-guide/settings/sync/index.html @@ -0,0 +1,7735 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Services - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Services

    +

    You can connect your PhotoPrism instance to other services with WebDAV support, such as other PhotoPrism instances, Nextcloud or ownCloud.

    +

    This might be useful if you want to share or synchronize files between multiple services.

    +
    +

    Since they are not safe to use without authentication, these settings are not available when running in public mode.

    +
    +
    +

    PhotoPrism can also serve/share files via WebDAV to be browsed on macOS or Windows. +See instructions.

    +
    +

    Add Service

    +

    <! -- PhotoPrism may connect with WebDAV servers like a second PhotoPrism instance or ownCloud, +so that you can automatically sync your latest pictures. -->

    +
      +
    1. Go to Settings
    2. +
    3. Open Services tab
    4. +
    5. Click Connect + Screenshot
    6. +
    7. Fill in your service url, username and password
    8. +
    9. Click connect + Screenshot
    10. +
    11. Now the other service is connected with PhotoPrism
    12. +
    +

    Edit Connection Details

    +
      +
    1. Go to Settings
    2. +
    3. Open Services tab
    4. +
    5. Click
    6. +
    7. Edit account details and click Save
    8. +
    +

    Screenshot

    +

    Edit Upload Settings

    +
      +
    1. Go to Settings
    2. +
    3. Open Services tab
    4. +
    5. Click into the upload cell of your service
    6. +
    +

    Screenshot +4. Select the folder to which photos should be uploaded and click save

    +

    Screenshot

    +

    You can now share albums or files with this service.

    +
    +

    Due to problems with some Nextcloud settings it might be that uploading to Nextcloud results in 0 byte files. You find information on how to solve it here.

    +
    +

    Edit Sync Settings

    +
      +
    1. Go to Settings
    2. +
    3. Open Services tab
    4. +
    5. Click into the sync cell of your service + Screenshot
    6. +
    7. Enable synchronization in the upper right corner
    8. +
    9. Choose a folder from your service
    10. +
    11. Choose a sync interval
    12. +
    13. Select the options that are suitable for you and click Save
    14. +
    +

    Screenshot

    +

    Remote Sync Options

    +
      +
    • Download remote files will download all files from the selected folder of the other service that do not yet exist in PhotoPrism
    • +
    • Upload local files will upload all files (including private or archived ones) from PhotoPrism to your service that do not yet exist there
    • +
    • Preserve filenames will keep filenames without renaming them
    • +
    • Sync raw and video files will upload/download raw and video files alongside with JPEGS
    • +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/share/img/link-card-dark.jpg b/user-guide/share/img/link-card-dark.jpg new file mode 100644 index 0000000000..0a5628f5b6 Binary files /dev/null and b/user-guide/share/img/link-card-dark.jpg differ diff --git a/user-guide/share/img/link-card-light.jpg b/user-guide/share/img/link-card-light.jpg new file mode 100644 index 0000000000..92be69be48 Binary files /dev/null and b/user-guide/share/img/link-card-light.jpg differ diff --git a/user-guide/share/img/link-mosaic-dark.jpg b/user-guide/share/img/link-mosaic-dark.jpg new file mode 100644 index 0000000000..4bd0fa5eb1 Binary files /dev/null and b/user-guide/share/img/link-mosaic-dark.jpg differ diff --git a/user-guide/share/img/link-mosaic-light.jpg b/user-guide/share/img/link-mosaic-light.jpg new file mode 100644 index 0000000000..d0f1e47aee Binary files /dev/null and b/user-guide/share/img/link-mosaic-light.jpg differ diff --git a/user-guide/share/img/link-overview-dark.jpg b/user-guide/share/img/link-overview-dark.jpg new file mode 100644 index 0000000000..edcd864046 Binary files /dev/null and b/user-guide/share/img/link-overview-dark.jpg differ diff --git a/user-guide/share/img/link-overview-light.jpg b/user-guide/share/img/link-overview-light.jpg new file mode 100644 index 0000000000..ce6bed89b1 Binary files /dev/null and b/user-guide/share/img/link-overview-light.jpg differ diff --git a/user-guide/share/img/link-places-dark.jpg b/user-guide/share/img/link-places-dark.jpg new file mode 100644 index 0000000000..3d4f43b0bb Binary files /dev/null and b/user-guide/share/img/link-places-dark.jpg differ diff --git a/user-guide/share/img/link-places-light.jpg b/user-guide/share/img/link-places-light.jpg new file mode 100644 index 0000000000..3240c145ce Binary files /dev/null and b/user-guide/share/img/link-places-light.jpg differ diff --git a/user-guide/share/img/services-album-upload-1-light.jpg b/user-guide/share/img/services-album-upload-1-light.jpg new file mode 100644 index 0000000000..52f340ff51 Binary files /dev/null and b/user-guide/share/img/services-album-upload-1-light.jpg differ diff --git a/user-guide/share/img/services-album-upload-2-light.jpg b/user-guide/share/img/services-album-upload-2-light.jpg new file mode 100644 index 0000000000..0764e6801a Binary files /dev/null and b/user-guide/share/img/services-album-upload-2-light.jpg differ diff --git a/user-guide/share/img/services-photo-upload-1-light.jpg b/user-guide/share/img/services-photo-upload-1-light.jpg new file mode 100644 index 0000000000..374bac16db Binary files /dev/null and b/user-guide/share/img/services-photo-upload-1-light.jpg differ diff --git a/user-guide/share/img/services-photo-upload-2-light.jpg b/user-guide/share/img/services-photo-upload-2-light.jpg new file mode 100644 index 0000000000..68be41d254 Binary files /dev/null and b/user-guide/share/img/services-photo-upload-2-light.jpg differ diff --git a/user-guide/share/img/share-delete-1-dark.jpg b/user-guide/share/img/share-delete-1-dark.jpg new file mode 100644 index 0000000000..840ddd8b63 Binary files /dev/null and b/user-guide/share/img/share-delete-1-dark.jpg differ diff --git a/user-guide/share/img/share-delete-1-german.jpg b/user-guide/share/img/share-delete-1-german.jpg new file mode 100644 index 0000000000..838aa8190a Binary files /dev/null and b/user-guide/share/img/share-delete-1-german.jpg differ diff --git a/user-guide/share/img/share-delete-2-dark.jpg b/user-guide/share/img/share-delete-2-dark.jpg new file mode 100644 index 0000000000..a4816ecbf8 Binary files /dev/null and b/user-guide/share/img/share-delete-2-dark.jpg differ diff --git a/user-guide/share/img/share-delete-2-german.jpg b/user-guide/share/img/share-delete-2-german.jpg new file mode 100644 index 0000000000..3f262f9abf Binary files /dev/null and b/user-guide/share/img/share-delete-2-german.jpg differ diff --git a/user-guide/share/img/share-dialog-add-dark.jpg b/user-guide/share/img/share-dialog-add-dark.jpg new file mode 100644 index 0000000000..c1ae33c2c5 Binary files /dev/null and b/user-guide/share/img/share-dialog-add-dark.jpg differ diff --git a/user-guide/share/img/share-dialog-copy-dark.jpg b/user-guide/share/img/share-dialog-copy-dark.jpg new file mode 100644 index 0000000000..1bd6bc44c7 Binary files /dev/null and b/user-guide/share/img/share-dialog-copy-dark.jpg differ diff --git a/user-guide/share/img/share-menu-dark.jpg b/user-guide/share/img/share-menu-dark.jpg new file mode 100644 index 0000000000..de6f95af84 Binary files /dev/null and b/user-guide/share/img/share-menu-dark.jpg differ diff --git a/user-guide/share/img/share-toolbar-dark.jpg b/user-guide/share/img/share-toolbar-dark.jpg new file mode 100644 index 0000000000..74d4123124 Binary files /dev/null and b/user-guide/share/img/share-toolbar-dark.jpg differ diff --git a/user-guide/share/index.html b/user-guide/share/index.html new file mode 100644 index 0000000000..2e07f76b8d --- /dev/null +++ b/user-guide/share/index.html @@ -0,0 +1,7665 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Link Sharing - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Sharing Albums via Link

    +

    Create sharing links to share albums, moments, calendar months, states or folders with your friends.

    +

    Your friends are able to view and download photos from shared albums. +Photos marked as private won't show up.

    +

    Screenshot

    +
    +

    When link visitors click on the location of a photo, they can view the photos of the shared album in the map view

    +
    +

    Screenshot

    +

    Clicking allows link visitors to end their session.

    +

    Support for optional password protection of sharing links as well as other enhancements are planned.

    + +
    +
    +
    +
      +
    1. Go to Albums / Moments / Calendar / States / Folders
    2. +
    3. Select the album you want to share
    4. +
    5. Open the context menu
    6. +
    7. Click
    8. +
    +

    Screenshot

    +
    +
    +
      +
    1. Go to Albums / Moments / Calendar / States / Folders
    2. +
    3. Open the album by clicking on it
    4. +
    5. Click
    6. +
    +

    Screenshot

    +
    +
    +
    +

    Then

    +
      +
    1. Click to open the link details
    2. +
    3. Set a secret and expiry date
    4. +
    5. +

      Click save

      +

      Screenshot

      +
    6. +
    7. +

      Copy the link by clicking on it

      +

      Screenshot

      +
    8. +
    9. +

      Share it with your friends

      +
    10. +
    +
    +

    Share multiple albums with one link

    +

    You can share multiple albums using the same link by using the same secret.

    +
    +
    +

    You can create additional links with different secrets and expiry dates by clicking .

    +
    + +
      +
    1. Go to Albums
    2. +
    3. +

      Click on the album cover

      +

      Screenshot

      +
    4. +
    5. +

      Click

      +
    6. +
    7. +

      Click

      +

      Screenshot

      +
    8. +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/share/services-share/index.html b/user-guide/share/services-share/index.html new file mode 100644 index 0000000000..97fbecc235 --- /dev/null +++ b/user-guide/share/services-share/index.html @@ -0,0 +1,7620 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + With Apps - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Share with other Apps

    +

    In settings you can connect your PhotoPrism instance to other services with WebDAV support, such as other PhotoPrism instances, Nextcloud or ownCloud.

    +

    Share Files with other Apps

    +
      +
    1. Go to Search
    2. +
    3. Select photos you want to upload
    4. +
    5. Open the context menu
    6. +
    7. Click + Screenshot
    8. +
    9. Select your account and click upload
    10. +
    +

    Screenshot

    +
    +

    Due to problems with some Nextcloud settings it might be that uploading to Nextcloud results in 0 byte files. You find information on how to solve it here.

    +
    +

    Share Albums with other Apps

    +
      +
    1. Go to Albums / Moments / Calendar / States / Folders
    2. +
    3. Select the album you want to share
    4. +
    5. Open the context menu
    6. +
    7. Click
    8. +
    9. Click WebDAV Upload + Screenshot
    10. +
    11. Select your account and click upload
    12. +
    +

    Screenshot

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/sync/dropbox/index.html b/user-guide/sync/dropbox/index.html new file mode 100644 index 0000000000..0fe3b0be2c --- /dev/null +++ b/user-guide/sync/dropbox/index.html @@ -0,0 +1,7629 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Syncing with Dropbox - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Syncing with Dropbox

    +

    It's possible to use Dropbox to store your photos, while viewing and managing them through PhotoPrism.

    +
      +
    1. Set up a Dropbox account.
    2. +
    3. Install the Dropbox desktop client.
    4. +
    5. Sync your Dropbox to a local directory.
    6. +
    7. If using Docker, configure your compose.yaml or docker-compose.yml with; +
      volumes:
      +  - "~/Dropbox/Photos:/photoprism/originals"
      +
    8. +
    9. Follow the PhotoPrism getting started guide as normal.
    10. +
    +

    Auto-upload from Mobile

    +

    The Dropbox mobile apps also have a 'Camera Upload' feature which syncs photos to Dropbox, and then to any machine with Dropbox installed.

    +

    To auto-import uploaded files into PhotoPrism;

    +
      +
    1. Install the Dropbox iOS or Android app.
    2. +
    3. Enable 'Camera Uploads' in the Dropbox app's settings.
    4. +
    5. Install the Dropbox desktop client on your server or a network-accessible machine
    6. +
    7. Configure the Camera Uploads folder as your import directory for PhotoPrism. + In your compose.yaml or docker-compose.yml file, this is; +
      volumes:
      +  - "~/Dropbox/Camera Uploads:/photoprism/import"
      +
    8. +
    9. Optional: Enable 'delete on import' in PhotoPrism's settings to delete imported files from Dropbox. This saves Dropbox space, allowing you to remain within the 2 GB free tier.
    10. +
    +
    +

    The Dropbox mobile app needs to be opened periodically or it tends to fail to identify and sync new photos.

    +
    +

    Smart / Selective Sync

    +

    A useful (although paid) feature is Dropbox Smart Sync (with optional auto-evict) which will download the files from Dropbox's servers only when you (or PhotoPrism) accesses them (such as during initial indexing, or when downloading an original file via the PhotoPrism interface).

    +

    This can save space on your server by automically offloading the originals unless/until they're viewed.

    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/sync/img/photosync-1.jpg b/user-guide/sync/img/photosync-1.jpg new file mode 100644 index 0000000000..4aba15e0c0 Binary files /dev/null and b/user-guide/sync/img/photosync-1.jpg differ diff --git a/user-guide/sync/img/photosync-2.jpg b/user-guide/sync/img/photosync-2.jpg new file mode 100644 index 0000000000..71323ccbfd Binary files /dev/null and b/user-guide/sync/img/photosync-2.jpg differ diff --git a/user-guide/sync/img/photosync-3.jpg b/user-guide/sync/img/photosync-3.jpg new file mode 100644 index 0000000000..064ed8ece9 Binary files /dev/null and b/user-guide/sync/img/photosync-3.jpg differ diff --git a/user-guide/sync/img/photosync-4.jpg b/user-guide/sync/img/photosync-4.jpg new file mode 100644 index 0000000000..9e64bc7c42 Binary files /dev/null and b/user-guide/sync/img/photosync-4.jpg differ diff --git a/user-guide/sync/img/photosync-5.jpg b/user-guide/sync/img/photosync-5.jpg new file mode 100644 index 0000000000..78be51664b Binary files /dev/null and b/user-guide/sync/img/photosync-5.jpg differ diff --git a/user-guide/sync/img/photosync-6.jpg b/user-guide/sync/img/photosync-6.jpg new file mode 100644 index 0000000000..d104dd42cd Binary files /dev/null and b/user-guide/sync/img/photosync-6.jpg differ diff --git a/user-guide/sync/img/photosync-7.jpg b/user-guide/sync/img/photosync-7.jpg new file mode 100644 index 0000000000..50ff48308c Binary files /dev/null and b/user-guide/sync/img/photosync-7.jpg differ diff --git a/user-guide/sync/img/webdav-1.jpg b/user-guide/sync/img/webdav-1.jpg new file mode 100644 index 0000000000..f3faf9ddff Binary files /dev/null and b/user-guide/sync/img/webdav-1.jpg differ diff --git a/user-guide/sync/img/webdav-2.jpg b/user-guide/sync/img/webdav-2.jpg new file mode 100644 index 0000000000..ea4bf39fdc Binary files /dev/null and b/user-guide/sync/img/webdav-2.jpg differ diff --git a/user-guide/sync/img/webdav-3.jpg b/user-guide/sync/img/webdav-3.jpg new file mode 100644 index 0000000000..d26079afbe Binary files /dev/null and b/user-guide/sync/img/webdav-3.jpg differ diff --git a/user-guide/sync/img/webdav-4.jpg b/user-guide/sync/img/webdav-4.jpg new file mode 100644 index 0000000000..21f8fe9e9d Binary files /dev/null and b/user-guide/sync/img/webdav-4.jpg differ diff --git a/user-guide/sync/img/webdav-5.jpg b/user-guide/sync/img/webdav-5.jpg new file mode 100644 index 0000000000..539a9f1f06 Binary files /dev/null and b/user-guide/sync/img/webdav-5.jpg differ diff --git a/user-guide/sync/img/webdav-6.jpg b/user-guide/sync/img/webdav-6.jpg new file mode 100644 index 0000000000..a328f98b3d Binary files /dev/null and b/user-guide/sync/img/webdav-6.jpg differ diff --git a/user-guide/sync/img/webdav-7.jpg b/user-guide/sync/img/webdav-7.jpg new file mode 100644 index 0000000000..678d2a5f6c Binary files /dev/null and b/user-guide/sync/img/webdav-7.jpg differ diff --git a/user-guide/sync/img/webdav-8.jpg b/user-guide/sync/img/webdav-8.jpg new file mode 100644 index 0000000000..36e4aee1d3 Binary files /dev/null and b/user-guide/sync/img/webdav-8.jpg differ diff --git a/user-guide/sync/img/webdav-9.jpg b/user-guide/sync/img/webdav-9.jpg new file mode 100644 index 0000000000..c430735664 Binary files /dev/null and b/user-guide/sync/img/webdav-9.jpg differ diff --git a/user-guide/sync/mobile-devices/index.html b/user-guide/sync/mobile-devices/index.html new file mode 100644 index 0000000000..a4535202e1 --- /dev/null +++ b/user-guide/sync/mobile-devices/index.html @@ -0,0 +1,7770 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Mobile Devices - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Syncing with Mobile Devices

    +

    You can use any app that supports the WebDAV protocol to synchronize photos and videos between your phone and PhotoPrism.

    +

    Based on our own experience, we can highly recommend PhotoSync as it is one of the most feature-rich and sophisticated apps currently available for iOS and Android.

    +

    An overview of mobile sync apps for iOS and Android can be found below.

    +
    +

    WebDAV access can be disabled under Settings > Advanced. Since it requires write permissions and authentication, the built-in WebDAV server is automatically disabled when running in read-only or public mode.

    +
    +

    Using PhotoSync

    +

    Set PhotoPrism or WebDAV as Target

    +
      +
    1. Open PhotoSync and click
    2. +
    3. Click Configure
    4. +
    5. +

      Select PhotoPrism as target

      +

      Screenshot +Screenshot

      +
    6. +
    7. +

      Enter your settings

      +
      +

      Server: Your server url, e.g. "example.com".

      +

      Port: Your port. If you are using HTTPS the port is 443.

      +

      Login: Your username, e.g. "admin".

      +

      Password: Your admin password.

      +

      Directory: /import/ or /originals/ depending on your preferred ingestion method.

      +

      Use SSL: Should be enabled.

      +

      PikaPods users can find more information here.

      +
      +

      Screenshot

      +
    8. +
    9. +

      Click Done

      +
    10. +
    11. +

      You may adapt transfer details to match your preferences

      +

      Screenshot + Screenshot

      +
    12. +
    +

    Set Up Automatic Sync

    +
      +
    1. Open PhotoSync and click
    2. +
    3. +

      Click Autotransfer

      +

      Screenshot

      +
    4. +
    5. +

      Click Add new trigger and choose one or more trigger

      +

      Screenshot

      +
    6. +
    7. +

      Choose PhotoPrism as target

      +
    8. +
    9. +

      Click Done

      +

      Screenshot

      +
    10. +
    +

    Because PhotoSync uses WebDAV to send files, PhotoPrism automatically starts importing/indexing when it receives new files.

    +

    Sync Apps for iOS and Android

    +

    As an alternative to PhotoSync, you can also use a wide range of other apps to synchronize your pictures with PhotoPrism, either directly via WebDAV or by sharing the +originals folder as an SMB network drive through your operating system or cloud provider:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NamePlatformSynchronizationPriceDownload
    PhotoSync ProiOS, AndroidWebDAV, SMB€6.99App Store, Google Play
    PhotoSync PremiumiOS, AndroidWebDAV, SMB€24.99App Store, Google Play
    EasySyncAndroidWebDAV€2.99Google Play, F-Droid
    SyncthingAndroidSyncthingFreeGoogle Play
    FolderSync ProAndroidWebDAV, SMB€6.49Google Play
    Owlfiles ProiOS, AndroidWebDAV, SMB€59.99App Store, Google Play
    +
    +

    Note that this overview is provided for your convenience only and that we are unable to provide technical support for any of these apps. If you have problems, please contact the author or ask the community for help. You are welcome to suggest additional sync apps, so we can include them in this list.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/sync/services-sync/index.html b/user-guide/sync/services-sync/index.html new file mode 100644 index 0000000000..96f68a6832 --- /dev/null +++ b/user-guide/sync/services-sync/index.html @@ -0,0 +1,7630 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Apps and Services - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Syncing with other WebDAV-compatible Apps and Services

    +

    Under Settings > Services you can connect your PhotoPrism instance to other apps and services that can be accessed via WebDAV, e.g. other PhotoPrism instances, Nextcloud or ownCloud.

    +
    +

    When syncing, your files will be uploaded or downloaded to/from another service, which requires additional storage space. If you want PhotoPrism to index files from another local application, e.g. Nextcloud, we recommend mounting its file storage directory as originals folder instead of synchronizing the files via WebDAV. This prevents unnecessary copies of your files from being created.

    +
    +

    Automatically Upload/Download Files to/from another App

    +
      +
    1. Go to Settings
    2. +
    3. Open Services tab
    4. +
    5. Click into the sync cell of your service + Screenshot
    6. +
    7. Enable synchronization in the upper right corner
    8. +
    9. Choose a folder from your service
    10. +
    11. Choose a sync interval
    12. +
    13. Select the options that are suitable for you and click Save
    14. +
    +

    Screenshot

    +

    Remote Sync Options

    +
      +
    • Download remote files will download all files from the selected folder of the other service that do not yet exist in PhotoPrism
    • +
    • Upload local files will upload all files (including private or archived ones) from PhotoPrism to your service that do not yet exist there
    • +
    • Preserve filenames will keep filenames without renaming them
    • +
    • Sync raw and video files will upload/download raw and video files alongside with JPEGS
    • +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/sync/webdav/index.html b/user-guide/sync/webdav/index.html new file mode 100644 index 0000000000..dfa9f5368f --- /dev/null +++ b/user-guide/sync/webdav/index.html @@ -0,0 +1,7765 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Using WebDAV - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Connecting via WebDAV

    +

    WebDAV-compatible apps and clients such as PhotoSync, Microsoft's Windows Explorer, +and Apple's Finder can connect directly to PhotoPrism.

    +

    This mounts the originals and/or import folder as a network drive, allowing you to open, edit, and delete files from a remote device +as if they were local.

    +

    After files have been transferred, you can index or import them as usual. +By default, indexing and importing start automatically after a safety delay when files have been uploaded using WebDAV.

    +

    It is also possible to sync files with external WebDAV servers such as ownCloud or other PhotoPrism instances.

    +
    +

    You can disable WebDAV by navigating to Settings > Advanced and selecting the corresponding option. As WebDAV access always requires authentication, the integrated server is automatically disabled if your instance is running in public mode.

    +
    +
    +

    Do not use WebDAV without HTTPS outside your local, private network as your password would be transmitted, in clear text, over the Internet. Backup tools and file sync apps like FolderSync may refuse to connect as well.

    +
    +

    Server URL

    +

    If your instance is connected to the public Internet, the WebDAV URL of the originals folder has the following format, where example.com must be replaced with the actual hostname and admin with the actual username:

    +
    https://admin@example.com/originals/
    +
    +

    For users running a local instance on the default port 2342 without HTTPS, the URL of the originals folder is as follows (the default username for new instances is admin, unless you have changed it in the configuration):

    +
    http://admin@localhost:2342/originals/
    +
    +

    Please note that the slash at the end of the path must not be omitted and that the WebDAV URL in your client apps needs to be updated if the hostname or port of the server changes.

    +
    +

    You can view the originals folder URL by navigating to Settings > Account and then clicking Connect via WebDAV. It is possible to connect to the import folder instead by replacing /originals/ with /import/ in the URL.

    +
    +

    Microsoft Windows

    +

    On Windows, you must instead enter a resource string in the following format to configure WebDAV access, where example.com must be replaced with the actual hostname of your instance:

    +
    \\example.com@SSL\originals\
    +
    +

    If your server does not use the standard port 443 for HTTPS, Windows lets you specify a custom port such as 8443 directly after @SSL:

    +
    \\example.com@SSL@8443\originals\
    +
    +

    For local installations running on the default port 2342 without HTTPS, enter the following resource in the connection dialog (you may need to update the registry settings for this):

    +
    \\localhost:2342\originals\
    +
    +

    Please note that the slash at the end must not be omitted and that the WebDAV resource in Windows needs to be updated when the hostname or port of the server changes.

    +
    +

    You can view the originals folder resource by navigating to Settings > Account and then clicking Connect via WebDAV. It is possible to connect to the import folder instead by replacing \originals\ with \import\.

    +
    +

    Credentials

    +

    To access your instance via WebDAV, you can use your username in combination with your account password or an app password, e.g. if you have 2-Factor Authentication (2FA) enabled for your account or authenticate via OpenID Connect (OIDC) as using your account password is not possible in this case.

    +

    If access is denied even though the login credentials are correct, please check whether the account has a role with WebDAV access and WebDAV is enabled for the specific account.

    +

    Learn more ›

    +

    Connect to a WebDAV Server

    +
    +
    +
    +
      +
    1. In the Finder on your Mac, choose Go > Connect to Server
    2. +
    3. Enter the URL as shown above in the Server Address field
    4. +
    5. Click Connect
    6. +
    +

    If you cannot connect to your instance via WebDAV using these instructions:

    +
      +
    • You do not have sufficient user rights (try as admin)
    • +
    • You are experiencing a general authentication problem
    • +
    • Your instance or reverse proxy uses an invalid HTTPS certificate
    • +
    • You are trying to connect to the wrong network or server
    • +
    +
    +
    +
      +
    1. Open Windows File Explorer
    2. +
    3. Right click This PC
    4. +
    5. +

      From the dropdown, select Map network drive...

      +

      Screenshot

      +
    6. +
    7. +

      Enter the drive letter and folder you want to map your WebDAV connection to

      +
    8. +
    9. Check the boxes Reconnect at sign-in and Connect using different credentials
    10. +
    11. +

      Click the Connect to a Web site that you can use to store your documents and pictures link

      +

      Screenshot

      +
    12. +
    13. +

      Click Next

      +

      Screenshot

      +
    14. +
    15. +

      Click Choose a custom network location and then click Next

      +

      Screenshot

      +
    16. +
    17. +

      In the Internet or network address field, enter the URL as shown above and click Next

      +

      Screenshot

      +
    18. +
    19. +

      Enter your username and password and click Ok

      +

      Screenshot

      +
    20. +
    21. +

      Enter a name for the network location and click Next

      +

      Screenshot

      +
    22. +
    23. +

      Click Finish

      +

      Screenshot

      +
    24. +
    +

    The originals folder appears as a mapped drive in Windows Explorer, and you can immediately add, + edit, or delete files and directories using the Windows File Explorer.

    +

    Screenshot

    +

    If you cannot connect to your instance via WebDAV using these instructions:

    + +
    +
    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/use-cases/apple/index.html b/user-guide/use-cases/apple/index.html new file mode 100644 index 0000000000..68fd2f6bb3 --- /dev/null +++ b/user-guide/use-cases/apple/index.html @@ -0,0 +1,7622 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Apple Photos - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Migrate from Apple Photos

    +

    Transfer Files

    +
      +
    1. Select the files or albums you want to export
    2. +
    3. Click File > Export > Export Unmodified Original For Photos
    4. +
    5. Select Export IPTC as XMP
    6. +
    7. Click Export
    8. +
    9. Move the exported files/folders to your originals or import diectory and start indexing or importing
    10. +
    +

    Metadata

    +

    Apple saves the following information in its XMP files:

    +
      +
    • Title
    • +
    • Description
    • +
    • TakenAt Date
    • +
    • Keywords (include people)
    • +
    • GPS information
    • +
    +

    The following metadata is read by PhotoPrism from the exported XMP files for each photo during indexing:

    +
      +
    • Title
    • +
    • Description
    • +
    • TakenAt Date
    • +
    • Keywords
    • +
    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/use-cases/flickr/index.html b/user-guide/use-cases/flickr/index.html new file mode 100644 index 0000000000..185605bd37 --- /dev/null +++ b/user-guide/use-cases/flickr/index.html @@ -0,0 +1,7584 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Flickr - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Migrate from Flickr

    +

    Transfer Files

    +
      +
    1. Go to https://www.flickr.com/account
    2. +
    3. Request your Flickr data
    4. +
    5. Depending on the amount of photos it can take a few days for your data to be exported
    6. +
    7. Download your data and add it to your originals directory (including json files)
    8. +
    9. Start indexing
    10. +
    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/use-cases/google/index.html b/user-guide/use-cases/google/index.html new file mode 100644 index 0000000000..94fb925e6e --- /dev/null +++ b/user-guide/use-cases/google/index.html @@ -0,0 +1,7650 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Google Photos - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Migrate from Google Photos

    +

    Transfer Files

    +
      +
    1. Go to Google Takeout
    2. +
    3. Click Deselect all then check only Google Photos.
    4. +
    5. Trigger the export of your Google Photos Data.
    6. +
    7. Depending on the number of photos, it can take a few days for your data to be exported
    8. +
    9. Download your data and extract all archives into to your originals or import folder
        +
      • the folder should include the photos themselves, alongside json files for each of the photos
      • +
      +
    10. +
    11. Start indexing or importing
    12. +
    +

    Metadata

    +

    The following metadata is read by PhotoPrism from each photo's JSON file

    +
      +
    • Title
    • +
    • Description
    • +
    • Geolocation Info (lat/long)
    • +
    • Date/Time Taken
    • +
    • Date/Time Created
    • +
    • Date/Time Updated
    • +
    +

    Transfer Albums

    +
    +

    Google Photos albums won't be automatically imported yet as we're trying to find a way to deal with +auto-generated albums users may not want to import.

    +
    +

    The community has created a bash script to import albums from a Google Takeout. +For more information and support, see the project page on Github:

    +

    inthreedee/photoprism-transfer-album

    +
    +

    Help improve these docs! You can contribute by clicking to send a pull request with your changes.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/users/2fa/index.html b/user-guide/users/2fa/index.html new file mode 100644 index 0000000000..4c49a17a99 --- /dev/null +++ b/user-guide/users/2fa/index.html @@ -0,0 +1,7761 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Setting Up 2FA - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    2-Factor Authentication

    +

    Two-factor authentication (2FA) can add an extra layer of security to your account in case someone gains access to your password. If enabled, you will need a randomly generated verification code in addition to your password to log in:

    +

    Screenshot

    +

    Authenticator Apps

    +

    To enable 2FA for your account, you need a compatible authenticator app or device, for example:

    + +

    It is best if you have the authenticator app installed on your phone, as this way you can easily set it up by scanning the displayed QR code with your camera and always have it with you.

    +
    +

    Hardware Devices

    +

    While there are also dedicated hardware devices available as an alternative to authenticator apps, these are less common and we cannot give any recommendations.

    +
    +

    Setup

    +

    Step 1: Verification Code

    +

    You can enable 2FA for your account by navigating to Settings > Account and then clicking the 2-Factor Authentication button to open the setup dialog:

    +

    Screenshot

    +

    On the following page, scan the displayed QR code with your authenticator app (or use the setup key shown if you are using an app or device without camera) and then enter the generated verification code to proceed.

    +

    Step 2: Recovery Code

    +

    In the last step before 2FA is activated, you will be shown a recovery code that you can use to access your account when you cannot generate a valid verification code with your app or device:

    +

    Screenshot

    +
    +

    To avoid being locked out of your account, please download, print or copy this recovery code and keep it in a safe place. It is a one-time use code that will disable 2FA for your account when you use it.

    +
    +

    Step 3: App Passwords

    +

    If 2FA is enabled for your account, other apps and services will no longer be able to use your password as they do not have access to the verification codes.

    +

    You can therefore generate app-specific passwords for them by navigating to Settings > Account and then clicking the Apps and Devices button. We also recommend using app-specific passwords in case 2FA is not enabled for your account.

    +

    Example for generating an app password that you can use with WebDAV-compatible file synchronization apps like PhotoSync:

    +

    Screenshot

    +
    +

    By selecting the WebDAV scope, you ensure that the app password cannot be used to log in through the regular user interface or for other actions. Apps will also not be able to change your password or manage user accounts, even if you grant them Full Access.

    +
    +

    New Authenticator

    +

    To switch to a new authenticator app or device, first deactivate 2FA and then re-enable it.

    +

    Deactivating 2FA

    +

    If 2FA has been enabled for your account, you can disable it by navigating to Settings > Account, clicking the 2-Factor Authentication button and then entering your password for confirmation:

    +

    Screenshot

    +
    +

    Should you lose access to your authenticator app or device, you can use your recovery code to regain access to your account. It is a one-time use code that disables 2FA for your account when you use it.

    +

    Alternatively, if you don't remember your recovery code, you can ask an administrator to disable 2FA for you in the User Details dialog of the Admin Web UI or by running the following command in a terminal: +

    photoprism users mod --disable-2fa [username]
    +

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/users/cli/index.html b/user-guide/users/cli/index.html new file mode 100644 index 0000000000..065dbad2fa --- /dev/null +++ b/user-guide/users/cli/index.html @@ -0,0 +1,8065 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CLI Commands - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    + +
    + + + + + + + + + + + + + +

    User Management Commands

    +

    Changing a Password

    +

    Running the following in a terminal changes the password of an existing user without affecting other account settings, e.g. if you cannot remember the currently set password or if there was a problem configuring the initial admin account (replace [username] with the username of the account you want to update):

    +
    photoprism passwd [username]
    +
    +

    Note that when you use Docker Compose and do not already have a terminal session open, you must prepend docker compose exec photoprism so that the command is executed within the photoprism container, for example:

    +
    docker compose exec photoprism photoprism passwd admin
    +
    +

    This also applies to other terminal commands, including those listed below.

    +
    +

    The examples in our documentation use the new docker compose command by default. If your server does not yet support it, you can still use docker-compose or alternatively podman-compose on Red Hat-compatible distributions.

    +
    +

    Removing a Password

    +

    Changing the authentication of an existing account to a password-less provider like OIDC will not remove a previously set password, so it can still be used to log in (optionally also with 2FA).

    +

    If a local password has been set for such an account that should no longer be used, you can remove it by running the following command in a terminal:

    +
    photoprism passwd --rm [username]
    +
    +

    Managing User Accounts

    +

    As an alternative to the web user interface, you can run the following commands in a terminal to perform tasks such as adding, viewing, editing and deleting user accounts:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CLI CommandDescription
    photoprism users ls [search]Searches existing user accounts
    photoprism users legacy [search]Searches legacy user accounts
    photoprism users add [options] [username]Adds a new user account
    photoprism users show [username]Displays user account information
    photoprism users mod [options] [username]Modifies an existing user account
    photoprism users rm [username]Removes a user account
    photoprism users reset --yesRemoves all accounts and resets the database
    +
    +

    Users who experience login problems after upgrading from development builds, or old releases prior to November 2022, can run the photoprism users reset --yes command to recreate the session and user management database tables so they are compatible with the current version. Note that any client access tokens and app passwords that users may have created will also be deleted and must be recreated.

    +
    +

    Command Options

    +

    The users add and users mod commands support these flags to set or change account properties:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Command FlagDescription
    --name NAME, -n NAMEfull NAME for display in the interface
    --email EMAIL, -m EMAILunique EMAIL address of the user
    --password PASSWORD, -p PASSWORDPASSWORD for local authentication (8-72 characters)
    --role value, -r valueuser account ROLE (admin, user, viewer or guest) (default: "admin")
    --auth PROVIDER, -A PROVIDERauthentication PROVIDER (default, local, oidc or none)
    --auth-id IDauthentication ID e.g. Subject ID or Distinguished Name (DN)
    --superadmin, -smake user super admin with full access
    --no-login, -ldisable login on the web interface
    --webdav, -wallow to sync files via WebDAV
    --upload-path value, -u valueupload files to this sub-folder
    --disable-2fadeactivate two-factor authentication
    +

    Creating a New Account

    +

    The photoprism users add command creates a new user account or offers to restore a previously deleted account with the same username if it exists. For example, you can run the following to add a new admin account with the username "bob" and the password "mysecret":

    +
    docker compose exec photoprism photoprism users add -p mysecret -n "Bob" bob
    +
    +

    If you do not specify an initial password with the -p flag, you will be prompted to enter a password for the new account. Further account properties can be set with the flags listed above.

    +
    +

    Some user account roles such as User and Viewer are currently only available with a membership to support development and maintenance.

    +
    +

    Viewing Account Details

    +

    To view the account properties of a specific user, use the show subcommand:

    +
    docker compose exec photoprism photoprism users show bob
    +
    +

    Searching User Accounts

    +

    To list all existing accounts, you can run the following:

    +
    docker compose exec photoprism photoprism users ls
    +
    +

    With the photoprism users ls command, you can also find specific accounts based on a search term you provide:

    +
    docker compose exec photoprism photoprism users ls bob
    +
    +

    To display a description and the available options for a command, use the --help flag:

    +
    docker compose exec photoprism photoprism users ls --help
    +
    +

    Viewing Login Attempts

    +

    For security reasons, the authentication logs are not accessible from the web user interface. They can only be viewed in the application service logs or by running the following command in a terminal:

    +
    docker compose exec photoprism photoprism audit logins [username]
    +
    +

    Command Options

    +

    You can combine it with these flags to change the output format and the maximum number of search results:

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Command FlagDescription
    --md, -mformat as machine-readable Markdown
    --csv, -cexport as semicolon separated values
    --tsv, -texport as tab separated values
    -n LIMITLIMIT number of results (default: 100)
    +

    Example Report

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Client IPUsernameRealmStatusLast LoginFailed At
    172.19.0.1userapiOK2023-02-03 07:17:46
    172.19.0.1viewerapiOK2023-02-03 07:16:55
    172.19.0.1adminapiOK2023-02-03 06:55:06
    +
    +

    Run photoprism audit reset --yes to clear all audit logs and reset the database table to a clean state.

    +
    +

    Session Management

    +

    You can use the following terminal commands to generate, inspect and, if necessary, delete access tokens for the authentication of browsers and other clients (including app passwords):

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CLI CommandDescription
    photoprism auth ls [search]Lists currently authenticated users and clients
    photoprism auth add [username]Adds a new authentication secret for client applications
    photoprism auth show [identifier]Shows detailed information about a session
    photoprism auth rm [identifier]Deletes a session by id or access token
    photoprism auth reset --yesResets the authentication of all users and clients
    +

    In order to grant limited API access to other applications and services, the photoprism clients add command lets you generate OAuth2 client credentials for them, or you can use the photoprism auth add command to generate access tokens with a limited scope and lifetime.

    +

    Learn more ›

    +
    +

    Should you experience login problems, for example after upgrading from a previous release or development preview, we recommend running the photoprism auth reset --yes command in a terminal to reset the auth_sessions table to a clean state and force a re-login of all users. Note that any client access tokens and app passwords that users may have created will also be deleted and must be recreated.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/users/client-credentials/index.html b/user-guide/users/client-credentials/index.html new file mode 100644 index 0000000000..ef0dda559c --- /dev/null +++ b/user-guide/users/client-credentials/index.html @@ -0,0 +1,7850 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Client Credentials - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Authenticating External Apps and Services

    +

    In order to grant limited access to other applications and services, administrators can use the photoprism auth and photoprism clients subcommands to generate authentication tokens for them. While app passwords are bound to user accounts and can be generated by anyone from the UI, OAuth2 access tokens and client credentials can be used to access the REST API without being bound to an account.

    +

    App Passwords

    +

    All users can generate app-specific passwords for their own use from the web interface by navigating to Settings > Account and then clicking the Apps and Devices button.

    +

    Alternatively, running the following command in a terminal will generate a new app-specific password e.g. for the admin account, so that WebDAV-compatible clients can synchronize files even if 2FA is enabled for the account or the account password is changed:

    +
    docker compose exec photoprism photoprism auth add -n Sync -s "webdav" admin
    +
    +

    You will then be shown the generated app password so you can copy it and keep it in a safe place or enter it directly into an app, as you will not be able to see it again:

    +
    |-----------------------------|---------------------|
    +| App Password                | Authorization Scope |
    +|-----------------------------|---------------------|
    +| HY8fxO-8hvNqB-43UV4q-1AZ0vu | webdav              |
    +|-----------------------------|---------------------|
    +
    +

    For added security, we recommend setting an expiration date for the app passwords and access tokens you generate. Common scopes for use with app passwords are either "*" for Full Access or "webdav" for WebDAV-compatible file synchronization apps.

    +
    +

    Besides using app passwords to create sessions through the POST /api/v1/session endpoint, developers can also use them as access tokens in the Bearer Authorization header without first creating a session access token.

    +
    +

    Command Options

    +

    The following flags can be used with the photoprism auth add command (if you omit name or scope, you will be asked to enter them interactively):

    + + + + + + + + + + + + + + + + + + + + + +
    Command FlagDescription
    --name CLIENT, -n CLIENTCLIENT name to help identify the application
    --scope SCOPES, -s SCOPESauthorization SCOPES e.g. "metrics" or "photos albums" ("*" to allow all)
    --expires LIFETIME, -e LIFETIMEauthentication LIFETIME in seconds, after which access expires (-1 to disable the limit) (default: 31536000)
    +

    Authorization Scopes

    +

    One or more of these scopes can be specified to limit the access to certain API endpoints:

    +

    files, folders, shares, photos, videos, favorites, albums, moments, calendar, people, places, labels, config, settings, services, users, sessions, logs, webdav, metrics

    +
    +

    Clients authenticated with app passwords are unable to change the account password or manage user accounts, even if you specify all scopes or use the wildcard "*" to allow all.

    +
    +

    Access Tokens

    +

    If you do not specify a username as argument for the photoprism auth add command, a client access token will be generated (the same flags and scopes as above can be used to limit token authorization and lifetime):

    +
    |--------------------------------------------------|---------------------|
    +| Access Token                                     | Authorization Scope |
    +|--------------------------------------------------|---------------------|
    +| 7dbfa37b5a3db2a9e9dd186479018bfe2e3ce5a71fc2f955 | files folders       |
    +|--------------------------------------------------|---------------------|
    +
    +

    Generating access tokens is a good choice for developers and other advanced users to connect scripts and external services to the PhotoPrism API, e.g. services that collect metrics or start indexing at regular intervals.

    +

    Please note, however, that client access tokens cannot be used to synchronize files via WebDAV, even if the token authorization scope is set to "webdav" or "*", as this requires a registered user account. Access tokens can also not be used as a direct password replacement for apps, since clients are not allowed to use the POST /api/v1/session endpoint, which is required for logging in through the user interface.

    +

    Client Credentials

    +

    If clients support authentication via OAuth2 client credentials, you can use the following terminal commands to generate a client_id and client_secret for them, list registered clients, and delete client credentials that are no longer used:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CLI CommandDescription
    photoprism clients ls [search]Lists registered client applications
    photoprism clients add [username]Registers a new client application
    photoprism clients show [identifier]Shows client configuration details
    photoprism clients mod [identifier]Updates client application settings
    photoprism clients rm [identifier]Deletes the specified client application
    photoprism clients reset --yesRemoves all registered client applications
    +

    For example, running the following in a terminal will generate credentials for Prometheus, with access limited to the metrics endpoint:

    +
    docker compose exec photoprism photoprism clients add -n Prometheus -s metrics
    +
    +

    You will then be shown the generated client_id and client_secret so you can copy them and keep them in a safe place:

    +
    |------------------|----------------------------------|
    +| Client ID        | Client Secret                    |
    +|------------------|----------------------------------|
    +| csce0w2joodmirvi | 5VKkBeZLDvojjpE9XzCMXShnrxmxHWvN |
    +|------------------|----------------------------------|
    +
    +
    +

    OAuth2 client credentials cannot be directly used for synchronizing files via WebDAV, as a password replacement for apps, or for logging in to the web interface.

    +
    +

    Command Options

    +

    The following parameters can be used with the photoprism clients add command, e.g. to limit the number of access tokens the client can request:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Command FlagDescription
    --name CLIENT, -n CLIENTCLIENT name to help identify the application
    --role ROLE, -r ROLEclient authorization ROLE (default: "client")
    --scope SCOPES, -s SCOPESclient authorization SCOPES e.g. "metrics" or "photos albums" ("*" to allow all)
    --expires LIFETIME, -e LIFETIMEaccess token LIFETIME in seconds, after which a new token must be requested (default: 86400)
    --tokens NUMBER, -t NUMBERmaximum NUMBER of access tokens that the client can request (-1 to disable the limit) (default: 10)
    +

    If you omit the name or scope parameter, you will be asked to enter them interactively. One or more of these scopes can be specified to limit the access to certain API endpoints:

    +

    files, folders, shares, photos, videos, favorites, albums, moments, calendar, people, places, labels, config, settings, services, users, sessions, logs, webdav, metrics

    +
    +

    When requesting access tokens, clients can further restrict the scope of the tokens by passing the scope parameter to the POST /api/v1/oauth/token endpoint.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/users/img/2fa-enabled.jpg b/user-guide/users/img/2fa-enabled.jpg new file mode 100644 index 0000000000..80ec922d18 Binary files /dev/null and b/user-guide/users/img/2fa-enabled.jpg differ diff --git a/user-guide/users/img/app-password.jpg b/user-guide/users/img/app-password.jpg new file mode 100644 index 0000000000..fcdbeb34f9 Binary files /dev/null and b/user-guide/users/img/app-password.jpg differ diff --git a/user-guide/users/img/disable-2fa.jpg b/user-guide/users/img/disable-2fa.jpg new file mode 100644 index 0000000000..3735e41d89 Binary files /dev/null and b/user-guide/users/img/disable-2fa.jpg differ diff --git a/user-guide/users/img/enable-2fa.jpg b/user-guide/users/img/enable-2fa.jpg new file mode 100644 index 0000000000..0b5417f8df Binary files /dev/null and b/user-guide/users/img/enable-2fa.jpg differ diff --git a/user-guide/users/img/login-with-2fa.jpg b/user-guide/users/img/login-with-2fa.jpg new file mode 100644 index 0000000000..543bf87286 Binary files /dev/null and b/user-guide/users/img/login-with-2fa.jpg differ diff --git a/user-guide/users/img/recovery-code.jpg b/user-guide/users/img/recovery-code.jpg new file mode 100644 index 0000000000..ead6767606 Binary files /dev/null and b/user-guide/users/img/recovery-code.jpg differ diff --git a/user-guide/users/img/users-add.jpg b/user-guide/users/img/users-add.jpg new file mode 100644 index 0000000000..e6cc620422 Binary files /dev/null and b/user-guide/users/img/users-add.jpg differ diff --git a/user-guide/users/img/users-change-pw.jpg b/user-guide/users/img/users-change-pw.jpg new file mode 100644 index 0000000000..a1b7ba15cc Binary files /dev/null and b/user-guide/users/img/users-change-pw.jpg differ diff --git a/user-guide/users/img/users-delete.jpg b/user-guide/users/img/users-delete.jpg new file mode 100644 index 0000000000..111409be7d Binary files /dev/null and b/user-guide/users/img/users-delete.jpg differ diff --git a/user-guide/users/img/users-edit.jpg b/user-guide/users/img/users-edit.jpg new file mode 100644 index 0000000000..1cbca8cfe1 Binary files /dev/null and b/user-guide/users/img/users-edit.jpg differ diff --git a/user-guide/users/img/users.jpg b/user-guide/users/img/users.jpg new file mode 100644 index 0000000000..2e972800db Binary files /dev/null and b/user-guide/users/img/users.jpg differ diff --git a/user-guide/users/index.html b/user-guide/users/index.html new file mode 100644 index 0000000000..a14b7c47e1 --- /dev/null +++ b/user-guide/users/index.html @@ -0,0 +1,7654 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Admin Web UI - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Managing User Accounts

    +
    +

    PhotoPrism® Plus includes a web user interface for account and session management, in addition to the command-line interface available in all editions.

    +
    +

    You can add, edit and delete user accounts by navigating to Settings > Users as an Admin:

    +

    Screenshot

    +

    Adding a New User

    +

    Screenshot

    +

    Editing User Details

    +

    Screenshot

    +

    Only super admins can change the authentication provider of an account through the web interface, except for their own account, so that they do not accidentally lock themselves out e.g. by setting it to "none".

    +

    Changing Passwords

    +

    Super admins can reset a user's password, while regular admins can change passwords only if they know the current password.

    +

    Screenshot

    +

    Deleting a User

    +

    Screenshot

    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/users/libraries/index.html b/user-guide/users/libraries/index.html new file mode 100644 index 0000000000..4293a1dbd9 --- /dev/null +++ b/user-guide/users/libraries/index.html @@ -0,0 +1,7513 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Multiple Libraries - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Multiple Libraries

    +

    PhotoPrism® Plus includes advanced multi-user functionality and additional account roles. These roles are intended for situations where you want other people to have access to your library, such as giving family members access to your pictures without granting write permissions or exposing private content.

    +

    It is recommended to set up additional instances if you have multiple users in a family, so that everyone can manage their own files independently. This way you can avoid problems with conflicting library settings, file permissions, and dealing with duplicates.

    +

    In future versions, users will be able to share albums and other content in a decentralized way, regardless of where their library is hosted. We are also working on a dedicated web interface for managing multiple libraries and user accounts, which will be made available as a separate tool.

    +
    +

    Our ultimate goal is to make personal sharing compatible with other apps like Pixelfed and Mastodon.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/users/roles/index.html b/user-guide/users/roles/index.html new file mode 100644 index 0000000000..1299909abc --- /dev/null +++ b/user-guide/users/roles/index.html @@ -0,0 +1,7730 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Account Roles - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Roles and Permissions

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    RoleSearch LibraryView & DownloadUploadWebDAVManage
    adminoptional
    useroptional
    viewerexcept privateexcept private
    guestshared
    visitorshared
    +

    Admin

    +

    Admins have unrestricted access to all pictures, albums, and settings.

    +

    Regular Admins can lose their privileges due to an intentional or accidental role change. However, accounts with the optional "superadmin" status (can be set with the -s flag) retain their admin privileges even if they are assigned a non-admin or invalid role. This is to prevent them from locking themselves out.

    +

    When Super Admins change settings such as the language or theme, these automatically become the default settings for other users, unless they have explicitly made a different choice. In addition, global feature flags can only be changed by Super Admins.

    +

    User

    +

    Users have full access to the library and can view, edit, and delete all pictures and albums. Unlike Admins, Users cannot view or change the Library and Advanced Settings, only personal preferences such as theme, language, and password. In addition, their WebDAV access can be disabled. Future releases may include more ways to customize user privileges, e.g. with individual account attributes.

    +

    Viewer

    +

    Viewers are similar to regular Users, except that they do not have write access to the library and cannot see content that has been archived or marked private. They also cannot upload/import files or trigger indexing. Like all registered users, Viewers can change and save personal preferences such as theme, language, and password.

    +

    Guest

    +

    Guests have read-only access to view and download the resources that other users have shared with them. They can also change personal settings such as theme, language, and password.

    +

    Visitor

    +

    Visitors cannot be added manually. This special role is tied to a system account that represents anonymous users who use links to view albums or other content that has been shared with them. Visitors can only access these resources and cannot log in with a username or password. Other than guests, they also cannot retain their personal settings for longer than their browsing session lasts.

    +
    +

    Some user account roles such as User and Viewer are currently only available with a membership to support development and maintenance.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/user-guide/users/sharing/index.html b/user-guide/users/sharing/index.html new file mode 100644 index 0000000000..3eb966d34d --- /dev/null +++ b/user-guide/users/sharing/index.html @@ -0,0 +1,7512 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sharing with Guests - PhotoPrism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + + + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    + + + + + + + + + + + + + +

    Sharing with Guests

    +

    To share albums with guests, admins and users can create share links, just like when sharing with friends who don't have an account.

    +

    When users with limited privileges open a share link while being logged in, they get permanent read-only access to the shared resources until the link is removed or expires.

    +
    +

    In a future release, you will be able to share content with local users directly from the web interface without having to create links first.

    +
    + + + + + + + + + + + + + +
    +
    + + + +
    + + + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file