diff --git a/src/autoconf/Config.py b/src/autoconf/Config.py
index 630216137..2725c563b 100644
--- a/src/autoconf/Config.py
+++ b/src/autoconf/Config.py
@@ -77,20 +77,25 @@ class Config(ConfigCaller):
)
sleep(5)
+ # update instances in database
+ err = self._db.update_instances(self.__instances)
+ if err:
+ self.__logger.error(f"Failed to update instances: {err}")
+
# save config to database
- ret = self._db.save_config(self.__config, "autoconf")
- if ret:
+ err = self._db.save_config(self.__config, "autoconf")
+ if err:
success = False
self.__logger.error(
- f"Can't save autoconf config in database: {ret}",
+ f"Can't save autoconf config in database: {err}",
)
# save custom configs to database
- ret = self._db.save_custom_configs(custom_configs, "autoconf")
- if ret:
+ err = self._db.save_custom_configs(custom_configs, "autoconf")
+ if err:
success = False
self.__logger.error(
- f"Can't save autoconf custom configs in database: {ret}",
+ f"Can't save autoconf custom configs in database: {err}",
)
return success
diff --git a/src/autoconf/Dockerfile b/src/autoconf/Dockerfile
index d7f9464d7..b9c44dd1f 100644
--- a/src/autoconf/Dockerfile
+++ b/src/autoconf/Dockerfile
@@ -9,7 +9,7 @@ RUN mkdir -p /usr/share/bunkerweb/deps && \
rm -rf /tmp/req
# Install dependencies
-RUN apk add --no-cache --virtual .build-deps g++ gcc libffi-dev && \
+RUN apk add --no-cache --virtual .build-deps g++ gcc musl-dev jpeg-dev zlib-dev libffi-dev cairo-dev pango-dev gdk-pixbuf-dev && \
pip install --no-cache-dir --upgrade pip && \
pip install wheel && \
mkdir -p /usr/share/bunkerweb/deps/python && \
diff --git a/src/bw/lua/api.lua b/src/bw/lua/api.lua
index 68bce8d72..5a25c0faa 100644
--- a/src/bw/lua/api.lua
+++ b/src/bw/lua/api.lua
@@ -19,7 +19,7 @@ api.global.GET["^/ping$"] = function(api)
end
api.global.POST["^/reload$"] = function(api)
- local status = os.execute("/usr/sbin/nginx -s reload")
+ local status = os.execute("nginx -s reload")
if status == 0 then
return api:response(ngx.HTTP_OK, "success", "reload successful")
end
@@ -27,7 +27,7 @@ api.global.POST["^/reload$"] = function(api)
end
api.global.POST["^/stop$"] = function(api)
- local status = os.execute("/usr/sbin/nginx -s quit")
+ local status = os.execute("nginx -s quit")
if status == 0 then
return api:response(ngx.HTTP_OK, "success", "stop successful")
end
diff --git a/src/common/core/jobs/jobs/download-plugins.py b/src/common/core/jobs/jobs/download-plugins.py
index 989db6b2a..f2e65ed10 100644
--- a/src/common/core/jobs/jobs/download-plugins.py
+++ b/src/common/core/jobs/jobs/download-plugins.py
@@ -108,10 +108,10 @@ try:
external_plugins.append(plugin_file)
if external_plugins:
- ret = db.update_external_plugins(external_plugins)
- if ret:
+ err = db.update_external_plugins(external_plugins)
+ if err:
logger.error(
- f"Couldn't update external plugins to database: {ret}",
+ f"Couldn't update external plugins to database: {err}",
)
except:
diff --git a/src/common/core/letsencrypt/jobs/certbot-auth.py b/src/common/core/letsencrypt/jobs/certbot-auth.py
index 7bc55d4e1..a1245066c 100755
--- a/src/common/core/letsencrypt/jobs/certbot-auth.py
+++ b/src/common/core/letsencrypt/jobs/certbot-auth.py
@@ -1,32 +1,46 @@
#!/usr/bin/python3
-import sys, os, traceback
+from os import getenv, makedirs
+from os.path import exists
+from sys import exit as sys_exit, path as sys_path
+from traceback import format_exc
-sys.path.append("/usr/share/bunkerweb/deps/python")
-sys.path.append("/usr/share/bunkerweb/utils")
-sys.path.append("/usr/share/bunkerweb/api")
+sys_path.append("/usr/share/bunkerweb/deps/python")
+sys_path.append("/usr/share/bunkerweb/utils")
+sys_path.append("/usr/share/bunkerweb/api")
+sys_path.append("/usr/share/bunkerweb/db")
+from Database import Database
from logger import setup_logger
from API import API
-logger = setup_logger("Lets-encrypt", os.getenv("LOG_LEVEL", "INFO"))
+logger = setup_logger("Lets-encrypt", getenv("LOG_LEVEL", "INFO"))
+db = Database(
+ logger,
+ sqlalchemy_string=getenv("DATABASE_URI", None),
+)
status = 0
try:
# Get env vars
- is_kubernetes_mode = os.getenv("KUBERNETES_MODE") == "yes"
- is_swarm_mode = os.getenv("SWARM_MODE") == "yes"
- is_autoconf_mode = os.getenv("AUTOCONF_MODE") == "yes"
- token = os.getenv("CERTBOT_TOKEN")
- validation = os.getenv("CERTBOT_VALIDATION")
+ bw_integration = None
+ if getenv("KUBERNETES_MODE") == "yes":
+ bw_integration = "Swarm"
+ elif getenv("SWARM_MODE") == "yes":
+ bw_integration = "Kubernetes"
+ elif getenv("AUTOCONF_MODE") == "yes":
+ bw_integration = "Autoconf"
+ elif exists("/usr/share/bunkerweb/INTEGRATION"):
+ with open("/usr/share/bunkerweb/INTEGRATION", "r") as f:
+ bw_integration = f.read().strip()
+ token = getenv("CERTBOT_TOKEN")
+ validation = getenv("CERTBOT_VALIDATION")
# Cluster case
- if is_kubernetes_mode or is_swarm_mode or is_autoconf_mode:
- for variable, value in os.environ.items():
- if not variable.startswith("CLUSTER_INSTANCE_"):
- continue
- endpoint = value.split(" ")[0]
- host = value.split(" ")[1]
+ if bw_integration in ("Docker", "Swarm", "Kubernetes", "Autoconf"):
+ for instance in db.get_instances():
+ endpoint = f"http://{instance['hostname']}:{instance['port']}"
+ host = instance["server_name"]
api = API(endpoint, host=host)
sent, err, status, resp = api.request(
"POST",
@@ -49,15 +63,14 @@ try:
f"Successfully sent API request to {api.get_endpoint()}/lets-encrypt/challenge",
)
- # Docker or Linux case
+ # Linux case
else:
root_dir = "/var/tmp/bunkerweb/lets-encrypt/.well-known/acme-challenge/"
- os.makedirs(root_dir, exist_ok=True)
- with open(root_dir + token, "w") as f:
+ makedirs(root_dir, exist_ok=True)
+ with open(f"{root_dir}{token}", "w") as f:
f.write(validation)
except:
status = 1
- logger.error("Exception while running certbot-auth.py :")
- print(traceback.format_exc())
+ logger.error(f"Exception while running certbot-auth.py :\n{format_exc()}")
-sys.exit(status)
+sys_exit(status)
diff --git a/src/common/core/letsencrypt/jobs/certbot-cleanup.py b/src/common/core/letsencrypt/jobs/certbot-cleanup.py
index f51c2b768..cb6d886ad 100755
--- a/src/common/core/letsencrypt/jobs/certbot-cleanup.py
+++ b/src/common/core/letsencrypt/jobs/certbot-cleanup.py
@@ -1,31 +1,45 @@
#!/usr/bin/python3
-import sys, os, traceback
+from os import getenv, remove
+from os.path import exists, isfile
+from sys import exit as sys_exit, path as sys_path
+from traceback import format_exc
-sys.path.append("/usr/share/bunkerweb/deps/python")
-sys.path.append("/usr/share/bunkerweb/utils")
-sys.path.append("/usr/share/bunkerweb/api")
+sys_path.append("/usr/share/bunkerweb/deps/python")
+sys_path.append("/usr/share/bunkerweb/utils")
+sys_path.append("/usr/share/bunkerweb/api")
+sys_path.append("/usr/share/bunkerweb/db")
+from Database import Database
from logger import setup_logger
from API import API
-logger = setup_logger("Lets-encrypt", os.getenv("LOG_LEVEL", "INFO"))
+logger = setup_logger("Lets-encrypt", getenv("LOG_LEVEL", "INFO"))
+db = Database(
+ logger,
+ sqlalchemy_string=getenv("DATABASE_URI", None),
+)
status = 0
try:
# Get env vars
- is_kubernetes_mode = os.getenv("KUBERNETES_MODE") == "yes"
- is_swarm_mode = os.getenv("SWARM_MODE") == "yes"
- is_autoconf_mode = os.getenv("AUTOCONF_MODE") == "yes"
- token = os.getenv("CERTBOT_TOKEN")
+ bw_integration = None
+ if getenv("KUBERNETES_MODE") == "yes":
+ bw_integration = "Swarm"
+ elif getenv("SWARM_MODE") == "yes":
+ bw_integration = "Kubernetes"
+ elif getenv("AUTOCONF_MODE") == "yes":
+ bw_integration = "Autoconf"
+ elif exists("/usr/share/bunkerweb/INTEGRATION"):
+ with open("/usr/share/bunkerweb/INTEGRATION", "r") as f:
+ bw_integration = f.read().strip()
+ token = getenv("CERTBOT_TOKEN")
# Cluster case
- if is_kubernetes_mode or is_swarm_mode or is_autoconf_mode:
- for variable, value in os.environ.items():
- if not variable.startswith("CLUSTER_INSTANCE_"):
- continue
- endpoint = value.split(" ")[0]
- host = value.split(" ")[1]
+ if bw_integration in ("Docker", "Swarm", "Kubernetes", "Autoconf"):
+ for instance in db.get_instances():
+ endpoint = f"http://{instance['hostname']}:{instance['port']}"
+ host = instance["server_name"]
api = API(endpoint, host=host)
sent, err, status, resp = api.request(
"DELETE", "/lets-encrypt/challenge", data={"token": token}
@@ -45,17 +59,15 @@ try:
logger.info(
f"Successfully sent API request to {api.get_endpoint()}/lets-encrypt/challenge",
)
-
- # Docker or Linux case
+ # Linux case
else:
challenge_path = (
f"/var/tmp/bunkerweb/lets-encrypt/.well-known/acme-challenge/{token}"
)
- if os.path.isfile(challenge_path):
- os.remove(challenge_path)
+ if isfile(challenge_path):
+ remove(challenge_path)
except:
status = 1
- logger.error("Exception while running certbot-cleanup.py :")
- print(traceback.format_exc())
+ logger.error(f"Exception while running certbot-cleanup.py :\n{format_exc()}")
-sys.exit(status)
+sys_exit(status)
diff --git a/src/common/core/letsencrypt/jobs/certbot-deploy.py b/src/common/core/letsencrypt/jobs/certbot-deploy.py
index 3d2b6305f..4e21428b4 100755
--- a/src/common/core/letsencrypt/jobs/certbot-deploy.py
+++ b/src/common/core/letsencrypt/jobs/certbot-deploy.py
@@ -1,7 +1,7 @@
#!/usr/bin/python3
from io import BytesIO
-from os import environ, getenv
+from os import getenv
from os.path import exists
from subprocess import run, DEVNULL, STDOUT
from sys import exit as sys_exit, path as sys_path
@@ -11,13 +11,17 @@ from traceback import format_exc
sys_path.append("/usr/share/bunkerweb/deps/python")
sys_path.append("/usr/share/bunkerweb/utils")
sys_path.append("/usr/share/bunkerweb/api")
+sys_path.append("/usr/share/bunkerweb/db")
-from docker import DockerClient
-
+from Database import Database
from logger import setup_logger
from API import API
logger = setup_logger("Lets-encrypt", getenv("LOG_LEVEL", "INFO"))
+db = Database(
+ logger,
+ sqlalchemy_string=getenv("DATABASE_URI", None),
+)
status = 0
try:
@@ -33,11 +37,11 @@ try:
with open("/usr/share/bunkerweb/INTEGRATION", "r") as f:
bw_integration = f.read().strip()
token = getenv("CERTBOT_TOKEN")
-
+
logger.info(f"Certificates renewal for {getenv('RENEWED_DOMAINS')} successful")
# Cluster case
- if bw_integration in ("Swarm", "Kubernetes", "Autoconf"):
+ if bw_integration in ("Docker", "Swarm", "Kubernetes", "Autoconf"):
# Create tarball of /data/cache/letsencrypt
tgz = BytesIO()
with tar_open(mode="w:gz", fileobj=tgz) as tf:
@@ -45,11 +49,9 @@ try:
tgz.seek(0, 0)
files = {"archive.tar.gz": tgz}
- for variable, value in environ.items():
- if not variable.startswith("CLUSTER_INSTANCE_"):
- continue
- endpoint = value.split(" ")[0]
- host = value.split(" ")[1]
+ for instance in db.get_instances():
+ endpoint = f"http://{instance['hostname']}:{instance['port']}"
+ host = instance["server_name"]
api = API(endpoint, host=host)
sent, err, status, resp = api.request(
"POST", "/lets-encrypt/certificates", files=files
@@ -85,53 +87,13 @@ try:
logger.info(
f"Successfully sent API request to {api.get_endpoint()}/reload"
)
-
- # Docker or Linux case
- elif bw_integration == "Docker":
- docker_client = DockerClient(
- base_url=getenv("DOCKER_HOST", "unix:///var/run/docker.sock")
+ # Linux case
+ else:
+ proc = run(
+ ["nginx", "-s", "reload"],
+ stdin=DEVNULL,
+ stderr=STDOUT,
)
-
- apis = []
- for instance in docker_client.containers.list(
- filters={"label": "bunkerweb.INSTANCE"}
- ):
- api = None
-
- for var in instance.attrs["Config"]["Env"]:
- if var.startswith("API_HTTP_PORT="):
- api = API(
- f"http://{instance.name}:{var.replace('API_HTTP_PORT=', '', 1)}"
- )
- break
-
- if api:
- apis.append(api)
- else:
- apis.append(
- API(f"http://{instance.name}:{getenv('API_HTTP_PORT', '5000')}")
- )
-
- for api in apis:
- sent, err, status, resp = api.request("POST", "/reload")
- if not sent:
- status = 1
- logger.error(
- f"Can't send API request to {api.get_endpoint()}/reload : {err}"
- )
- else:
- if status != 200:
- status = 1
- logger.error(
- f"Error while sending API request to {api.get_endpoint()}/reload : status = {resp['status']}, msg = {resp['msg']}"
- )
- else:
- logger.info(
- f"Successfully sent API request to {api.get_endpoint()}/reload"
- )
- elif bw_integration == "Linux":
- cmd = "/usr/sbin/nginx -s reload"
- proc = run(cmd.split(" "), stdin=DEVNULL, stderr=STDOUT)
if proc.returncode != 0:
status = 1
logger.error("Error while reloading nginx")
diff --git a/src/common/core/letsencrypt/jobs/certbot-new.py b/src/common/core/letsencrypt/jobs/certbot-new.py
index 35b638798..26202490c 100755
--- a/src/common/core/letsencrypt/jobs/certbot-new.py
+++ b/src/common/core/letsencrypt/jobs/certbot-new.py
@@ -1,6 +1,6 @@
#!/usr/bin/python3
-from os import environ, getenv
+from os import environ, getcwd, getenv
from os.path import exists
from subprocess import DEVNULL, STDOUT, run
from sys import exit as sys_exit, path as sys_path
@@ -8,17 +8,42 @@ from traceback import format_exc
sys_path.append("/usr/share/bunkerweb/deps/python")
sys_path.append("/usr/share/bunkerweb/utils")
+sys_path.append("/usr/share/bunkerweb/db")
+from Database import Database
from logger import setup_logger
+logger = setup_logger("LETS-ENCRYPT", getenv("LOG_LEVEL", "INFO"))
+db = Database(
+ logger,
+ sqlalchemy_string=getenv("DATABASE_URI", None),
+)
+status = 0
+
def certbot_new(domains, email):
- cmd = f"/usr/share/bunkerweb/deps/python/bin/certbot certonly --manual --preferred-challenges=http --manual-auth-hook /usr/share/bunkerweb/core/letsencrypt/jobs/certbot-auth.py --manual-cleanup-hook /usr/share/bunkerweb/core/letsencrypt/jobs/certbot-cleanup.py -n -d {domains} --email {email} --agree-tos"
- if getenv("USE_LETS_ENCRYPT_STAGING") == "yes":
- cmd += " --staging"
environ["PYTHONPATH"] = "/usr/share/bunkerweb/deps/python"
proc = run(
- cmd.split(" "),
+ [
+ "/usr/share/bunkerweb/deps/python/bin/certbot certonly",
+ "--manual",
+ "--preferred-challenges=http",
+ "--manual-auth-hook",
+ f"{getcwd()}/certbot-auth.py",
+ "--manual-cleanup-hook",
+ f"{getcwd()}/certbot-cleanup.py",
+ "-n",
+ "-d",
+ domains,
+ "--email",
+ email,
+ "--agree-tos",
+ "--logs-dir",
+ "/var/tmp/bunkerweb",
+ "--work-dir",
+ "/var/lib/bunkerweb",
+ ]
+ + (["--staging"] if getenv("USE_LETS_ENCRYPT_STAGING", "no") == "yes" else []),
stdin=DEVNULL,
stderr=STDOUT,
env=environ,
@@ -26,34 +51,37 @@ def certbot_new(domains, email):
return proc.returncode
-logger = setup_logger("LETS-ENCRYPT", getenv("LOG_LEVEL", "INFO"))
-status = 0
-
try:
-
# Multisite case
- if getenv("MULTISITE") == "yes":
- for first_server in getenv("SERVER_NAME").split(" "):
+ if getenv("MULTISITE", "no") == "yes":
+ for first_server in getenv("SERVER_NAME", "").split(" "):
if (
- getenv(f"{first_server}_AUTO_LETS_ENCRYPT", getenv("AUTO_LETS_ENCRYPT"))
+ not first_server
+ or getenv(
+ f"{first_server}_AUTO_LETS_ENCRYPT",
+ getenv("AUTO_LETS_ENCRYPT", "no"),
+ )
!= "yes"
):
continue
- if first_server == "":
- continue
- real_server_name = getenv(f"{first_server}_SERVER_NAME", first_server)
- domains = real_server_name.replace(" ", ",")
+
+ domains = getenv(f"{first_server}_SERVER_NAME", first_server).replace(
+ " ", ","
+ )
+
if exists(f"/etc/letsencrypt/live/{first_server}/cert.pem"):
logger.info(
f"Certificates already exists for domain(s) {domains}",
)
continue
+
real_email = getenv(
f"{first_server}_EMAIL_LETS_ENCRYPT",
getenv("EMAIL_LETS_ENCRYPT", f"contact@{first_server}"),
)
- if real_email == "":
+ if not real_email:
real_email = f"contact@{first_server}"
+
logger.info(
f"Asking certificates for domains : {domains} (email = {real_email}) ...",
)
@@ -67,16 +95,32 @@ try:
f"Certificate generation succeeded for domain(s) : {domains}"
)
+ if exists(f"/etc/letsencrypt/live/{first_server}/cert.pem"):
+ with open(f"/etc/letsencrypt/live/{first_server}/cert.pem") as f:
+ cert = f.read()
+
+ # Update db
+ err = db.update_job_cache(
+ "letsencrypt",
+ first_server,
+ "cert.pem",
+ cert,
+ )
+ if err:
+ logger.warning(f"Couldn't update db cache: {err}")
+
# Singlesite case
- elif getenv("AUTO_LETS_ENCRYPT") == "yes" and getenv("SERVER_NAME") != "":
- first_server = getenv("SERVER_NAME").split(" ")[0]
- domains = getenv("SERVER_NAME").replace(" ", ",")
+ elif getenv("AUTO_LETS_ENCRYPT", "no") == "yes" and getenv("SERVER_NAME", ""):
+ first_server = getenv("SERVER_NAME", "").split(" ")[0]
+ domains = getenv("SERVER_NAME", "").replace(" ", ",")
+
if exists(f"/etc/letsencrypt/live/{first_server}/cert.pem"):
logger.info(f"Certificates already exists for domain(s) {domains}")
else:
real_email = getenv("EMAIL_LETS_ENCRYPT", f"contact@{first_server}")
- if real_email == "":
+ if not real_email:
real_email = f"contact@{first_server}"
+
logger.info(
f"Asking certificates for domain(s) : {domains} (email = {real_email}) ...",
)
@@ -88,7 +132,19 @@ try:
f"Certificate generation succeeded for domain(s) : {domains}"
)
+ if exists(f"/etc/letsencrypt/live/{first_server}/cert.pem"):
+ with open(f"/etc/letsencrypt/live/{first_server}/cert.pem") as f:
+ cert = f.read()
+ # Update db
+ err = db.update_job_cache(
+ "letsencrypt",
+ first_server,
+ "cert.pem",
+ cert,
+ )
+ if err:
+ logger.warning(f"Couldn't update db cache: {err}")
except:
status = 1
logger.error(f"Exception while running certbot-new.py :\n{format_exc()}")
diff --git a/src/common/core/letsencrypt/jobs/certbot-renew.py b/src/common/core/letsencrypt/jobs/certbot-renew.py
index 9e7951f1e..cf464c3b8 100755
--- a/src/common/core/letsencrypt/jobs/certbot-renew.py
+++ b/src/common/core/letsencrypt/jobs/certbot-renew.py
@@ -1,6 +1,6 @@
#!/usr/bin/python3
-from os import environ, getenv
+from os import environ, getcwd, getenv
from os.path import exists
from subprocess import DEVNULL, STDOUT, run
from sys import exit as sys_exit, path as sys_path
@@ -13,10 +13,16 @@ from logger import setup_logger
def renew(domain):
- cmd = f"/usr/share/bunkerweb/deps/python/bin/certbot renew --cert-name {domain} --deploy-hook /usr/share/bunkerweb/core/letsencrypt/jobs/certbot-deploy.py"
environ["PYTHONPATH"] = "/usr/share/bunkerweb/deps/python"
proc = run(
- cmd.split(" "),
+ [
+ "/usr/share/bunkerweb/deps/python/bin/certbot",
+ "renew",
+ "--cert-name",
+ domain,
+ "--deploy-hook",
+ f"{getcwd()}/certbot-deploy.py",
+ ],
stdin=DEVNULL,
stderr=STDOUT,
env=environ,
@@ -30,23 +36,25 @@ status = 0
try:
if getenv("MULTISITE") == "yes":
for first_server in getenv("SERVER_NAME").split(" "):
- if first_server == "":
- continue
if (
- getenv(f"{first_server}_AUTO_LETS_ENCRYPT", getenv("AUTO_LETS_ENCRYPT"))
+ not first_server
+ or getenv(
+ f"{first_server}_AUTO_LETS_ENCRYPT",
+ getenv("AUTO_LETS_ENCRYPT", "no"),
+ )
!= "yes"
+ or not exists(f"/etc/letsencrypt/live/{first_server}/cert.pem")
):
continue
- if not exists(f"/etc/letsencrypt/live/{first_server}/cert.pem"):
- continue
+
ret = renew(first_server)
if ret != 0:
status = 2
logger.error(
f"Certificates renewal for {first_server} failed",
)
- elif getenv("AUTO_LETS_ENCRYPT") == "yes" and getenv("SERVER_NAME") != "":
- first_server = getenv("SERVER_NAME").split(" ")[0]
+ elif getenv("AUTO_LETS_ENCRYPT", "no") == "yes" and not getenv("SERVER_NAME", ""):
+ first_server = getenv("SERVER_NAME", "").split(" ")[0]
if exists(f"/etc/letsencrypt/live/{first_server}/cert.pem"):
ret = renew(first_server)
if ret != 0:
diff --git a/src/common/db/Database.py b/src/common/db/Database.py
index 8959d8a70..0e614f38b 100644
--- a/src/common/db/Database.py
+++ b/src/common/db/Database.py
@@ -2,7 +2,12 @@ from contextlib import contextmanager
from copy import deepcopy
from datetime import datetime
from hashlib import sha256
-from logging import INFO, WARNING, Logger, getLogger
+from logging import (
+ NOTSET,
+ Logger,
+ _levelToName,
+ _nameToLevel,
+)
import oracledb
from os import _exit, getenv, listdir, makedirs
from os.path import dirname, exists
@@ -24,6 +29,8 @@ from traceback import format_exc
from model import (
Base,
+ Instances,
+ Logs,
Plugins,
Settings,
Global_values,
@@ -55,10 +62,6 @@ class Database:
self.__sql_session = None
self.__sql_engine = None
- getLogger("sqlalchemy.engine").setLevel(
- logger.level if logger.level != INFO else WARNING
- )
-
if not sqlalchemy_string:
sqlalchemy_string = getenv(
"DATABASE_URI", "sqlite:////var/lib/bunkerweb/db.sqlite3"
@@ -77,7 +80,6 @@ class Database:
sqlalchemy_string,
encoding="utf-8",
future=True,
- logging_name="sqlalchemy.engine",
)
except ArgumentError:
self.__logger.error(f"Invalid database URI: {sqlalchemy_string}")
@@ -1238,7 +1240,6 @@ class Database:
{
"service_id": cache.service_id,
"file_name": cache.file_name,
- "data": cache.data,
"last_update": cache.last_update.strftime(
"%Y/%m/%d, %I:%M:%S %p"
),
@@ -1247,7 +1248,6 @@ class Database:
.with_entities(
Jobs_cache.service_id,
Jobs_cache.file_name,
- Jobs_cache.data,
Jobs_cache.last_update,
)
.filter_by(job_name=job.name)
@@ -1266,3 +1266,104 @@ class Database:
.all()
)
}
+
+ def get_job_cache_file(self, job_name: str, file_name: str) -> Optional[bytes]:
+ """Get job cache file."""
+ with self.__db_session() as session:
+ return (
+ session.query(Jobs_cache)
+ .with_entities(Jobs_cache.data)
+ .filter_by(job_name=job_name, file_name=file_name)
+ .first()
+ )
+
+ def save_log(
+ self,
+ log: str,
+ level: Tuple[str, int],
+ component: str,
+ ) -> str:
+ """Save log."""
+ with self.__db_session() as session:
+ session.add(
+ Logs(
+ id=int(datetime.now().timestamp()),
+ message=log,
+ level=str(_levelToName[_nameToLevel.get(level, NOTSET)])
+ if isinstance(level, str)
+ else _levelToName.get(level, "NOTSET"),
+ component=component,
+ )
+ )
+
+ try:
+ session.commit()
+ except BaseException:
+ return format_exc()
+
+ return ""
+
+ def add_instance(self, hostname: str, port: int, server_name: str) -> str:
+ """Add instance."""
+ with self.__db_session() as session:
+ db_instance = (
+ session.query(Instances)
+ .with_entities(Instances.hostname)
+ .filter_by(hostname=hostname)
+ .first()
+ )
+
+ if db_instance is not None:
+ return "An instance with the same hostname already exists."
+
+ session.add(
+ Instances(hostname=hostname, port=int(port), server_name=server_name)
+ )
+
+ try:
+ session.commit()
+ except BaseException:
+ return f"An error occurred while adding the instance {hostname} (port: {port}, server name: {server_name}).\n{format_exc()}"
+
+ return ""
+
+ def update_instances(self, instances: List[Dict[str, Any]]) -> str:
+ """Update instances."""
+ to_put = []
+ with self.__db_session() as session:
+ session.query(Instances).delete()
+
+ for instance in instances:
+ to_put.append(
+ Instances(
+ hostname=instance["hostname"],
+ port=instance["env"].get("API_HTTP_PORT", 5000),
+ server_name=instance["env"].get("API_SERVER_NAME", "bwapi"),
+ )
+ )
+
+ try:
+ session.add_all(to_put)
+ session.commit()
+ except BaseException:
+ return format_exc()
+
+ return ""
+
+ def get_instances(self) -> List[Dict[str, Any]]:
+ """Get instances."""
+ with self.__db_session() as session:
+ return [
+ {
+ "hostname": instance.hostname,
+ "port": instance.port,
+ "server_name": instance.server_name,
+ }
+ for instance in (
+ session.query(Instances)
+ .with_entities(
+ Instances.hostname, Instances.port, Instances.server_name
+ )
+ .all()
+ )
+ ]
diff --git a/src/common/db/model.py b/src/common/db/model.py
index d07841560..0dc46d247 100644
--- a/src/common/db/model.py
+++ b/src/common/db/model.py
@@ -10,7 +10,6 @@ from sqlalchemy import (
PrimaryKeyConstraint,
SmallInteger,
String,
- text,
TIMESTAMP,
)
from sqlalchemy.orm import declarative_base, relationship
@@ -30,7 +29,15 @@ CUSTOM_CONFIGS_TYPES_ENUM = Enum(
"stream_http",
name="custom_configs_types_enum",
)
-LOG_LEVELS_ENUM = Enum("DEBUG", "INFO", "WARNING", "ERROR", name="log_levels_enum")
+LOG_LEVELS_ENUM = Enum(
+ "CRITICAL",
+ "ERROR",
+ "WARNING",
+ "INFO",
+ "DEBUG",
+ "NOTSET",
+ name="log_levels_enum",
+)
INTEGRATIONS_ENUM = Enum(
"Linux",
"Docker",
@@ -265,6 +272,14 @@ class Logs(Base):
component = Column(String(255), nullable=False)
+class Instances(Base):
+ __tablename__ = "instances"
+
+ hostname = Column(String(255), primary_key=True)
+ port = Column(Integer, nullable=False)
+ server_name = Column(String(255), nullable=False)
+
+
class Metadata(Base):
__tablename__ = "metadata"
diff --git a/src/common/gen/main.py b/src/common/gen/main.py
index 08909931a..ef397d3e4 100644
--- a/src/common/gen/main.py
+++ b/src/common/gen/main.py
@@ -174,8 +174,7 @@ if __name__ == "__main__":
retries += 1
sleep(5)
- cmd = "/usr/sbin/nginx -s reload"
- proc = run(cmd.split(" "), stdin=DEVNULL, stderr=STDOUT)
+ proc = run(["nginx", "-s", "reload"], stdin=DEVNULL, stderr=STDOUT)
if proc.returncode != 0:
status = 1
logger.error("Error while reloading nginx")
diff --git a/src/common/gen/save_config.py b/src/common/gen/save_config.py
index f9096f7a8..6ead30945 100644
--- a/src/common/gen/save_config.py
+++ b/src/common/gen/save_config.py
@@ -369,6 +369,25 @@ if __name__ == "__main__":
sys_exit(1)
else:
logger.info("Config successfully saved to database")
+
+ if apis:
+ for api in apis:
+ endpoint_data = api.get_endpoint().replace("http://", "").split(":")
+ err = db.add_instance(
+ endpoint_data[0], endpoint_data[1], api.get_host()
+ )
+
+ if err:
+ logger.warning(err)
+ else:
+ err = db.add_instance(
+ "localhost",
+ config_files.get("API_HTTP_PORT", 5000),
+ config_files.get("API_SERVER_NAME", "bwapi"),
+ )
+
+ if err:
+ logger.warning(err)
except SystemExit as e:
sys_exit(e)
except:
diff --git a/src/common/helpers/entrypoint.sh b/src/common/helpers/entrypoint.sh
index c0f6b84b6..695ff6e69 100644
--- a/src/common/helpers/entrypoint.sh
+++ b/src/common/helpers/entrypoint.sh
@@ -11,7 +11,7 @@ log "ENTRYPOINT" "ℹ️" "Starting BunkerWeb v$(cat /usr/share/bunkerweb/VERSIO
function trap_exit() {
log "ENTRYPOINT" "ℹ️" "Catched stop operation"
log "ENTRYPOINT" "ℹ️" "Stopping nginx ..."
- /usr/sbin/nginx -s stop
+ nginx -s stop
}
trap "trap_exit" TERM INT QUIT
diff --git a/src/common/utils/logger.py b/src/common/utils/logger.py
index 2c1d55086..f54319dd4 100644
--- a/src/common/utils/logger.py
+++ b/src/common/utils/logger.py
@@ -1,38 +1,89 @@
-import logging
+from logging import (
+ CRITICAL,
+ DEBUG,
+ ERROR,
+ INFO,
+ WARNING,
+ Logger,
+ _levelToName,
+ _nameToLevel,
+ addLevelName,
+ basicConfig,
+ getLogger,
+ setLoggerClass,
+)
+from os import getenv
+from threading import Lock
-logging.basicConfig(
+
+class BWLogger(Logger):
+ def __init__(self, name, level=INFO):
+ self.name = name
+ self.db_lock = Lock()
+ return super(BWLogger, self).__init__(name, level)
+
+ def _log(
+ self,
+ level,
+ msg,
+ args,
+ exc_info=None,
+ extra=None,
+ stack_info=False,
+ stacklevel=1,
+ *,
+ store_db: bool = False,
+ db=None,
+ ):
+ if store_db is True and db is not None:
+ with self.db_lock:
+ err = db.save_log(msg, level, self.name)
+
+ if err:
+ self.error(f"Failed to save log to database: {err}")
+
+ return super(BWLogger, self)._log(
+ level, msg, args, exc_info, extra, stack_info, stacklevel
+ )
+
+
+setLoggerClass(BWLogger)
+
+default_level = _nameToLevel.get(getenv("LOG_LEVEL", "INFO").upper(), INFO)
+basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt="[%Y-%m-%d %H:%M:%S]",
- level=logging.INFO,
+ level=default_level,
+)
+
+getLogger("sqlalchemy.orm.mapper.Mapper").setLevel(
+ default_level if default_level != INFO else WARNING
+)
+getLogger("sqlalchemy.orm.relationships.RelationshipProperty").setLevel(
+ default_level if default_level != INFO else WARNING
+)
+getLogger("sqlalchemy.orm.strategies.LazyLoader").setLevel(
+ default_level if default_level != INFO else WARNING
+)
+getLogger("sqlalchemy.pool.impl.QueuePool").setLevel(
+ default_level if default_level != INFO else WARNING
+)
+getLogger("sqlalchemy.engine.Engine").setLevel(
+ default_level if default_level != INFO else WARNING
)
# Edit the default levels of the logging module
-logging.addLevelName(logging.CRITICAL, "🚨")
-logging.addLevelName(logging.DEBUG, "🐛")
-logging.addLevelName(logging.ERROR, "❌")
-logging.addLevelName(logging.INFO, "ℹ️ ")
-logging.addLevelName(logging.WARNING, "⚠️ ")
+addLevelName(CRITICAL, "🚨")
+addLevelName(DEBUG, "🐛")
+addLevelName(ERROR, "❌")
+addLevelName(INFO, "ℹ️ ")
+addLevelName(WARNING, "⚠️ ")
-def setup_logger(title: str, level=logging.INFO) -> logging.Logger:
+def setup_logger(title: str, level=INFO) -> Logger:
"""Set up local logger"""
title = title.upper()
- logger = logging.getLogger(title)
-
- if level not in (
- logging.DEBUG,
- logging.INFO,
- logging.WARNING,
- logging.ERROR,
- logging.CRITICAL,
- "DEBUG",
- "INFO",
- "WARNING",
- "ERROR",
- "CRITICAL",
- ):
- level = logging.INFO
-
- logger.setLevel(level)
+ logger = getLogger(title)
+ logger.setLevel(_nameToLevel.get(level, _levelToName.get(level, INFO)))
return logger
diff --git a/src/scheduler/Dockerfile b/src/scheduler/Dockerfile
index 7568ec7b3..3066d7f89 100644
--- a/src/scheduler/Dockerfile
+++ b/src/scheduler/Dockerfile
@@ -10,7 +10,7 @@ RUN mkdir -p /usr/share/bunkerweb/deps && \
rm -rf /tmp/req
# Install python requirements
-RUN apk add --no-cache --virtual .build-deps g++ gcc libffi-dev && \
+RUN apk add --no-cache --virtual .build-deps g++ gcc musl-dev jpeg-dev zlib-dev libffi-dev cairo-dev pango-dev gdk-pixbuf-dev && \
pip install --no-cache-dir --upgrade pip && \
pip install wheel && \
mkdir -p /usr/share/bunkerweb/deps/python && \
@@ -51,7 +51,8 @@ RUN apk add --no-cache bash libgcc libstdc++ openssl && \
chown -R root:scheduler /usr/share/bunkerweb /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb && \
for dir in $(echo "/usr/share/bunkerweb /etc/bunkerweb") ; do find ${dir} -type f -exec chmod 0740 {} \; ; done && \
for dir in $(echo "/usr/share/bunkerweb /etc/bunkerweb") ; do find ${dir} -type d -exec chmod 0750 {} \; ; done && \
- chmod 770 /var/cache/bunkerweb /var/lib/bunkerweb /var/tmp/bunkerweb && \
+ chmod -R 770 /var/cache/bunkerweb /var/lib/bunkerweb /var/tmp/bunkerweb && \
+ find /usr/share/bunkerweb/core/*/jobs/* -type f -exec chmod 750 {} \; && \
chmod 750 /usr/share/bunkerweb/gen/*.py /usr/share/bunkerweb/scheduler/main.py /usr/share/bunkerweb/scheduler/entrypoint.sh /usr/share/bunkerweb/helpers/*.sh /usr/share/bunkerweb/deps/python/bin/* && \
mkdir /etc/nginx && \
chown -R scheduler:scheduler /etc/nginx && \
diff --git a/src/scheduler/JobScheduler.py b/src/scheduler/JobScheduler.py
index ccbcd8cf9..db3c1db8d 100644
--- a/src/scheduler/JobScheduler.py
+++ b/src/scheduler/JobScheduler.py
@@ -80,7 +80,7 @@ class JobScheduler(ApiCaller):
if self.__integration == "Linux":
self.__logger.info("Reloading nginx ...")
proc = run(
- ["/usr/sbin/nginx", "-s", "reload"],
+ ["nginx", "-s", "reload"],
stdin=DEVNULL,
stderr=PIPE,
env=self.__env,
@@ -111,6 +111,8 @@ class JobScheduler(ApiCaller):
stdin=DEVNULL,
stderr=STDOUT,
env=self.__env,
+ user=101,
+ group=101,
)
except BaseException:
with self.__thread_lock:
diff --git a/src/scheduler/main.py b/src/scheduler/main.py
index e1c9f129e..e2d8e53ec 100644
--- a/src/scheduler/main.py
+++ b/src/scheduler/main.py
@@ -152,7 +152,6 @@ if __name__ == "__main__":
logger,
sqlalchemy_string=getenv("DATABASE_URI", None),
)
- custom_configs = db.get_custom_configs()
# END Define db because otherwhise it will be undefined for Linux
logger.info("Scheduler started ...")
@@ -192,11 +191,11 @@ if __name__ == "__main__":
"Kubernetes",
"Autoconf",
):
- ret = db.set_autoconf_load(False)
- if ret:
+ err = db.set_autoconf_load(False)
+ if err:
success = False
logger.error(
- f"Can't set autoconf loaded metadata to false in database: {ret}",
+ f"Can't set autoconf loaded metadata to false in database: {err}",
)
while not db.is_autoconf_loaded():
@@ -209,8 +208,16 @@ if __name__ == "__main__":
or db.get_config() != dotenv_values("/var/tmp/bunkerweb/variables.env")
):
# run the config saver
- cmd = f"python /usr/share/bunkerweb/gen/save_config.py --settings /usr/share/bunkerweb/settings.json"
- proc = subprocess_run(cmd.split(" "), stdin=DEVNULL, stderr=STDOUT)
+ proc = subprocess_run(
+ [
+ "python",
+ "/usr/share/bunkerweb/gen/save_config.py",
+ "--settings",
+ "/usr/share/bunkerweb/settings.json",
+ ],
+ stdin=DEVNULL,
+ stderr=STDOUT,
+ )
if proc.returncode != 0:
logger.error(
"Config saver failed, configuration will not work as expected...",
@@ -261,10 +268,10 @@ if __name__ == "__main__":
if custom_confs:
old_configs = db.get_custom_configs()
- ret = db.save_custom_configs(custom_confs, "manual")
- if ret:
+ err = db.save_custom_configs(custom_confs, "manual")
+ if err:
logger.error(
- f"Couldn't save some manually created custom configs to database: {ret}",
+ f"Couldn't save some manually created custom configs to database: {err}",
)
custom_configs = db.get_custom_configs()
@@ -273,7 +280,7 @@ if __name__ == "__main__":
generate_custom_configs(custom_configs, integration, api_caller)
logger.info("Executing scheduler ...")
-
+
generate = not exists(
"/var/tmp/bunkerweb/variables.env"
) or env != dotenv_values("/var/tmp/bunkerweb/variables.env")
@@ -300,8 +307,21 @@ if __name__ == "__main__":
if generate is True:
# run the generator
- cmd = f"python /usr/share/bunkerweb/gen/main.py --settings /usr/share/bunkerweb/settings.json --templates /usr/share/bunkerweb/confs --output /etc/nginx{f' --variables {args.variables}' if args.variables else ''}"
- proc = subprocess_run(cmd.split(" "), stdin=DEVNULL, stderr=STDOUT)
+ proc = subprocess_run(
+ [
+ "python3",
+ "/usr/share/bunkerweb/gen/main.py",
+ "--settings",
+ "/usr/share/bunkerweb/settings.json",
+ "--templates",
+ "/usr/share/bunkerweb/confs",
+ "--output",
+ "/etc/nginx",
+ ]
+ + (["--variables", args.variables] if args.variables else []),
+ stdin=DEVNULL,
+ stderr=STDOUT,
+ )
if proc.returncode != 0:
logger.error(
@@ -351,13 +371,12 @@ if __name__ == "__main__":
logger.info("Reloading nginx ...")
# Reloading the nginx server.
# Had to use this instead of the nginx reload command because it was not working
- proc = subprocess_run(["nginx", "-s", "reload"], stdin=DEVNULL, stderr=STDOUT)
- # proc = run(
- # ["/usr/sbin/nginx", "-s", "reload"],
- # stdin=DEVNULL,
- # stderr=PIPE,
- # env=deepcopy(env),
- # )
+ proc = subprocess_run(
+ ["nginx", "-s", "reload"],
+ stdin=DEVNULL,
+ stderr=STDOUT,
+ env=deepcopy(env),
+ )
if proc.returncode == 0:
logger.info("Successfuly reloaded nginx")
else:
diff --git a/src/ui/Dockerfile b/src/ui/Dockerfile
index 40e2c88f6..e1a5ae428 100755
--- a/src/ui/Dockerfile
+++ b/src/ui/Dockerfile
@@ -10,7 +10,7 @@ RUN mkdir -p /usr/share/bunkerweb/deps && \
rm -rf /tmp/req
# Install python requirements
-RUN apk add --no-cache --virtual .build-deps g++ gcc && \
+RUN apk add --no-cache --virtual .build-deps g++ gcc musl-dev jpeg-dev zlib-dev libffi-dev cairo-dev pango-dev gdk-pixbuf-dev && \
pip install --no-cache-dir --upgrade pip && \
pip install wheel && \
mkdir -p /usr/share/bunkerweb/deps/python && \
diff --git a/src/ui/main.py b/src/ui/main.py
index a95fed58e..a35dbb61a 100755
--- a/src/ui/main.py
+++ b/src/ui/main.py
@@ -1,3 +1,4 @@
+from io import BytesIO
from bs4 import BeautifulSoup
from copy import deepcopy
from datetime import datetime, timedelta, timezone
@@ -15,6 +16,7 @@ from flask import (
redirect,
render_template,
request,
+ send_file,
url_for,
)
from flask_login import LoginManager, login_required, login_user, logout_user
@@ -49,12 +51,6 @@ from src.User import User
from utils import (
check_settings,
- env_to_summary_class,
- form_plugin_gen,
- form_service_gen,
- form_service_gen_multiple,
- form_service_gen_multiple_values,
- gen_folders_tree_html,
get_variables,
path_to_dict,
)
@@ -138,6 +134,8 @@ elif integration == "Kubernetes":
kubernetes_client = kube_client.CoreV1Api()
db = Database(logger)
+with open("/usr/share/bunkerweb/VERSION", "r") as f:
+ bw_version = f.read().strip()
try:
app.config.update(
@@ -164,14 +162,6 @@ except FileNotFoundError as e:
sys_exit(1)
# Declare functions for jinja2
-app.jinja_env.globals.update(env_to_summary_class=env_to_summary_class)
-app.jinja_env.globals.update(form_plugin_gen=form_plugin_gen)
-app.jinja_env.globals.update(form_service_gen=form_service_gen)
-app.jinja_env.globals.update(form_service_gen_multiple=form_service_gen_multiple)
-app.jinja_env.globals.update(
- form_service_gen_multiple_values=form_service_gen_multiple_values
-)
-app.jinja_env.globals.update(gen_folders_tree_html=gen_folders_tree_html)
app.jinja_env.globals.update(check_settings=check_settings)
@@ -273,27 +263,30 @@ def home():
posts: a list of posts
"""
- r = get(
- "https://raw.githubusercontent.com/bunkerity/bunkerweb/master/VERSION",
- )
+ try:
+ r = get(
+ "https://raw.githubusercontent.com/bunkerity/bunkerweb/master/VERSION",
+ )
+ except BaseException:
+ r = None
remote_version = None
- if r.status_code == 200:
+ if r and r.status_code == 200:
remote_version = r.text.strip()
- with open("/usr/share/bunkerweb/VERSION", "r") as f:
- version = f.read().strip()
-
headers = default_headers()
headers.update({"User-Agent": "bunkerweb-ui"})
- r = get(
- "https://www.bunkerity.com/wp-json/wp/v2/posts",
- headers=headers,
- )
+ try:
+ r = get(
+ "https://www.bunkerity.com/wp-json/wp/v2/posts",
+ headers=headers,
+ )
+ except BaseException:
+ r = None
formatted_posts = None
- if r.status_code == 200:
+ if r and r.status_code == 200:
posts = r.json()
formatted_posts = []
@@ -325,12 +318,13 @@ def home():
return render_template(
"home.html",
- check_version=not remote_version or version == remote_version,
+ check_version=not remote_version or bw_version == remote_version,
remote_version=remote_version,
- version=version,
+ version=bw_version,
instances_number=instances_number,
services_number=services_number,
posts=formatted_posts,
+ plugins_errors=db.get_plugins_errors(),
dark_mode=app.config["DARK_MODE"],
)
@@ -1019,7 +1013,6 @@ def plugins():
return render_template(
"plugins.html",
- plugins=app.config["CONFIG"].get_plugins(),
dark_mode=app.config["DARK_MODE"],
)
@@ -1275,36 +1268,48 @@ def logs_linux():
@app.route("/logs/", methods=["GET"])
@login_required
def logs_container(container_id):
- last_update = request.args.get(
- "last_update",
- str(datetime.now().timestamp() - timedelta(days=1).total_seconds()),
- )
+ last_update = request.args.get("last_update", None)
+ from_date = request.args.get("from_date", None)
+ to_date = request.args.get("to_date", None)
+
+ if from_date is not None:
+ last_update = from_date
+
+ if any(arg and not arg.isdigit() for arg in [last_update, from_date, to_date]):
+ return (
+ jsonify(
+ {
+ "status": "ko",
+ "message": "arguments must all be integers (timestamps)",
+ }
+ ),
+ 422,
+ )
+ elif not last_update:
+ last_update = int(
+ datetime.now().timestamp()
+ - timedelta(days=1).total_seconds() # 1 day before
+ )
+ else:
+ last_update = int(last_update) // 1000
+
+ to_date = int(to_date) // 1000 if to_date else None
+
logs = []
if docker_client:
try:
- if last_update and not last_update.isdigit():
- return (
- jsonify(
- {
- "status": "ko",
- "message": "last_update must be an integer",
- }
- ),
- 422,
- )
-
if getenv("SWARM_MODE", "no") == "no":
docker_logs = docker_client.containers.get(container_id).logs(
stdout=True,
stderr=True,
- since=datetime.fromtimestamp(int(last_update)),
+ since=datetime.fromtimestamp(last_update),
timestamps=True,
)
else:
docker_logs = docker_client.services.get(container_id).logs(
stdout=True,
stderr=True,
- since=datetime.fromtimestamp(int(last_update)),
+ since=datetime.fromtimestamp(last_update),
timestamps=True,
)
@@ -1324,7 +1329,7 @@ def logs_container(container_id):
kubernetes_logs = kubernetes_client.read_namespaced_pod_log(
container_id,
getenv("KUBERNETES_NAMESPACE", "default"),
- since_seconds=int(datetime.now().timestamp() - int(last_update)),
+ since_seconds=int(datetime.now().timestamp() - last_update),
timestamps=True,
)
tmp_logs = kubernetes_logs.split("\n")[0:-1]
@@ -1339,10 +1344,16 @@ def logs_container(container_id):
404,
)
- logger.warning(tmp_logs)
-
for log in tmp_logs:
+ splitted = log.split(" ")
+ timestamp = splitted[0]
+
+ if to_date is not None and dateutil_parse(timestamp).timestamp() > to_date:
+ break
+
+ log = " ".join(splitted[1:])
log_lower = log.lower()
+
logs.append(
{
"content": log,
@@ -1371,11 +1382,47 @@ def logs_container(container_id):
def jobs():
return render_template(
"jobs.html",
- jobs=db.get_jobs(),
+ jobs=dumps(db.get_jobs()),
dark_mode=app.config["DARK_MODE"],
)
+@app.route("/jobs/download", methods=["GET"])
+@login_required
+def jobs_download():
+ job_name = request.args.get("job_name", None)
+ file_name = request.args.get("file_name", None)
+
+ if not job_name or not file_name:
+ return (
+ jsonify(
+ {
+ "status": "ko",
+ "message": "job_name and file_name are required",
+ }
+ ),
+ 422,
+ )
+
+ cache_file = db.get_job_cache_file(job_name, file_name)
+
+ if not cache_file:
+ return (
+ jsonify(
+ {
+ "status": "ko",
+ "message": "file not found",
+ }
+ ),
+ 404,
+ )
+
+ with BytesIO(cache_file) as file:
+ file.seek(0)
+
+ return send_file(file, as_attachment=True, attachment_filename=file_name)
+
+
@app.route("/login", methods=["GET", "POST"])
def login():
fail = False
@@ -1416,12 +1463,6 @@ def darkmode():
return jsonify({"status": "ok"})
-@app.route("/plugins_errors", methods=["GET"])
-@login_required
-def plugins_errors():
- return jsonify({"status": "ok", "plugins_errors": db.get_plugins_errors()})
-
-
@app.route("/check_reloading")
@login_required
def check_reloading():
diff --git a/src/ui/src/Config.py b/src/ui/src/Config.py
index 3f5cf1c99..b88cb7ec7 100644
--- a/src/ui/src/Config.py
+++ b/src/ui/src/Config.py
@@ -166,10 +166,10 @@ class Config:
if proc.returncode != 0:
raise Exception(f"Error from generator (return code = {proc.returncode})")
- ret = self.__db.save_config(conf, "ui")
- if ret:
+ err = self.__db.save_config(conf, "ui")
+ if err:
self.__logger.error(
- f"Can't save config in database: {ret}",
+ f"Can't save config in database: {err}",
)
def get_plugins_settings(self) -> dict:
diff --git a/src/ui/src/ConfigFiles.py b/src/ui/src/ConfigFiles.py
index d47e9a87c..3456a009c 100644
--- a/src/ui/src/ConfigFiles.py
+++ b/src/ui/src/ConfigFiles.py
@@ -43,9 +43,9 @@ class ConfigFiles:
}
)
- ret = self.__db.save_custom_configs(custom_configs, "ui")
- if ret:
- self.__logger.error(f"Could not save custom configs: {ret}")
+ err = self.__db.save_custom_configs(custom_configs, "ui")
+ if err:
+ self.__logger.error(f"Could not save custom configs: {err}")
return "Couldn't save custom configs to database"
return ""
diff --git a/src/ui/static/css/air-datepicker/air-datepicker.css b/src/ui/static/css/air-datepicker/air-datepicker.css
deleted file mode 100644
index 6d6be2281..000000000
--- a/src/ui/static/css/air-datepicker/air-datepicker.css
+++ /dev/null
@@ -1,18 +0,0 @@
-.air-datepicker-cell.-day-.-other-month-,.air-datepicker-cell.-year-.-other-decade-{color:var(--adp-color-other-month)}.air-datepicker-cell.-day-.-other-month-:hover,.air-datepicker-cell.-year-.-other-decade-:hover{color:var(--adp-color-other-month-hover)}.-disabled-.-focus-.air-datepicker-cell.-day-.-other-month-,.-disabled-.-focus-.air-datepicker-cell.-year-.-other-decade-{color:var(--adp-color-other-month)}.-selected-.air-datepicker-cell.-day-.-other-month-,.-selected-.air-datepicker-cell.-year-.-other-decade-{color:#fff;background:var(--adp-background-color-selected-other-month)}.-selected-.-focus-.air-datepicker-cell.-day-.-other-month-,.-selected-.-focus-.air-datepicker-cell.-year-.-other-decade-{background:var(--adp-background-color-selected-other-month-focused)}.-in-range-.air-datepicker-cell.-day-.-other-month-,.-in-range-.air-datepicker-cell.-year-.-other-decade-{background-color:var(--adp-background-color-in-range);color:var(--adp-color)}.-in-range-.-focus-.air-datepicker-cell.-day-.-other-month-,.-in-range-.-focus-.air-datepicker-cell.-year-.-other-decade-{background-color:var(--adp-background-color-in-range-focused)}.air-datepicker-cell.-day-.-other-month-:empty,.air-datepicker-cell.-year-.-other-decade-:empty{background:none;border:none}.air-datepicker-cell{border-radius:var(--adp-cell-border-radius);box-sizing:border-box;cursor:pointer;display:flex;position:relative;align-items:center;justify-content:center;z-index:1}.air-datepicker-cell.-focus-{background:var(--adp-cell-background-color-hover)}.air-datepicker-cell.-current-{color:var(--adp-color-current-date)}.air-datepicker-cell.-current-.-focus-{color:var(--adp-color)}.air-datepicker-cell.-current-.-in-range-{color:var(--adp-color-current-date)}.air-datepicker-cell.-disabled-{cursor:default;color:var(--adp-color-disabled)}.air-datepicker-cell.-disabled-.-focus-{color:var(--adp-color-disabled)}.air-datepicker-cell.-disabled-.-in-range-{color:var(--adp-color-disabled-in-range)}.air-datepicker-cell.-disabled-.-current-.-focus-{color:var(--adp-color-disabled)}.air-datepicker-cell.-in-range-{background:var(--adp-cell-background-color-in-range);border-radius:0}.air-datepicker-cell.-in-range-:hover{background:var(--adp-cell-background-color-in-range-hover)}.air-datepicker-cell.-range-from-{border:1px solid var(--adp-cell-border-color-in-range);background-color:var(--adp-cell-background-color-in-range);border-radius:var(--adp-cell-border-radius) 0 0 var(--adp-cell-border-radius)}.air-datepicker-cell.-range-to-{border:1px solid var(--adp-cell-border-color-in-range);background-color:var(--adp-cell-background-color-in-range);border-radius:0 var(--adp-cell-border-radius) var(--adp-cell-border-radius) 0}.air-datepicker-cell.-range-to-.-range-from-{border-radius:var(--adp-cell-border-radius)}.air-datepicker-cell.-selected-{color:#fff;border:none;background:var(--adp-cell-background-color-selected)}.air-datepicker-cell.-selected-.-current-{color:#fff;background:var(--adp-cell-background-color-selected)}.air-datepicker-cell.-selected-.-focus-{background:var(--adp-cell-background-color-selected-hover)}
-
-.air-datepicker-body{transition:all var(--adp-transition-duration) var(--adp-transition-ease)}.air-datepicker-body.-hidden-{display:none}.air-datepicker-body--day-names{display:grid;grid-template-columns:repeat(7, var(--adp-day-cell-width));margin:8px 0 3px}.air-datepicker-body--day-name{color:var(--adp-day-name-color);display:flex;align-items:center;justify-content:center;flex:1;text-align:center;text-transform:uppercase;font-size:.8em}.air-datepicker-body--day-name.-clickable-{cursor:pointer}.air-datepicker-body--day-name.-clickable-:hover{color:var(--adp-day-name-color-hover)}.air-datepicker-body--cells{display:grid}.air-datepicker-body--cells.-days-{grid-template-columns:repeat(7, var(--adp-day-cell-width));grid-auto-rows:var(--adp-day-cell-height)}.air-datepicker-body--cells.-months-{grid-template-columns:repeat(3, 1fr);grid-auto-rows:var(--adp-month-cell-height)}.air-datepicker-body--cells.-years-{grid-template-columns:repeat(4, 1fr);grid-auto-rows:var(--adp-year-cell-height)}
-
-.air-datepicker-nav{display:flex;justify-content:space-between;border-bottom:1px solid var(--adp-border-color-inner);min-height:var(--adp-nav-height);padding:var(--adp-padding);box-sizing:content-box}.-only-timepicker- .air-datepicker-nav{display:none}.air-datepicker-nav--title,.air-datepicker-nav--action{display:flex;cursor:pointer;align-items:center;justify-content:center}.air-datepicker-nav--action{width:var(--adp-nav-action-size);border-radius:var(--adp-border-radius);-webkit-user-select:none;-moz-user-select:none;user-select:none}.air-datepicker-nav--action:hover{background:var(--adp-background-color-hover)}.air-datepicker-nav--action:active{background:var(--adp-background-color-active)}.air-datepicker-nav--action.-disabled-{visibility:hidden}.air-datepicker-nav--action svg{width:32px;height:32px}.air-datepicker-nav--action path{fill:none;stroke:var(--adp-nav-arrow-color);stroke-width:2px}.air-datepicker-nav--title{border-radius:var(--adp-border-radius);padding:0 8px}.air-datepicker-nav--title i{font-style:normal;color:var(--adp-nav-color-secondary);margin-left:.3em}.air-datepicker-nav--title:hover{background:var(--adp-background-color-hover)}.air-datepicker-nav--title:active{background:var(--adp-background-color-active)}.air-datepicker-nav--title.-disabled-{cursor:default;background:none}
-
-.air-datepicker-buttons{display:grid;grid-auto-columns:1fr;grid-auto-flow:column}.air-datepicker-button{display:inline-flex;color:var(--adp-btn-color);border-radius:var(--adp-btn-border-radius);cursor:pointer;height:var(--adp-btn-height);border:none;background:rgba(255,255,255,0)}.air-datepicker-button:hover{color:var(--adp-btn-color-hover);background:var(--adp-btn-background-color-hover)}.air-datepicker-button:focus{color:var(--adp-btn-color-hover);background:var(--adp-btn-background-color-hover);outline:none}.air-datepicker-button:active{background:var(--adp-btn-background-color-active)}.air-datepicker-button span{outline:none;display:flex;align-items:center;justify-content:center;width:100%;height:100%}
-
-.air-datepicker-time{display:grid;grid-template-columns:-webkit-max-content 1fr;grid-template-columns:max-content 1fr;grid-column-gap:12px;align-items:center;position:relative;padding:0 var(--adp-time-padding-inner)}.-only-timepicker- .air-datepicker-time{border-top:none}.air-datepicker-time--current{display:flex;align-items:center;flex:1;font-size:14px;text-align:center}.air-datepicker-time--current-colon{margin:0 2px 3px;line-height:1}.air-datepicker-time--current-hours,.air-datepicker-time--current-minutes{line-height:1;font-size:19px;font-family:"Century Gothic", CenturyGothic, AppleGothic, sans-serif;position:relative;z-index:1}.air-datepicker-time--current-hours:after,.air-datepicker-time--current-minutes:after{content:'';background:var(--adp-background-color-hover);border-radius:var(--adp-border-radius);position:absolute;left:-2px;top:-3px;right:-2px;bottom:-2px;z-index:-1;opacity:0}.air-datepicker-time--current-hours.-focus-:after,.air-datepicker-time--current-minutes.-focus-:after{opacity:1}.air-datepicker-time--current-ampm{text-transform:uppercase;align-self:flex-end;color:var(--adp-time-day-period-color);margin-left:6px;font-size:11px;margin-bottom:1px}.air-datepicker-time--row{display:flex;align-items:center;font-size:11px;height:17px;background:linear-gradient(to right, var(--adp-time-track-color), var(--adp-time-track-color)) left 50%/100% var(--adp-time-track-height) no-repeat}.air-datepicker-time--row:first-child{margin-bottom:4px}.air-datepicker-time--row input[type='range']{background:none;cursor:pointer;flex:1;height:100%;width:100%;padding:0;margin:0;-webkit-appearance:none}.air-datepicker-time--row input[type='range']::-webkit-slider-thumb{-webkit-appearance:none}.air-datepicker-time--row input[type='range']::-ms-tooltip{display:none}.air-datepicker-time--row input[type='range']:hover::-webkit-slider-thumb{border-color:var(--adp-time-track-color-hover)}.air-datepicker-time--row input[type='range']:hover::-moz-range-thumb{border-color:var(--adp-time-track-color-hover)}.air-datepicker-time--row input[type='range']:hover::-ms-thumb{border-color:var(--adp-time-track-color-hover)}.air-datepicker-time--row input[type='range']:focus{outline:none}.air-datepicker-time--row input[type='range']:focus::-webkit-slider-thumb{background:var(--adp-cell-background-color-selected);border-color:var(--adp-cell-background-color-selected)}.air-datepicker-time--row input[type='range']:focus::-moz-range-thumb{background:var(--adp-cell-background-color-selected);border-color:var(--adp-cell-background-color-selected)}.air-datepicker-time--row input[type='range']:focus::-ms-thumb{background:var(--adp-cell-background-color-selected);border-color:var(--adp-cell-background-color-selected)}.air-datepicker-time--row input[type='range']::-webkit-slider-thumb{box-sizing:border-box;height:12px;width:12px;border-radius:3px;border:1px solid var(--adp-time-track-color);background:#fff;cursor:pointer;-webkit-transition:background var(--adp-transition-duration);transition:background var(--adp-transition-duration)}.air-datepicker-time--row input[type='range']::-moz-range-thumb{box-sizing:border-box;height:12px;width:12px;border-radius:3px;border:1px solid var(--adp-time-track-color);background:#fff;cursor:pointer;-moz-transition:background var(--adp-transition-duration);transition:background var(--adp-transition-duration)}.air-datepicker-time--row input[type='range']::-ms-thumb{box-sizing:border-box;height:12px;width:12px;border-radius:3px;border:1px solid var(--adp-time-track-color);background:#fff;cursor:pointer;-ms-transition:background var(--adp-transition-duration);transition:background var(--adp-transition-duration)}.air-datepicker-time--row input[type='range']::-webkit-slider-thumb{margin-top:calc(var(--adp-time-thumb-size) / 2 * -1)}.air-datepicker-time--row input[type='range']::-webkit-slider-runnable-track{border:none;height:var(--adp-time-track-height);cursor:pointer;color:transparent;background:transparent}.air-datepicker-time--row input[type='range']::-moz-range-track{border:none;height:var(--adp-time-track-height);cursor:pointer;color:transparent;background:transparent}.air-datepicker-time--row input[type='range']::-ms-track{border:none;height:var(--adp-time-track-height);cursor:pointer;color:transparent;background:transparent}.air-datepicker-time--row input[type='range']::-ms-fill-lower{background:transparent}.air-datepicker-time--row input[type='range']::-ms-fill-upper{background:transparent}
-
-.air-datepicker{--adp-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";--adp-font-size: 14px;--adp-width: 246px;--adp-z-index: 100;--adp-padding: 4px;--adp-grid-areas:
- 'nav'
- 'body'
- 'timepicker'
- 'buttons';--adp-transition-duration: .3s;--adp-transition-ease: ease-out;--adp-transition-offset: 8px;--adp-background-color: #fff;--adp-background-color-hover: #f0f0f0;--adp-background-color-active: #eaeaea;--adp-background-color-in-range: rgba(92, 196, 239, .1);--adp-background-color-in-range-focused: rgba(92, 196, 239, .2);--adp-background-color-selected-other-month-focused: #8ad5f4;--adp-background-color-selected-other-month: #a2ddf6;--adp-color: #4a4a4a;--adp-color-secondary: #9c9c9c;--adp-accent-color: #4eb5e6;--adp-color-current-date: var(--adp-accent-color);--adp-color-other-month: #dedede;--adp-color-disabled: #aeaeae;--adp-color-disabled-in-range: #939393;--adp-color-other-month-hover: #c5c5c5;--adp-border-color: #dbdbdb;--adp-border-color-inner: #efefef;--adp-border-radius: 4px;--adp-border-color-inline: #d7d7d7;--adp-nav-height: 32px;--adp-nav-arrow-color: var(--adp-color-secondary);--adp-nav-action-size: 32px;--adp-nav-color-secondary: var(--adp-color-secondary);--adp-day-name-color: #ff9a19;--adp-day-name-color-hover: #8ad5f4;--adp-day-cell-width: 1fr;--adp-day-cell-height: 32px;--adp-month-cell-height: 42px;--adp-year-cell-height: 56px;--adp-pointer-size: 10px;--adp-poiner-border-radius: 2px;--adp-pointer-offset: 14px;--adp-cell-border-radius: 4px;--adp-cell-background-color-hover: var(--adp-background-color-hover);--adp-cell-background-color-selected: #5cc4ef;--adp-cell-background-color-selected-hover: #45bced;--adp-cell-background-color-in-range: rgba(92, 196, 239, 0.1);--adp-cell-background-color-in-range-hover: rgba(92, 196, 239, 0.2);--adp-cell-border-color-in-range: var(--adp-cell-background-color-selected);--adp-btn-height: 32px;--adp-btn-color: var(--adp-accent-color);--adp-btn-color-hover: var(--adp-color);--adp-btn-border-radius: var(--adp-border-radius);--adp-btn-background-color-hover: var(--adp-background-color-hover);--adp-btn-background-color-active: var(--adp-background-color-active);--adp-time-track-height: 1px;--adp-time-track-color: #dedede;--adp-time-track-color-hover: #b1b1b1;--adp-time-thumb-size: 12px;--adp-time-padding-inner: 10px;--adp-time-day-period-color: var(--adp-color-secondary);--adp-mobile-font-size: 16px;--adp-mobile-nav-height: 40px;--adp-mobile-width: 320px;--adp-mobile-day-cell-height: 38px;--adp-mobile-month-cell-height: 48px;--adp-mobile-year-cell-height: 64px}.air-datepicker-overlay{--adp-overlay-background-color: rgba(0, 0, 0, .3);--adp-overlay-transition-duration: .3s;--adp-overlay-transition-ease: ease-out;--adp-overlay-z-index: 99}
-
-.air-datepicker{background:var(--adp-background-color);border:1px solid var(--adp-border-color);box-shadow:0 4px 12px rgba(0,0,0,0.15);border-radius:var(--adp-border-radius);box-sizing:content-box;display:grid;grid-template-columns:1fr;grid-template-rows:repeat(4, -webkit-max-content);grid-template-rows:repeat(4, max-content);grid-template-areas:var(--adp-grid-areas);font-family:var(--adp-font-family),sans-serif;font-size:var(--adp-font-size);color:var(--adp-color);width:var(--adp-width);position:absolute;transition:opacity var(--adp-transition-duration) var(--adp-transition-ease),transform var(--adp-transition-duration) var(--adp-transition-ease);z-index:var(--adp-z-index)}.air-datepicker:not(.-custom-position-){opacity:0}.air-datepicker.-from-top-{transform:translateY(calc(var(--adp-transition-offset) * -1))}.air-datepicker.-from-right-{transform:translateX(var(--adp-transition-offset))}.air-datepicker.-from-bottom-{transform:translateY(var(--adp-transition-offset))}.air-datepicker.-from-left-{transform:translateX(calc(var(--adp-transition-offset) * -1))}.air-datepicker.-active-:not(.-custom-position-){transform:translate(0, 0);opacity:1}.air-datepicker.-active-.-custom-position-{transition:none}.air-datepicker.-inline-{border-color:var(--adp-border-color-inline);box-shadow:none;position:static;left:auto;right:auto;opacity:1;transform:none}.air-datepicker.-inline- .air-datepicker--pointer{display:none}.air-datepicker.-is-mobile-{--adp-font-size: var(--adp-mobile-font-size);--adp-day-cell-height: var(--adp-mobile-day-cell-height);--adp-month-cell-height: var(--adp-mobile-month-cell-height);--adp-year-cell-height: var(--adp-mobile-year-cell-height);--adp-nav-height: var(--adp-mobile-nav-height);--adp-nav-action-size: var(--adp-mobile-nav-height);position:fixed;width:var(--adp-mobile-width);border:none}.air-datepicker.-is-mobile- *{-webkit-tap-highlight-color:transparent}.air-datepicker.-is-mobile- .air-datepicker--pointer{display:none}.air-datepicker.-is-mobile-:not(.-custom-position-){transform:translate(-50%, calc(-50% + var(--adp-transition-offset)))}.air-datepicker.-is-mobile-.-active-:not(.-custom-position-){transform:translate(-50%, -50%)}.air-datepicker.-custom-position-{transition:none}.air-datepicker-global-container{position:absolute;left:0;top:0}.air-datepicker--pointer{--pointer-half-size: calc(var(--adp-pointer-size) / 2);position:absolute;width:var(--adp-pointer-size);height:var(--adp-pointer-size);z-index:-1}.air-datepicker--pointer:after{content:'';position:absolute;background:#fff;border-top:1px solid var(--adp-border-color-inline);border-right:1px solid var(--adp-border-color-inline);border-top-right-radius:var(--adp-poiner-border-radius);width:var(--adp-pointer-size);height:var(--adp-pointer-size);box-sizing:border-box}.-top-left- .air-datepicker--pointer,.-top-center- .air-datepicker--pointer,.-top-right- .air-datepicker--pointer,[data-popper-placement^='top'] .air-datepicker--pointer{top:calc(100% - var(--pointer-half-size) + 1px)}.-top-left- .air-datepicker--pointer:after,.-top-center- .air-datepicker--pointer:after,.-top-right- .air-datepicker--pointer:after,[data-popper-placement^='top'] .air-datepicker--pointer:after{transform:rotate(135deg)}.-right-top- .air-datepicker--pointer,.-right-center- .air-datepicker--pointer,.-right-bottom- .air-datepicker--pointer,[data-popper-placement^='right'] .air-datepicker--pointer{right:calc(100% - var(--pointer-half-size) + 1px)}.-right-top- .air-datepicker--pointer:after,.-right-center- .air-datepicker--pointer:after,.-right-bottom- .air-datepicker--pointer:after,[data-popper-placement^='right'] .air-datepicker--pointer:after{transform:rotate(225deg)}.-bottom-left- .air-datepicker--pointer,.-bottom-center- .air-datepicker--pointer,.-bottom-right- .air-datepicker--pointer,[data-popper-placement^='bottom'] .air-datepicker--pointer{bottom:calc(100% - var(--pointer-half-size) + 1px)}.-bottom-left- .air-datepicker--pointer:after,.-bottom-center- .air-datepicker--pointer:after,.-bottom-right- .air-datepicker--pointer:after,[data-popper-placement^='bottom'] .air-datepicker--pointer:after{transform:rotate(315deg)}.-left-top- .air-datepicker--pointer,.-left-center- .air-datepicker--pointer,.-left-bottom- .air-datepicker--pointer,[data-popper-placement^='left'] .air-datepicker--pointer{left:calc(100% - var(--pointer-half-size) + 1px)}.-left-top- .air-datepicker--pointer:after,.-left-center- .air-datepicker--pointer:after,.-left-bottom- .air-datepicker--pointer:after,[data-popper-placement^='left'] .air-datepicker--pointer:after{transform:rotate(45deg)}.-top-left- .air-datepicker--pointer,.-bottom-left- .air-datepicker--pointer{left:var(--adp-pointer-offset)}.-top-right- .air-datepicker--pointer,.-bottom-right- .air-datepicker--pointer{right:var(--adp-pointer-offset)}.-top-center- .air-datepicker--pointer,.-bottom-center- .air-datepicker--pointer{left:calc(50% - var(--adp-pointer-size) / 2)}.-left-top- .air-datepicker--pointer,.-right-top- .air-datepicker--pointer{top:var(--adp-pointer-offset)}.-left-bottom- .air-datepicker--pointer,.-right-bottom- .air-datepicker--pointer{bottom:var(--adp-pointer-offset)}.-left-center- .air-datepicker--pointer,.-right-center- .air-datepicker--pointer{top:calc(50% - var(--adp-pointer-size) / 2)}.air-datepicker--navigation{grid-area:nav}.air-datepicker--content{box-sizing:content-box;padding:var(--adp-padding);grid-area:body}.-only-timepicker- .air-datepicker--content{display:none}.air-datepicker--time{grid-area:timepicker}.air-datepicker--buttons{grid-area:buttons}.air-datepicker--buttons,.air-datepicker--time{padding:var(--adp-padding);border-top:1px solid var(--adp-border-color-inner)}.air-datepicker-overlay{position:fixed;background:var(--adp-overlay-background-color);left:0;top:0;width:0;height:0;opacity:0;transition:opacity var(--adp-overlay-transition-duration) var(--adp-overlay-transition-ease),left 0s,height 0s,width 0s;transition-delay:0s,var(--adp-overlay-transition-duration),var(--adp-overlay-transition-duration),var(--adp-overlay-transition-duration);z-index:var(--adp-overlay-z-index)}.air-datepicker-overlay.-active-{opacity:1;width:100%;height:100%;transition:opacity var(--adp-overlay-transition-duration) var(--adp-overlay-transition-ease),height 0s,width 0s}
-
diff --git a/src/ui/static/css/dashboard.css b/src/ui/static/css/dashboard.css
index fc3671cdf..58b6f4c44 100644
--- a/src/ui/static/css/dashboard.css
+++ b/src/ui/static/css/dashboard.css
@@ -807,6 +807,10 @@ h6 {
z-index: 50;
}
+.-z-10 {
+ z-index: -10;
+}
+
.order-2 {
order: 2;
}
@@ -831,10 +835,6 @@ h6 {
grid-column: span 2 / span 2;
}
-.col-span-4 {
- grid-column: span 4 / span 4;
-}
-
.col-span-9 {
grid-column: span 9 / span 9;
}
@@ -912,11 +912,6 @@ h6 {
margin-bottom: 0.25rem;
}
-.my-auto {
- margin-top: auto;
- margin-bottom: auto;
-}
-
.my-4 {
margin-top: 1rem;
margin-bottom: 1rem;
@@ -927,9 +922,9 @@ h6 {
margin-right: 0.625rem;
}
-.mx-3 {
- margin-left: 0.75rem;
- margin-right: 0.75rem;
+.mx-4 {
+ margin-left: 1rem;
+ margin-right: 1rem;
}
.my-3 {
@@ -937,14 +932,19 @@ h6 {
margin-bottom: 0.75rem;
}
+.mx-3 {
+ margin-left: 0.75rem;
+ margin-right: 0.75rem;
+}
+
.my-5 {
margin-top: 1.25rem;
margin-bottom: 1.25rem;
}
-.mx-4 {
- margin-left: 1rem;
- margin-right: 1rem;
+.my-6 {
+ margin-top: 1.5rem;
+ margin-bottom: 1.5rem;
}
.mb-1 {
@@ -1019,10 +1019,18 @@ h6 {
margin-top: 0.25rem;
}
+.ml-6 {
+ margin-left: 1.5rem;
+}
+
.mb-4 {
margin-bottom: 1rem;
}
+.mt-2 {
+ margin-top: 0.5rem;
+}
+
.mt-0 {
margin-top: 0px;
}
@@ -1047,14 +1055,6 @@ h6 {
margin-top: 1.5rem;
}
-.mt-2 {
- margin-top: 0.5rem;
-}
-
-.mb-auto {
- margin-bottom: auto;
-}
-
.mr-6 {
margin-right: 1.5rem;
}
@@ -1071,14 +1071,14 @@ h6 {
margin-left: -1.75rem;
}
-.ml-6 {
- margin-left: 1.5rem;
-}
-
.mb-5 {
margin-bottom: 1.25rem;
}
+.mb-8 {
+ margin-bottom: 2rem;
+}
+
.box-content {
box-sizing: content-box;
}
@@ -1195,6 +1195,18 @@ h6 {
max-height: 25rem;
}
+.max-h-60 {
+ max-height: 15rem;
+}
+
+.max-h-16 {
+ max-height: 4rem;
+}
+
+.max-h-80 {
+ max-height: 20rem;
+}
+
.max-h-90 {
max-height: 22.5rem;
}
@@ -1203,6 +1215,14 @@ h6 {
max-height: 2rem;
}
+.max-h-30 {
+ max-height: 7.5rem;
+}
+
+.max-h-135 {
+ max-height: 33.75rem;
+}
+
.min-h-20 {
min-height: 5rem;
}
@@ -1223,8 +1243,12 @@ h6 {
min-height: 13rem;
}
-.min-h-75-screen {
- min-height: 75vh;
+.min-h-\[55vh\] {
+ min-height: 55vh;
+}
+
+.min-h-12 {
+ min-height: 3rem;
}
.min-h-75 {
@@ -1235,6 +1259,18 @@ h6 {
min-height: 85vh;
}
+.min-h-screen {
+ min-height: 100vh;
+}
+
+.min-h-90 {
+ min-height: 22.5rem;
+}
+
+.min-h-80 {
+ min-height: 20rem;
+}
+
.w-full {
width: 100%;
}
@@ -1307,6 +1343,26 @@ h6 {
min-width: 0px;
}
+.min-w-\[900\] {
+ min-width: 900;
+}
+
+.min-w-\[900px\] {
+ min-width: 900px;
+}
+
+.min-w-\[800px\] {
+ min-width: 800px;
+}
+
+.min-w-\[700px\] {
+ min-width: 700px;
+}
+
+.min-w-\[750px\] {
+ min-width: 750px;
+}
+
.max-w-screen-sm {
max-width: 576px;
}
@@ -1319,6 +1375,10 @@ h6 {
max-width: 10rem;
}
+.max-w-120 {
+ max-width: 30rem;
+}
+
.max-w-64 {
max-width: 16rem;
}
@@ -1335,6 +1395,22 @@ h6 {
max-width: 300px;
}
+.max-w-60 {
+ max-width: 15rem;
+}
+
+.max-w-\[460px\] {
+ max-width: 460px;
+}
+
+.max-w-\[40px\] {
+ max-width: 40px;
+}
+
+.max-w-\[400px\] {
+ max-width: 400px;
+}
+
.flex-auto {
flex: 1 1 auto;
}
@@ -1487,6 +1563,10 @@ h6 {
resize: both;
}
+.scroll-m-4 {
+ scroll-margin: 1rem;
+}
+
.list-none {
list-style-type: none;
}
@@ -1509,10 +1589,6 @@ h6 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
-.flex-row {
- flex-direction: row;
-}
-
.flex-col {
flex-direction: column;
}
@@ -1570,11 +1646,6 @@ h6 {
row-gap: 0.5rem;
}
-.gap-x-1 {
- -moz-column-gap: 0.25rem;
- column-gap: 0.25rem;
-}
-
.overflow-auto {
overflow: auto;
}
@@ -1583,6 +1654,14 @@ h6 {
overflow: hidden;
}
+.overflow-scroll {
+ overflow: scroll;
+}
+
+.overflow-x-auto {
+ overflow-x: auto;
+}
+
.overflow-y-auto {
overflow-y: auto;
}
@@ -1591,6 +1670,14 @@ h6 {
overflow-x: hidden;
}
+.overflow-x-scroll {
+ overflow-x: scroll;
+}
+
+.overflow-y-scroll {
+ overflow-y: scroll;
+}
+
.whitespace-nowrap {
white-space: nowrap;
}
@@ -1816,6 +1903,10 @@ h6 {
--tw-bg-opacity: 0;
}
+.bg-gradient-to-tl {
+ background-image: linear-gradient(to top left, var(--tw-gradient-stops));
+}
+
.bg-gradient-to-r {
background-image: linear-gradient(to right, var(--tw-gradient-stops));
}
@@ -1824,10 +1915,6 @@ h6 {
background-image: none;
}
-.bg-gradient-to-tl {
- background-image: linear-gradient(to top left, var(--tw-gradient-stops));
-}
-
.from-transparent {
--tw-gradient-from: transparent;
--tw-gradient-to: rgb(0 0 0 / 0);
@@ -1951,6 +2038,10 @@ h6 {
fill: #047857;
}
+.fill-amber-500 {
+ fill: #f59e0b;
+}
+
.fill-slate-800 {
fill: #3a416f;
}
@@ -1988,6 +2079,10 @@ h6 {
padding: 1.5rem;
}
+.p-12 {
+ padding: 3rem;
+}
+
.px-2 {
padding-left: 0.5rem;
padding-right: 0.5rem;
@@ -2073,6 +2168,16 @@ h6 {
padding-bottom: 1.25rem;
}
+.px-12 {
+ padding-left: 3rem;
+ padding-right: 3rem;
+}
+
+.py-16 {
+ padding-top: 4rem;
+ padding-bottom: 4rem;
+}
+
.pb-0 {
padding-bottom: 0px;
}
@@ -2109,10 +2214,6 @@ h6 {
padding-bottom: 0.5rem;
}
-.pt-5 {
- padding-top: 1.25rem;
-}
-
.pl-6 {
padding-left: 1.5rem;
}
@@ -2149,6 +2250,10 @@ h6 {
padding-left: 1.75rem;
}
+.pt-5 {
+ padding-top: 1.25rem;
+}
+
.text-left {
text-align: left;
}
@@ -2717,6 +2822,10 @@ h6 {
background-color: rgb(251 177 64 / 0.8);
}
+.hover\:bg-primary\/80:hover {
+ background-color: rgb(8 85 119 / 0.8);
+}
+
.hover\:bg-primary\/30:hover {
background-color: rgb(8 85 119 / 0.3);
}
@@ -2730,10 +2839,6 @@ h6 {
background-color: rgb(248 249 250 / var(--tw-bg-opacity));
}
-.hover\:bg-primary\/80:hover {
- background-color: rgb(8 85 119 / 0.8);
-}
-
.hover\:opacity-80:hover {
opacity: 0.8;
}
@@ -3065,10 +3170,23 @@ h6 {
grid-column: span 1 / span 1;
}
+ .sm\:col-span-2 {
+ grid-column: span 2 / span 2;
+ }
+
+ .sm\:col-span-8 {
+ grid-column: span 8 / span 8;
+ }
+
.sm\:col-start-5 {
grid-column-start: 5;
}
+ .sm\:my-0 {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ }
+
.sm\:mx-6 {
margin-left: 1.5rem;
margin-right: 1.5rem;
@@ -3087,6 +3205,10 @@ h6 {
margin-right: 4rem;
}
+ .sm\:block {
+ display: block;
+ }
+
.sm\:hidden {
display: none;
}
@@ -3107,6 +3229,22 @@ h6 {
max-height: 7rem;
}
+ .sm\:max-h-0 {
+ max-height: 0px;
+ }
+
+ .sm\:max-h-120 {
+ max-height: 30rem;
+ }
+
+ .sm\:max-h-135 {
+ max-height: 33.75rem;
+ }
+
+ .sm\:max-h-125 {
+ max-height: 31.25rem;
+ }
+
.sm\:w-80 {
width: 20rem;
}
@@ -3170,6 +3308,18 @@ h6 {
grid-column: span 6 / span 6;
}
+ .md\:col-span-5 {
+ grid-column: span 5 / span 5;
+ }
+
+ .md\:col-span-7 {
+ grid-column: span 7 / span 7;
+ }
+
+ .md\:col-span-1 {
+ grid-column: span 1 / span 1;
+ }
+
.md\:my-0 {
margin-top: 0px;
margin-bottom: 0px;
@@ -3209,10 +3359,18 @@ h6 {
display: none;
}
+ .md\:max-h-160 {
+ max-height: 40rem;
+ }
+
.md\:min-h-75-screen {
min-height: 75vh;
}
+ .md\:min-h-50-screen {
+ min-height: 50vh;
+ }
+
.md\:w-1\/2 {
width: 50%;
}
@@ -3221,6 +3379,11 @@ h6 {
justify-content: flex-end;
}
+ .md\:bg-white {
+ --tw-bg-opacity: 1;
+ background-color: rgb(255 255 255 / var(--tw-bg-opacity));
+ }
+
.md\:py-4 {
padding-top: 1rem;
padding-bottom: 1rem;
@@ -3248,6 +3411,10 @@ h6 {
}
@media (min-width: 992px) {
+ .lg\:relative {
+ position: relative;
+ }
+
.lg\:order-1 {
order: 1;
}
@@ -3268,16 +3435,16 @@ h6 {
grid-column: span 12 / span 12;
}
- .lg\:col-span-9 {
- grid-column: span 9 / span 9;
+ .lg\:col-span-8 {
+ grid-column: span 8 / span 8;
}
.lg\:col-span-3 {
grid-column: span 3 / span 3;
}
- .lg\:col-span-8 {
- grid-column: span 8 / span 8;
+ .lg\:col-span-1 {
+ grid-column: span 1 / span 1;
}
.lg\:mx-8 {
@@ -3298,6 +3465,18 @@ h6 {
margin-bottom: 0px;
}
+ .lg\:block {
+ display: block;
+ }
+
+ .lg\:flex {
+ display: flex;
+ }
+
+ .lg\:hidden {
+ display: none;
+ }
+
.lg\:h-9 {
height: 2.25rem;
}
@@ -3342,14 +3521,25 @@ h6 {
justify-content: space-between;
}
- .lg\:justify-items-start {
- justify-items: start;
- }
-
.lg\:gap-6 {
gap: 1.5rem;
}
+ .lg\:bg-white {
+ --tw-bg-opacity: 1;
+ background-color: rgb(255 255 255 / var(--tw-bg-opacity));
+ }
+
+ .lg\:bg-gray-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(235 239 244 / var(--tw-bg-opacity));
+ }
+
+ .lg\:bg-gray-50 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(248 249 250 / var(--tw-bg-opacity));
+ }
+
.lg\:text-left {
text-align: left;
}
@@ -3452,4 +3642,13 @@ h6 {
.\33xl\:col-span-4 {
grid-column: span 4 / span 4;
}
+
+ .\33xl\:col-span-2 {
+ grid-column: span 2 / span 2;
+ }
+}
+
+.\[\&\>\*\]\:bg-primary>* {
+ --tw-bg-opacity: 1;
+ background-color: rgb(8 85 119 / var(--tw-bg-opacity));
}
diff --git a/src/ui/static/css/datepicker-foundation.css b/src/ui/static/css/datepicker-foundation.css
new file mode 100644
index 000000000..eb16026cf
--- /dev/null
+++ b/src/ui/static/css/datepicker-foundation.css
@@ -0,0 +1,275 @@
+.datepicker {
+ display: none;
+}
+
+.datepicker.active {
+ display: block;
+}
+
+.datepicker-dropdown {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 10;
+ padding-top: 4px;
+}
+
+.datepicker-dropdown.datepicker-orient-top {
+ padding-top: 0;
+ padding-bottom: 4px;
+}
+
+.datepicker-picker {
+ display: inline-block;
+ border-radius: 0;
+ background-color: #fefefe;
+}
+
+.datepicker-dropdown .datepicker-picker {
+ box-shadow: 0 0 0 1px #cacaca;
+}
+
+.datepicker-picker span {
+ display: block;
+ flex: 1;
+ border: 0;
+ border-radius: 0;
+ cursor: default;
+ text-align: center;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.datepicker-main {
+ padding: 2px;
+}
+
+.datepicker-footer {
+ box-shadow: inset 0 1px 1px rgba(10, 10, 10, 0.1);
+ background-color: #e6e6e6;
+}
+
+.datepicker-grid,
+.datepicker-view .days-of-week,
+.datepicker-view,
+.datepicker-controls {
+ display: flex;
+}
+
+.datepicker-grid {
+ flex-wrap: wrap;
+}
+
+.datepicker-view .days .datepicker-cell,
+.datepicker-view .dow {
+ flex-basis: 14.2857142857%;
+}
+
+.datepicker-view.datepicker-grid .datepicker-cell {
+ flex-basis: 25%;
+}
+
+.datepicker-cell,
+.datepicker-view .week {
+ height: 2.25rem;
+ line-height: 2.25rem;
+}
+
+.datepicker-title {
+ box-shadow: inset 0 -1px 1px rgba(10, 10, 10, 0.1);
+ background-color: #e6e6e6;
+ padding: 0.375rem 0.75rem;
+ text-align: center;
+ font-weight: bold;
+}
+
+.datepicker-header .datepicker-controls {
+ padding: 2px 2px 0;
+}
+
+.datepicker-controls .button {
+ margin: 0;
+ background-color: #fefefe;
+ color: #0a0a0a;
+}
+
+.datepicker-controls .button:hover,
+.datepicker-controls .button:focus {
+ background-color: #d8d8d8;
+}
+
+.datepicker-controls .button:hover[disabled],
+.datepicker-controls .button:focus[disabled] {
+ opacity: 0.25;
+ background-color: #fefefe;
+ color: #0a0a0a;
+}
+
+.datepicker-header .datepicker-controls .button {
+ border-color: transparent;
+ font-weight: bold;
+}
+
+.datepicker-footer .datepicker-controls .button {
+ margin: calc(0.375rem - 1px) 0.375rem;
+ border-radius: 0;
+ width: 100%;
+ font-size: 0.75rem;
+}
+
+.datepicker-controls .view-switch {
+ flex: auto;
+}
+
+.datepicker-controls .prev-btn,
+.datepicker-controls .next-btn {
+ padding-right: 0.375rem;
+ padding-left: 0.375rem;
+ width: 2.25rem;
+}
+
+.datepicker-controls .prev-btn.disabled,
+.datepicker-controls .next-btn.disabled {
+ visibility: hidden;
+}
+
+.datepicker-view .dow {
+ height: 1.5rem;
+ line-height: 1.5rem;
+ font-size: 0.875rem;
+ font-weight: bold;
+}
+
+.datepicker-view .week {
+ width: 2.25rem;
+ color: #8a8a8a;
+ font-size: 0.75rem;
+}
+
+@media (max-width: 22.5rem) {
+ .datepicker-view .week {
+ width: 1.96875rem;
+ }
+}
+
+.datepicker-grid {
+ width: 15.75rem;
+}
+
+@media (max-width: 22.5rem) {
+ .calendar-weeks + .days .datepicker-grid {
+ width: 13.78125rem;
+ }
+}
+
+.datepicker-cell:not(.disabled):hover {
+ background-color: #f8f8f8;
+ cursor: pointer;
+}
+
+.datepicker-cell.focused:not(.selected) {
+ background-color: #f1f1f1;
+}
+
+.datepicker-cell.selected,
+.datepicker-cell.selected:hover {
+ background-color: #1779ba;
+ color: #fefefe;
+ font-weight: semibold;
+}
+
+.datepicker-cell.disabled {
+ color: #e6e6e6;
+}
+
+.datepicker-cell.prev:not(.disabled),
+.datepicker-cell.next:not(.disabled) {
+ color: #cacaca;
+}
+
+.datepicker-cell.prev.selected,
+.datepicker-cell.next.selected {
+ color: #e5e5e5;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today) {
+ border-radius: 0;
+ background-color: #f7f7f7;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today):not(.disabled):hover {
+ background-color: #f1f1f1;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today).focused {
+ background-color: #f1f1f1;
+}
+
+.datepicker-cell.today:not(.selected) {
+ background-color: #d7ecfa;
+}
+
+.datepicker-cell.today:not(.selected):not(.disabled) {
+ color: #8a8a8a;
+}
+
+.datepicker-cell.today.focused:not(.selected) {
+ background-color: #cbe7f9;
+}
+
+.datepicker-cell.range-end:not(.selected),
+.datepicker-cell.range-start:not(.selected) {
+ background-color: #767676;
+ color: #fefefe;
+}
+
+.datepicker-cell.range-end.focused:not(.selected),
+.datepicker-cell.range-start.focused:not(.selected) {
+ background-color: #707070;
+}
+
+.datepicker-cell.range-start {
+ border-radius: 0 0 0 0;
+}
+
+.datepicker-cell.range-end {
+ border-radius: 0 0 0 0;
+}
+
+.datepicker-cell.range {
+ border-radius: 0;
+ background-color: #e6e6e6;
+}
+
+.datepicker-cell.range:not(.disabled):not(.focused):not(.today):hover {
+ background-color: #e0e0e0;
+}
+
+.datepicker-cell.range.disabled {
+ color: #cdcdcd;
+}
+
+.datepicker-cell.range.focused {
+ background-color: #d9d9d9;
+}
+
+.datepicker-cell.range.today {
+ background-color: #b3dbf6;
+}
+
+.datepicker-view.datepicker-grid .datepicker-cell {
+ height: 4.5rem;
+ line-height: 4.5rem;
+}
+
+.datepicker-input.in-edit {
+ border-color: #a4a4a4;
+}
+
+.datepicker-input.in-edit:focus,
+.datepicker-input.in-edit:active {
+ box-shadow: 0 0 0.25em 0.25em rgba(164, 164, 164, 0.2);
+}
\ No newline at end of file
diff --git a/src/ui/static/css/datepicker-foundation.min.css b/src/ui/static/css/datepicker-foundation.min.css
new file mode 100644
index 000000000..8465be876
--- /dev/null
+++ b/src/ui/static/css/datepicker-foundation.min.css
@@ -0,0 +1 @@
+.datepicker{display:none}.datepicker.active{display:block}.datepicker-dropdown{left:0;padding-top:4px;position:absolute;top:0;z-index:10}.datepicker-dropdown.datepicker-orient-top{padding-bottom:4px;padding-top:0}.datepicker-picker{background-color:#fefefe;border-radius:0;display:inline-block}.datepicker-dropdown .datepicker-picker{box-shadow:0 0 0 1px #cacaca}.datepicker-picker span{-webkit-touch-callout:none;border:0;border-radius:0;cursor:default;display:block;flex:1;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.datepicker-main{padding:2px}.datepicker-footer{background-color:#e6e6e6;box-shadow:inset 0 1px 1px hsla(0,0%,4%,.1)}.datepicker-controls,.datepicker-grid,.datepicker-view,.datepicker-view .days-of-week{display:flex}.datepicker-grid{flex-wrap:wrap}.datepicker-view .days .datepicker-cell,.datepicker-view .dow{flex-basis:14.2857142857%}.datepicker-view.datepicker-grid .datepicker-cell{flex-basis:25%}.datepicker-cell,.datepicker-view .week{height:2.25rem;line-height:2.25rem}.datepicker-title{background-color:#e6e6e6;box-shadow:inset 0 -1px 1px hsla(0,0%,4%,.1);font-weight:700;padding:.375rem .75rem;text-align:center}.datepicker-header .datepicker-controls{padding:2px 2px 0}.datepicker-controls .button{background-color:#fefefe;color:#0a0a0a;margin:0}.datepicker-controls .button:focus,.datepicker-controls .button:hover{background-color:#d8d8d8}.datepicker-controls .button:focus[disabled],.datepicker-controls .button:hover[disabled]{background-color:#fefefe;color:#0a0a0a;opacity:.25}.datepicker-header .datepicker-controls .button{border-color:transparent;font-weight:700}.datepicker-footer .datepicker-controls .button{border-radius:0;font-size:.75rem;margin:calc(.375rem - 1px) .375rem;width:100%}.datepicker-controls .view-switch{flex:auto}.datepicker-controls .next-btn,.datepicker-controls .prev-btn{padding-left:.375rem;padding-right:.375rem;width:2.25rem}.datepicker-controls .next-btn.disabled,.datepicker-controls .prev-btn.disabled{visibility:hidden}.datepicker-view .dow{font-size:.875rem;font-weight:700;height:1.5rem;line-height:1.5rem}.datepicker-view .week{color:#8a8a8a;font-size:.75rem;width:2.25rem}@media (max-width:22.5rem){.datepicker-view .week{width:1.96875rem}}.datepicker-grid{width:15.75rem}@media (max-width:22.5rem){.calendar-weeks+.days .datepicker-grid{width:13.78125rem}}.datepicker-cell:not(.disabled):hover{background-color:#f8f8f8;cursor:pointer}.datepicker-cell.focused:not(.selected){background-color:#f1f1f1}.datepicker-cell.selected,.datepicker-cell.selected:hover{background-color:#1779ba;color:#fefefe;font-weight:semibold}.datepicker-cell.disabled{color:#e6e6e6}.datepicker-cell.next:not(.disabled),.datepicker-cell.prev:not(.disabled){color:#cacaca}.datepicker-cell.next.selected,.datepicker-cell.prev.selected{color:#e5e5e5}.datepicker-cell.highlighted:not(.selected):not(.range):not(.today){background-color:#f7f7f7;border-radius:0}.datepicker-cell.highlighted:not(.selected):not(.range):not(.today).focused,.datepicker-cell.highlighted:not(.selected):not(.range):not(.today):not(.disabled):hover{background-color:#f1f1f1}.datepicker-cell.today:not(.selected){background-color:#d7ecfa}.datepicker-cell.today:not(.selected):not(.disabled){color:#8a8a8a}.datepicker-cell.today.focused:not(.selected){background-color:#cbe7f9}.datepicker-cell.range-end:not(.selected),.datepicker-cell.range-start:not(.selected){background-color:#767676;color:#fefefe}.datepicker-cell.range-end.focused:not(.selected),.datepicker-cell.range-start.focused:not(.selected){background-color:#707070}.datepicker-cell.range-end,.datepicker-cell.range-start{border-radius:0 0 0 0}.datepicker-cell.range{background-color:#e6e6e6;border-radius:0}.datepicker-cell.range:not(.disabled):not(.focused):not(.today):hover{background-color:#e0e0e0}.datepicker-cell.range.disabled{color:#cdcdcd}.datepicker-cell.range.focused{background-color:#d9d9d9}.datepicker-cell.range.today{background-color:#b3dbf6}.datepicker-view.datepicker-grid .datepicker-cell{height:4.5rem;line-height:4.5rem}.datepicker-input.in-edit{border-color:#a4a4a4}.datepicker-input.in-edit:active,.datepicker-input.in-edit:focus{box-shadow:0 0 .25em .25em hsla(0,0%,64%,.2)}
\ No newline at end of file
diff --git a/src/ui/static/css/datepicker.css b/src/ui/static/css/datepicker.css
new file mode 100644
index 000000000..e51ff9c14
--- /dev/null
+++ b/src/ui/static/css/datepicker.css
@@ -0,0 +1,318 @@
+.datepicker {
+ display: none;
+}
+
+.datepicker.active {
+ display: block;
+}
+
+.datepicker-dropdown {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 20;
+ padding-top: 4px;
+}
+
+.datepicker-dropdown.datepicker-orient-top {
+ padding-top: 0;
+ padding-bottom: 4px;
+}
+
+.datepicker-picker {
+ display: inline-block;
+ border-radius: 4px;
+ background-color: white;
+}
+
+.datepicker-dropdown .datepicker-picker {
+ box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
+}
+
+.datepicker-picker span {
+ display: block;
+ flex: 1;
+ border: 0;
+ border-radius: 4px;
+ cursor: default;
+ text-align: center;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.datepicker-main {
+ padding: 2px;
+}
+
+.datepicker-footer {
+ box-shadow: inset 0 1px 1px rgba(10, 10, 10, 0.1);
+ background-color: whitesmoke;
+}
+
+.datepicker-grid,
+.datepicker-view .days-of-week,
+.datepicker-view,
+.datepicker-controls {
+ display: flex;
+}
+
+.datepicker-grid {
+ flex-wrap: wrap;
+}
+
+.datepicker-view .days .datepicker-cell,
+.datepicker-view .dow {
+ flex-basis: 14.2857142857%;
+}
+
+.datepicker-view.datepicker-grid .datepicker-cell {
+ flex-basis: 25%;
+}
+
+.datepicker-cell,
+.datepicker-view .week {
+ height: 2.25rem;
+ line-height: 2.25rem;
+}
+
+.datepicker-title {
+ box-shadow: inset 0 -1px 1px rgba(10, 10, 10, 0.1);
+ background-color: whitesmoke;
+ padding: 0.375rem 0.75rem;
+ text-align: center;
+ font-weight: 700;
+}
+
+.datepicker-header .datepicker-controls {
+ padding: 2px 2px 0;
+}
+
+.datepicker-controls .button {
+ display: inline-flex;
+ position: relative;
+ align-items: center;
+ justify-content: center;
+ margin: 0;
+ border: 1px solid #dbdbdb;
+ border-radius: 4px;
+ box-shadow: none;
+ background-color: white;
+ cursor: pointer;
+ padding: calc(0.375em - 1px) 0.75em;
+ height: 2.25em;
+ vertical-align: top;
+ text-align: center;
+ line-height: 1.5;
+ white-space: nowrap;
+ color: #363636;
+ font-size: 1rem;
+}
+
+.datepicker-controls .button:focus,
+.datepicker-controls .button:active {
+ outline: none;
+}
+
+.datepicker-controls .button:hover {
+ border-color: #b5b5b5;
+ color: #363636;
+}
+
+.datepicker-controls .button:focus {
+ border-color: #3273dc;
+ color: #363636;
+}
+
+.datepicker-controls .button:focus:not(:active) {
+ box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25);
+}
+
+.datepicker-controls .button:active {
+ border-color: #4a4a4a;
+ color: #363636;
+}
+
+.datepicker-controls .button[disabled] {
+ cursor: not-allowed;
+}
+
+.datepicker-header .datepicker-controls .button {
+ border-color: transparent;
+ font-weight: bold;
+}
+
+.datepicker-header .datepicker-controls .button:hover {
+ background-color: #f9f9f9;
+}
+
+.datepicker-header .datepicker-controls .button:focus:not(:active) {
+ box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25);
+}
+
+.datepicker-header .datepicker-controls .button:active {
+ background-color: #f2f2f2;
+}
+
+.datepicker-header .datepicker-controls .button[disabled] {
+ box-shadow: none;
+}
+
+.datepicker-footer .datepicker-controls .button {
+ margin: calc(0.375rem - 1px) 0.375rem;
+ border-radius: 2px;
+ width: 100%;
+ font-size: 0.75rem;
+}
+
+.datepicker-controls .view-switch {
+ flex: auto;
+}
+
+.datepicker-controls .prev-btn,
+.datepicker-controls .next-btn {
+ padding-right: 0.375rem;
+ padding-left: 0.375rem;
+ width: 2.25rem;
+}
+
+.datepicker-controls .prev-btn.disabled,
+.datepicker-controls .next-btn.disabled {
+ visibility: hidden;
+}
+
+.datepicker-view .dow {
+ height: 1.5rem;
+ line-height: 1.5rem;
+ font-size: 0.875rem;
+ font-weight: 700;
+}
+
+.datepicker-view .week {
+ width: 2.25rem;
+ color: #b5b5b5;
+ font-size: 0.75rem;
+}
+
+@media (max-width: 22.5rem) {
+ .datepicker-view .week {
+ width: 1.96875rem;
+ }
+}
+
+.datepicker-grid {
+ width: 15.75rem;
+}
+
+@media (max-width: 22.5rem) {
+ .calendar-weeks + .days .datepicker-grid {
+ width: 13.78125rem;
+ }
+}
+
+.datepicker-cell:not(.disabled):hover {
+ background-color: #f9f9f9;
+ cursor: pointer;
+}
+
+.datepicker-cell.focused:not(.selected) {
+ background-color: #e8e8e8;
+}
+
+.datepicker-cell.selected,
+.datepicker-cell.selected:hover {
+ background-color: #3273dc;
+ color: #fff;
+ font-weight: 600;
+}
+
+.datepicker-cell.disabled {
+ color: #dbdbdb;
+}
+
+.datepicker-cell.prev:not(.disabled),
+.datepicker-cell.next:not(.disabled) {
+ color: #7a7a7a;
+}
+
+.datepicker-cell.prev.selected,
+.datepicker-cell.next.selected {
+ color: #e6e6e6;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today) {
+ border-radius: 0;
+ background-color: whitesmoke;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today):not(.disabled):hover {
+ background-color: #eeeeee;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today).focused {
+ background-color: #e8e8e8;
+}
+
+.datepicker-cell.today:not(.selected) {
+ background-color: #00d1b2;
+}
+
+.datepicker-cell.today:not(.selected):not(.disabled) {
+ color: #fff;
+}
+
+.datepicker-cell.today.focused:not(.selected) {
+ background-color: #00c4a7;
+}
+
+.datepicker-cell.range-end:not(.selected),
+.datepicker-cell.range-start:not(.selected) {
+ background-color: #b5b5b5;
+ color: #fff;
+}
+
+.datepicker-cell.range-end.focused:not(.selected),
+.datepicker-cell.range-start.focused:not(.selected) {
+ background-color: #afafaf;
+}
+
+.datepicker-cell.range-start {
+ border-radius: 4px 0 0 4px;
+}
+
+.datepicker-cell.range-end {
+ border-radius: 0 4px 4px 0;
+}
+
+.datepicker-cell.range {
+ border-radius: 0;
+ background-color: #dbdbdb;
+}
+
+.datepicker-cell.range:not(.disabled):not(.focused):not(.today):hover {
+ background-color: #d5d5d5;
+}
+
+.datepicker-cell.range.disabled {
+ color: #c2c2c2;
+}
+
+.datepicker-cell.range.focused {
+ background-color: #cfcfcf;
+}
+
+.datepicker-view.datepicker-grid .datepicker-cell {
+ height: 4.5rem;
+ line-height: 4.5rem;
+}
+
+.datepicker-input.in-edit {
+ border-color: #2366d1;
+}
+
+.datepicker-input.in-edit:focus,
+.datepicker-input.in-edit:active {
+ box-shadow: 0 0 0.25em 0.25em rgba(35, 102, 209, 0.2);
+}
\ No newline at end of file
diff --git a/src/ui/static/css/datepicker.min.css b/src/ui/static/css/datepicker.min.css
new file mode 100644
index 000000000..c2cfa6802
--- /dev/null
+++ b/src/ui/static/css/datepicker.min.css
@@ -0,0 +1 @@
+.datepicker{display:none}.datepicker.active{display:block}.datepicker-dropdown{left:0;padding-top:4px;position:absolute;top:0;z-index:20}.datepicker-dropdown.datepicker-orient-top{padding-bottom:4px;padding-top:0}.datepicker-picker{background-color:#fff;border-radius:4px;display:inline-block}.datepicker-dropdown .datepicker-picker{box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1)}.datepicker-picker span{-webkit-touch-callout:none;border:0;border-radius:4px;cursor:default;display:block;flex:1;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.datepicker-main{padding:2px}.datepicker-footer{background-color:#f5f5f5;box-shadow:inset 0 1px 1px hsla(0,0%,4%,.1)}.datepicker-controls,.datepicker-grid,.datepicker-view,.datepicker-view .days-of-week{display:flex}.datepicker-grid{flex-wrap:wrap}.datepicker-view .days .datepicker-cell,.datepicker-view .dow{flex-basis:14.2857142857%}.datepicker-view.datepicker-grid .datepicker-cell{flex-basis:25%}.datepicker-cell,.datepicker-view .week{height:2.25rem;line-height:2.25rem}.datepicker-title{background-color:#f5f5f5;box-shadow:inset 0 -1px 1px hsla(0,0%,4%,.1);font-weight:700;padding:.375rem .75rem;text-align:center}.datepicker-header .datepicker-controls{padding:2px 2px 0}.datepicker-controls .button{align-items:center;background-color:#fff;border:1px solid #dbdbdb;border-radius:4px;box-shadow:none;color:#363636;cursor:pointer;display:inline-flex;font-size:1rem;height:2.25em;justify-content:center;line-height:1.5;margin:0;padding:calc(.375em - 1px) .75em;position:relative;text-align:center;vertical-align:top;white-space:nowrap}.datepicker-controls .button:active,.datepicker-controls .button:focus{outline:none}.datepicker-controls .button:hover{border-color:#b5b5b5;color:#363636}.datepicker-controls .button:focus{border-color:#3273dc;color:#363636}.datepicker-controls .button:focus:not(:active){box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.datepicker-controls .button:active{border-color:#4a4a4a;color:#363636}.datepicker-controls .button[disabled]{cursor:not-allowed}.datepicker-header .datepicker-controls .button{border-color:transparent;font-weight:700}.datepicker-header .datepicker-controls .button:hover{background-color:#f9f9f9}.datepicker-header .datepicker-controls .button:focus:not(:active){box-shadow:0 0 0 .125em hsla(0,0%,100%,.25)}.datepicker-header .datepicker-controls .button:active{background-color:#f2f2f2}.datepicker-header .datepicker-controls .button[disabled]{box-shadow:none}.datepicker-footer .datepicker-controls .button{border-radius:2px;font-size:.75rem;margin:calc(.375rem - 1px) .375rem;width:100%}.datepicker-controls .view-switch{flex:auto}.datepicker-controls .next-btn,.datepicker-controls .prev-btn{padding-left:.375rem;padding-right:.375rem;width:2.25rem}.datepicker-controls .next-btn.disabled,.datepicker-controls .prev-btn.disabled{visibility:hidden}.datepicker-view .dow{font-size:.875rem;font-weight:700;height:1.5rem;line-height:1.5rem}.datepicker-view .week{color:#b5b5b5;font-size:.75rem;width:2.25rem}@media (max-width:22.5rem){.datepicker-view .week{width:1.96875rem}}.datepicker-grid{width:15.75rem}@media (max-width:22.5rem){.calendar-weeks+.days .datepicker-grid{width:13.78125rem}}.datepicker-cell:not(.disabled):hover{background-color:#f9f9f9;cursor:pointer}.datepicker-cell.focused:not(.selected){background-color:#e8e8e8}.datepicker-cell.selected,.datepicker-cell.selected:hover{background-color:#3273dc;color:#fff;font-weight:600}.datepicker-cell.disabled{color:#dbdbdb}.datepicker-cell.next:not(.disabled),.datepicker-cell.prev:not(.disabled){color:#7a7a7a}.datepicker-cell.next.selected,.datepicker-cell.prev.selected{color:#e6e6e6}.datepicker-cell.highlighted:not(.selected):not(.range):not(.today){background-color:#f5f5f5;border-radius:0}.datepicker-cell.highlighted:not(.selected):not(.range):not(.today):not(.disabled):hover{background-color:#eee}.datepicker-cell.highlighted:not(.selected):not(.range):not(.today).focused{background-color:#e8e8e8}.datepicker-cell.today:not(.selected){background-color:#00d1b2}.datepicker-cell.today:not(.selected):not(.disabled){color:#fff}.datepicker-cell.today.focused:not(.selected){background-color:#00c4a7}.datepicker-cell.range-end:not(.selected),.datepicker-cell.range-start:not(.selected){background-color:#b5b5b5;color:#fff}.datepicker-cell.range-end.focused:not(.selected),.datepicker-cell.range-start.focused:not(.selected){background-color:#afafaf}.datepicker-cell.range-start{border-radius:4px 0 0 4px}.datepicker-cell.range-end{border-radius:0 4px 4px 0}.datepicker-cell.range{background-color:#dbdbdb;border-radius:0}.datepicker-cell.range:not(.disabled):not(.focused):not(.today):hover{background-color:#d5d5d5}.datepicker-cell.range.disabled{color:#c2c2c2}.datepicker-cell.range.focused{background-color:#cfcfcf}.datepicker-view.datepicker-grid .datepicker-cell{height:4.5rem;line-height:4.5rem}.datepicker-input.in-edit{border-color:#2366d1}.datepicker-input.in-edit:active,.datepicker-input.in-edit:focus{box-shadow:0 0 .25em .25em rgba(35,102,209,.2)}
\ No newline at end of file
diff --git a/src/ui/static/css/dropzone/basic.css b/src/ui/static/css/dropzone/basic.css
deleted file mode 100644
index f8be93ce1..000000000
--- a/src/ui/static/css/dropzone/basic.css
+++ /dev/null
@@ -1 +0,0 @@
-.dropzone,.dropzone *{box-sizing:border-box}.dropzone{position:relative}.dropzone .dz-preview{position:relative;display:inline-block;width:120px;margin:.5em}.dropzone .dz-preview .dz-progress{display:block;height:15px;border:1px solid #aaa}.dropzone .dz-preview .dz-progress .dz-upload{display:block;height:100%;width:0;background:green}.dropzone .dz-preview .dz-error-message{color:red;display:none}.dropzone .dz-preview.dz-error .dz-error-message,.dropzone .dz-preview.dz-error .dz-error-mark{display:block}.dropzone .dz-preview.dz-success .dz-success-mark{display:block}.dropzone .dz-preview .dz-error-mark,.dropzone .dz-preview .dz-success-mark{position:absolute;display:none;left:30px;top:30px;width:54px;height:58px;left:50%;margin-left:-27px}/*# sourceMappingURL=basic.css.map */
diff --git a/src/ui/static/css/dropzone/dropzone.css b/src/ui/static/css/dropzone/dropzone.css
index 569ac4484..60f2be9e1 100644
--- a/src/ui/static/css/dropzone/dropzone.css
+++ b/src/ui/static/css/dropzone/dropzone.css
@@ -1 +1,274 @@
-@keyframes passing-through{0%{opacity:0;transform:translateY(40px)}30%,70%{opacity:1;transform:translateY(0px)}100%{opacity:0;transform:translateY(-40px)}}@keyframes slide-in{0%{opacity:0;transform:translateY(40px)}30%{opacity:1;transform:translateY(0px)}}@keyframes pulse{0%{transform:scale(1)}10%{transform:scale(1.1)}20%{transform:scale(1)}}.dropzone,.dropzone *{box-sizing:border-box}.dropzone{min-height:150px;border:1px solid rgba(0,0,0,.8);border-radius:5px;padding:20px 20px}.dropzone.dz-clickable{cursor:pointer}.dropzone.dz-clickable *{cursor:default}.dropzone.dz-clickable .dz-message,.dropzone.dz-clickable .dz-message *{cursor:pointer}.dropzone.dz-started .dz-message{display:none}.dropzone.dz-drag-hover{border-style:solid}.dropzone.dz-drag-hover .dz-message{opacity:.5}.dropzone .dz-message{text-align:center;margin:3em 0}.dropzone .dz-message .dz-button{background:none;color:inherit;border:none;padding:0;font:inherit;cursor:pointer;outline:inherit}.dropzone .dz-preview{position:relative;display:inline-block;vertical-align:top;margin:16px;min-height:100px}.dropzone .dz-preview:hover{z-index:1000}.dropzone .dz-preview:hover .dz-details{opacity:1}.dropzone .dz-preview.dz-file-preview .dz-image{border-radius:20px;background:#999;background:linear-gradient(to bottom, #eee, #ddd)}.dropzone .dz-preview.dz-file-preview .dz-details{opacity:1}.dropzone .dz-preview.dz-image-preview{background:#fff}.dropzone .dz-preview.dz-image-preview .dz-details{transition:opacity .2s linear}.dropzone .dz-preview .dz-remove{font-size:14px;text-align:center;display:block;cursor:pointer;border:none}.dropzone .dz-preview .dz-remove:hover{text-decoration:underline}.dropzone .dz-preview:hover .dz-details{opacity:1}.dropzone .dz-preview .dz-details{z-index:20;position:absolute;top:0;left:0;opacity:0;font-size:13px;min-width:100%;max-width:100%;padding:2em 1em;text-align:center;color:rgba(0,0,0,.9);line-height:150%}.dropzone .dz-preview .dz-details .dz-size{margin-bottom:1em;font-size:16px}.dropzone .dz-preview .dz-details .dz-filename{white-space:nowrap}.dropzone .dz-preview .dz-details .dz-filename:hover span{border:1px solid rgba(200,200,200,.8);background-color:rgba(255,255,255,.8)}.dropzone .dz-preview .dz-details .dz-filename:not(:hover){overflow:hidden;text-overflow:ellipsis}.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span{border:1px solid transparent}.dropzone .dz-preview .dz-details .dz-filename span,.dropzone .dz-preview .dz-details .dz-size span{background-color:rgba(255,255,255,.4);padding:0 .4em;border-radius:3px}.dropzone .dz-preview:hover .dz-image img{transform:scale(1.05, 1.05);filter:blur(8px)}.dropzone .dz-preview .dz-image{border-radius:20px;overflow:hidden;width:120px;height:120px;position:relative;display:block;z-index:10}.dropzone .dz-preview .dz-image img{display:block}.dropzone .dz-preview.dz-success .dz-success-mark{animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1)}.dropzone .dz-preview.dz-error .dz-error-mark{opacity:1;animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1)}.dropzone .dz-preview .dz-success-mark,.dropzone .dz-preview .dz-error-mark{pointer-events:none;opacity:0;z-index:500;position:absolute;display:block;top:50%;left:50%;margin-left:-27px;margin-top:-27px;background:rgba(0,0,0,.8);border-radius:50%}.dropzone .dz-preview .dz-success-mark svg,.dropzone .dz-preview .dz-error-mark svg{display:block;width:54px;height:54px;fill:#fff}.dropzone .dz-preview.dz-processing .dz-progress{opacity:1;transition:all .2s linear}.dropzone .dz-preview.dz-complete .dz-progress{opacity:0;transition:opacity .4s ease-in}.dropzone .dz-preview:not(.dz-processing) .dz-progress{animation:pulse 6s ease infinite}.dropzone .dz-preview .dz-progress{opacity:1;z-index:1000;pointer-events:none;position:absolute;height:20px;top:50%;margin-top:-10px;left:15%;right:15%;border:3px solid rgba(0,0,0,.8);background:rgba(0,0,0,.8);border-radius:10px;overflow:hidden}.dropzone .dz-preview .dz-progress .dz-upload{background:#fff;display:block;position:relative;height:100%;width:0;transition:width 300ms ease-in-out;border-radius:17px}.dropzone .dz-preview.dz-error .dz-error-message{display:block}.dropzone .dz-preview.dz-error:hover .dz-error-message{opacity:1;pointer-events:auto}.dropzone .dz-preview .dz-error-message{pointer-events:none;z-index:1000;position:absolute;display:block;display:none;opacity:0;transition:opacity .3s ease;border-radius:8px;font-size:13px;top:130px;left:-10px;width:140px;background:#b10606;padding:.5em 1em;color:#fff}.dropzone .dz-preview .dz-error-message:after{content:"";position:absolute;top:-6px;left:64px;width:0;height:0;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #b10606}/*# sourceMappingURL=dropzone.css.map */
+/*# sourceMappingURL=basic.css.map */
+@keyframes passing-through {
+ 0% {
+ opacity: 0;
+ transform: translateY(40px);
+ }
+ 30%,
+ 70% {
+ opacity: 1;
+ transform: translateY(0px);
+ }
+ 100% {
+ opacity: 0;
+ transform: translateY(-40px);
+ }
+}
+@keyframes slide-in {
+ 0% {
+ opacity: 0;
+ transform: translateY(40px);
+ }
+ 30% {
+ opacity: 1;
+ transform: translateY(0px);
+ }
+}
+@keyframes pulse {
+ 0% {
+ transform: scale(1);
+ }
+ 10% {
+ transform: scale(1.1);
+ }
+ 20% {
+ transform: scale(1);
+ }
+}
+.dropzone,
+.dropzone * {
+ box-sizing: border-box;
+}
+.dropzone {
+ min-height: 100px;
+ border: 1px solid rgba(0, 0, 0, 0.8);
+ border-radius: 5px;
+ padding: 0;
+}
+.dropzone.dz-clickable {
+ cursor: pointer;
+}
+.dropzone.dz-clickable * {
+ cursor: default;
+}
+.dropzone.dz-clickable .dz-message,
+.dropzone.dz-clickable .dz-message * {
+ cursor: pointer;
+}
+.dropzone.dz-started .dz-message {
+ display: none;
+}
+.dropzone.dz-drag-hover {
+ border-style: solid;
+}
+.dropzone.dz-drag-hover .dz-message {
+ opacity: 0.5;
+}
+.dropzone .dz-message {
+ text-align: center;
+ margin: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+.dropzone .dz-message .dz-button {
+ background: none;
+ color: inherit;
+ border: none;
+ padding: 0;
+ font: inherit;
+ cursor: pointer;
+ outline: inherit;
+}
+.dropzone .dz-preview {
+ position: relative;
+ display: inline-block;
+ vertical-align: top;
+ margin: 12px;
+ min-height: 100px;
+}
+.dropzone .dz-preview:hover {
+ z-index: 1000;
+}
+.dropzone .dz-preview:hover .dz-details {
+ opacity: 1;
+}
+.dropzone .dz-preview.dz-file-preview .dz-image {
+ border-radius: 20px;
+ background: #999;
+ background: linear-gradient(to bottom, #eee, #ddd);
+}
+.dropzone .dz-preview.dz-file-preview .dz-details {
+ opacity: 1;
+}
+.dropzone .dz-preview.dz-image-preview {
+ background: #fff;
+}
+.dropzone .dz-preview.dz-image-preview .dz-details {
+ transition: opacity 0.2s linear;
+}
+.dropzone .dz-preview .dz-remove {
+ font-size: 14px;
+ text-align: center;
+ display: block;
+ cursor: pointer;
+ border: none;
+}
+.dropzone .dz-preview .dz-remove:hover {
+ text-decoration: underline;
+}
+.dropzone .dz-preview:hover .dz-details {
+ opacity: 1;
+}
+.dropzone .dz-preview .dz-details {
+ z-index: 20;
+ position: absolute;
+ top: 0;
+ left: 0;
+ opacity: 0;
+ font-size: 13px;
+ min-width: 100%;
+ max-width: 100%;
+ padding: 2em 1em;
+ text-align: center;
+ color: rgba(0, 0, 0, 0.9);
+ line-height: 150%;
+}
+.dropzone .dz-preview .dz-details .dz-size {
+ margin-bottom: 1em;
+ font-size: 16px;
+}
+.dropzone .dz-preview .dz-details .dz-filename {
+ white-space: nowrap;
+}
+.dropzone .dz-preview .dz-details .dz-filename:hover span {
+ border: 1px solid rgba(200, 200, 200, 0.8);
+ background-color: rgba(255, 255, 255, 0.8);
+}
+.dropzone .dz-preview .dz-details .dz-filename:not(:hover) {
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span {
+ border: 1px solid transparent;
+}
+.dropzone .dz-preview .dz-details .dz-filename span,
+.dropzone .dz-preview .dz-details .dz-size span {
+ background-color: rgba(255, 255, 255, 0.4);
+ padding: 0 0.4em;
+ border-radius: 3px;
+}
+.dropzone .dz-preview:hover .dz-image img {
+ transform: scale(1.05, 1.05);
+ filter: blur(8px);
+}
+.dropzone .dz-preview .dz-image {
+ border-radius: 20px;
+ overflow: hidden;
+ width: 120px;
+ height: 120px;
+ position: relative;
+ display: block;
+ z-index: 10;
+}
+.dropzone .dz-preview .dz-image img {
+ display: block;
+}
+.dropzone .dz-preview.dz-success .dz-success-mark {
+ animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
+}
+.dropzone .dz-preview.dz-error .dz-error-mark {
+ opacity: 1;
+ animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
+}
+.dropzone .dz-preview .dz-success-mark,
+.dropzone .dz-preview .dz-error-mark {
+ pointer-events: none;
+ opacity: 0;
+ z-index: 500;
+ position: absolute;
+ display: block;
+ top: 50%;
+ left: 50%;
+ margin-left: -27px;
+ margin-top: -27px;
+ background: rgba(0, 0, 0, 0.8);
+ border-radius: 50%;
+}
+.dropzone .dz-preview .dz-success-mark svg,
+.dropzone .dz-preview .dz-error-mark svg {
+ display: block;
+ width: 54px;
+ height: 54px;
+ fill: #fff;
+}
+.dropzone .dz-preview.dz-processing .dz-progress {
+ opacity: 1;
+ transition: all 0.2s linear;
+}
+.dropzone .dz-preview.dz-complete .dz-progress {
+ opacity: 0;
+ transition: opacity 0.4s ease-in;
+}
+.dropzone .dz-preview:not(.dz-processing) .dz-progress {
+ animation: pulse 6s ease infinite;
+}
+.dropzone .dz-preview .dz-progress {
+ opacity: 1;
+ z-index: 1000;
+ pointer-events: none;
+ position: absolute;
+ height: 20px;
+ top: 50%;
+ margin-top: -10px;
+ left: 15%;
+ right: 15%;
+ border: 3px solid rgba(0, 0, 0, 0.8);
+ background: rgba(0, 0, 0, 0.8);
+ border-radius: 10px;
+ overflow: hidden;
+}
+.dropzone .dz-preview .dz-progress .dz-upload {
+ background: #fff;
+ display: block;
+ position: relative;
+ height: 100%;
+ width: 0;
+ transition: width 300ms ease-in-out;
+ border-radius: 17px;
+}
+.dropzone .dz-preview.dz-error .dz-error-message {
+ display: block;
+}
+.dropzone .dz-preview.dz-error:hover .dz-error-message {
+ opacity: 1;
+ pointer-events: auto;
+}
+.dropzone .dz-preview .dz-error-message {
+ pointer-events: none;
+ z-index: 1000;
+ position: absolute;
+ display: block;
+ display: none;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ border-radius: 8px;
+ font-size: 13px;
+ top: 130px;
+ left: -10px;
+ width: 140px;
+ background: #b10606;
+ padding: 0.5em 1em;
+ color: #fff;
+}
+.dropzone .dz-preview .dz-error-message:after {
+ content: "";
+ position: absolute;
+ top: -6px;
+ left: 64px;
+ width: 0;
+ height: 0;
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ border-bottom: 6px solid #b10606;
+} /*# sourceMappingURL=dropzone.css.map */
diff --git a/src/ui/static/js/air-datepicker/air-datepicker.d.ts b/src/ui/static/js/air-datepicker/air-datepicker.d.ts
deleted file mode 100644
index 0f4a9122a..000000000
--- a/src/ui/static/js/air-datepicker/air-datepicker.d.ts
+++ /dev/null
@@ -1,146 +0,0 @@
-declare type AirDatepickerSelector = string | HTMLElement;
-
-export declare type AirDatepickerLocale = {
- days: string[],
- daysShort: string[],
- daysMin: string[],
- months: string[],
- monthsShort: string[],
- today: string,
- clear: string,
- dateFormat: string,
- timeFormat: string,
- firstDay: 0 | 1 | 2 | 3 | 4 | 5 | 6,
-}
-
-export declare type AirDatepickerButton = {
- content: string | ((dp: AirDatepicker) => string),
- tagName?: keyof HTMLElementTagNameMap,
- className?: string,
- attrs?: Record,
- onClick?: (dp: AirDatepicker) => void
-}
-
-export declare type AirDatepickerButtonPresets = 'clear' | 'today';
-
-export declare type AirDatepickerPosition = 'left' | 'left top' | 'left bottom' | 'top' | 'top left' | 'top right' | 'right' | 'right top' | 'right bottom' | 'bottom' | 'bottom left' | 'bottom right';
-export declare type AirDatepickerViews = 'days' | 'months' | 'years';
-export declare type AirDatepickerViewsSingle = 'day' | 'month' | 'year';
-export declare type AirDatepickerDate = string | number | Date;
-export declare type AirDatepickerNavEntry = string | ((dp: AirDatepicker) => string);
-export declare type AirDatepickerDecade = [number, number];
-export declare type AirDatepickerPositionCallback = (
- {
- $datepicker,
- $target,
- $pointer,
- isViewChange,
- done
- }: {
- $datepicker: HTMLDivElement,
- $target: HTMLInputElement,
- $pointer: HTMLElement,
- isViewChange: boolean,
- done: () => void
- }) => void | (() => void)
-
-export declare type AirDatepickerOptions = {
- classes: string
- inline: boolean,
- locale: Partial,
- startDate: AirDatepickerDate,
- firstDay: number,
- isMobile: boolean,
- visible: boolean,
- weekends: [number, number],
- dateFormat: string | ((d: Date) => string),
- altField: AirDatepickerSelector,
- altFieldDateFormat: string,
- toggleSelected: boolean,
- keyboardNav: boolean,
- selectedDates: AirDatepickerDate[] | false,
- container: AirDatepickerSelector,
- position: AirDatepickerPosition | AirDatepickerPositionCallback,
- offset: number,
- view: AirDatepickerViews,
- minView: AirDatepickerViews,
- showOtherMonths: boolean,
- selectOtherMonths: boolean,
- moveToOtherMonthsOnSelect: boolean,
- showOtherYears: boolean,
- selectOtherYears: boolean,
- moveToOtherYearsOnSelect: boolean,
- minDate: AirDatepickerDate | false,
- maxDate: AirDatepickerDate | false,
- disableNavWhenOutOfRange: true,
- multipleDates: number | true | false,
- multipleDatesSeparator: string,
- range: boolean,
- dynamicRange: boolean,
- buttons: AirDatepickerButtonPresets | AirDatepickerButton | (AirDatepickerButtonPresets| AirDatepickerButton)[] | false,
- monthsField: keyof AirDatepickerLocale,
- showEvent: string,
- autoClose: boolean,
- prevHtml: string,
- nextHtml: string,
- navTitles: {
- days?: AirDatepickerNavEntry,
- months?: AirDatepickerNavEntry,
- years?: AirDatepickerNavEntry
- },
- timepicker: boolean,
- onlyTimepicker: boolean,
- dateTimeSeparator: string,
- timeFormat: string,
- minHours: number,
- maxHours: number,
- minMinutes: number,
- maxMinutes: number,
- hoursStep: number,
- minutesStep: number,
-
- onSelect: ({date, formattedDate, datepicker}: {date: Date | Date[], formattedDate: string | string[], datepicker: AirDatepicker}) => void,
- onChangeViewDate: ({month, year, decade}: {month: number, year: number, decade: AirDatepickerDecade}) => void,
- onChangeView: (view: AirDatepickerViews) => void,
- onRenderCell: (params: {date: Date, cellType: AirDatepickerViewsSingle, datepicker: AirDatepicker}) => ({
- disabled?: boolean,
- classes?: string,
- html?: string
- attrs?: Record
- } | void),
- onShow: (isAnimationComplete: boolean) => void,
- onHide: (isAnimationComplete: boolean) => void,
- onClickDayName: ({dayIndex, datepicker}: {dayIndex: number, datepicker: AirDatepicker}) => void
-}
-
-
-declare class AirDatepicker {
- constructor(el: string | E, opts? : Partial)
- static version: string
- show: () => void
- hide: () => void
- next: () => void
- prev: () => void
- selectDate: (date: AirDatepickerDate | AirDatepickerDate[], opts?: {updateTime?: boolean, silent?: boolean}) => void
- unselectDate: (date: AirDatepickerDate) => void
- clear: () => void
- formatDate: (date: AirDatepickerDate, format: string) => string
- destroy: () => void
- update: (newOpts: Partial) => void
- setCurrentView: (newView: AirDatepickerViews) => void
- setViewDate: (newViewDate: AirDatepickerDate) => void
- setFocusDate: (date: AirDatepickerDate | false, opts?: {viewDateTransition?: boolean}) => void
- up: (date?: AirDatepickerDate) => void
- down: (date?: AirDatepickerDate) => void
-
- $el: E
- $datepicker: HTMLDivElement
- viewDate: Date
- currentView: AirDatepickerViews
- selectedDates: Date[]
- focusDate: Date | false
- visible: boolean
-}
-
-
-export default AirDatepicker;
diff --git a/src/ui/static/js/air-datepicker/air-datepicker.js b/src/ui/static/js/air-datepicker/air-datepicker.js
deleted file mode 100644
index da4885f2d..000000000
--- a/src/ui/static/js/air-datepicker/air-datepicker.js
+++ /dev/null
@@ -1,2350 +0,0 @@
-const { default: AirDatepicker } = require("air-datepicker");
-
-!(function (e, t) {
- "object" == typeof exports && "object" == typeof module
- ? (module.exports = t())
- : "function" == typeof define && define.amd
- ? define([], t)
- : "object" == typeof exports
- ? (exports.AirDatepicker = t())
- : (e.AirDatepicker = t());
-})(this, function () {
- return (function () {
- "use strict";
- var e = {
- d: function (t, i) {
- for (var s in i)
- e.o(i, s) &&
- !e.o(t, s) &&
- Object.defineProperty(t, s, { enumerable: !0, get: i[s] });
- },
- o: function (e, t) {
- return Object.prototype.hasOwnProperty.call(e, t);
- },
- },
- t = {};
- e.d(t, {
- default: function () {
- return j;
- },
- });
- var i = {
- days: "days",
- months: "months",
- years: "years",
- day: "day",
- month: "month",
- year: "year",
- eventChangeViewDate: "changeViewDate",
- eventChangeCurrentView: "changeCurrentView",
- eventChangeFocusDate: "changeFocusDate",
- eventChangeSelectedDate: "changeSelectedDate",
- eventChangeTime: "changeTime",
- eventChangeLastSelectedDate: "changeLastSelectedDate",
- actionSelectDate: "selectDate",
- actionUnselectDate: "unselectDate",
- cssClassWeekend: "-weekend-",
- },
- s = {
- classes: "",
- inline: !1,
- locale: {
- days: [
- "Воскресенье",
- "Понедельник",
- "Вторник",
- "Среда",
- "Четверг",
- "Пятница",
- "Суббота",
- ],
- daysShort: ["Вос", "Пон", "Вто", "Сре", "Чет", "Пят", "Суб"],
- daysMin: ["Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"],
- months: [
- "Январь",
- "Февраль",
- "Март",
- "Апрель",
- "Май",
- "Июнь",
- "Июль",
- "Август",
- "Сентябрь",
- "Октябрь",
- "Ноябрь",
- "Декабрь",
- ],
- monthsShort: [
- "Янв",
- "Фев",
- "Мар",
- "Апр",
- "Май",
- "Июн",
- "Июл",
- "Авг",
- "Сен",
- "Окт",
- "Ноя",
- "Дек",
- ],
- today: "Сегодня",
- clear: "Очистить",
- dateFormat: "dd.MM.yyyy",
- timeFormat: "HH:mm",
- firstDay: 1,
- },
- startDate: new Date(),
- firstDay: "",
- weekends: [6, 0],
- dateFormat: "",
- altField: "",
- altFieldDateFormat: "T",
- toggleSelected: !0,
- keyboardNav: !0,
- selectedDates: !1,
- container: "",
- isMobile: !1,
- visible: !1,
- position: "bottom left",
- offset: 12,
- view: i.days,
- minView: i.days,
- showOtherMonths: !0,
- selectOtherMonths: !0,
- moveToOtherMonthsOnSelect: !0,
- showOtherYears: !0,
- selectOtherYears: !0,
- moveToOtherYearsOnSelect: !0,
- minDate: "",
- maxDate: "",
- disableNavWhenOutOfRange: !0,
- multipleDates: !1,
- multipleDatesSeparator: ", ",
- range: !1,
- dynamicRange: !0,
- buttons: !1,
- monthsField: "monthsShort",
- showEvent: "focus",
- autoClose: !1,
- prevHtml: '',
- nextHtml: '',
- navTitles: {
- days: "MMMM, yyyy",
- months: "yyyy",
- years: "yyyy1 - yyyy2",
- },
- timepicker: !1,
- onlyTimepicker: !1,
- dateTimeSeparator: " ",
- timeFormat: "",
- minHours: 0,
- maxHours: 24,
- minMinutes: 0,
- maxMinutes: 59,
- hoursStep: 1,
- minutesStep: 1,
- onSelect: !1,
- onChangeViewDate: !1,
- onChangeView: !1,
- onRenderCell: !1,
- onShow: !1,
- onHide: !1,
- onClickDayName: !1,
- };
- function a(e) {
- let t =
- arguments.length > 1 && void 0 !== arguments[1]
- ? arguments[1]
- : document;
- return "string" == typeof e ? t.querySelector(e) : e;
- }
- function n() {
- let {
- tagName: e = "div",
- className: t = "",
- innerHtml: i = "",
- id: s = "",
- attrs: a = {},
- } = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
- n = document.createElement(e);
- return (
- t && n.classList.add(...t.split(" ")),
- s && (n.id = s),
- i && (n.innerHTML = i),
- a && r(n, a),
- n
- );
- }
- function r(e, t) {
- for (let [i, s] of Object.entries(t))
- void 0 !== s && e.setAttribute(i, s);
- return e;
- }
- function h(e) {
- return new Date(e.getFullYear(), e.getMonth() + 1, 0).getDate();
- }
- function o(e) {
- let t = e.getHours(),
- i = t % 12 == 0 ? 12 : t % 12;
- return {
- year: e.getFullYear(),
- month: e.getMonth(),
- fullMonth:
- e.getMonth() + 1 < 10 ? "0" + (e.getMonth() + 1) : e.getMonth() + 1,
- date: e.getDate(),
- fullDate: e.getDate() < 10 ? "0" + e.getDate() : e.getDate(),
- day: e.getDay(),
- hours: t,
- fullHours: l(t),
- hours12: i,
- fullHours12: l(i),
- minutes: e.getMinutes(),
- fullMinutes:
- e.getMinutes() < 10 ? "0" + e.getMinutes() : e.getMinutes(),
- };
- }
- function l(e) {
- return e < 10 ? "0" + e : e;
- }
- function d(e) {
- let t = 10 * Math.floor(e.getFullYear() / 10);
- return [t, t + 9];
- }
- function c() {
- let e = [];
- for (var t = arguments.length, i = new Array(t), s = 0; s < t; s++)
- i[s] = arguments[s];
- return (
- i.forEach((t) => {
- if ("object" == typeof t) for (let i in t) t[i] && e.push(i);
- else t && e.push(t);
- }),
- e.join(" ")
- );
- }
- function u(e, t) {
- let s =
- arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : i.days;
- if (!e || !t) return !1;
- let a = o(e),
- n = o(t),
- r = {
- [i.days]:
- a.date === n.date && a.month === n.month && a.year === n.year,
- [i.months]: a.month === n.month && a.year === n.year,
- [i.years]: a.year === n.year,
- };
- return r[s];
- }
- function p(e, t, i) {
- let s = g(e, !1).getTime(),
- a = g(t, !1).getTime();
- return i ? s >= a : s > a;
- }
- function m(e, t) {
- return !p(e, t, !0);
- }
- function g(e) {
- let t =
- !(arguments.length > 1 && void 0 !== arguments[1]) || arguments[1],
- i = new Date(e.getTime());
- return "boolean" != typeof t || t || D(i), i;
- }
- function D(e) {
- return e.setHours(0, 0, 0, 0), e;
- }
- function v(e, t, i) {
- e.length
- ? e.forEach((e) => {
- e.addEventListener(t, i);
- })
- : e.addEventListener(t, i);
- }
- function y(e, t) {
- return (
- !(!e || e === document || e instanceof DocumentFragment) &&
- (e.matches(t) ? e : y(e.parentNode, t))
- );
- }
- function f(e, t, i) {
- return e > i ? i : e < t ? t : e;
- }
- function w(e) {
- for (
- var t = arguments.length, i = new Array(t > 1 ? t - 1 : 0), s = 1;
- s < t;
- s++
- )
- i[s - 1] = arguments[s];
- return (
- i
- .filter((e) => e)
- .forEach((t) => {
- for (let [i, s] of Object.entries(t))
- if (void 0 !== s && "[object Object]" === s.toString()) {
- let t = void 0 !== e[i] ? e[i].toString() : void 0,
- a = s.toString(),
- n = Array.isArray(s) ? [] : {};
- (e[i] = e[i] ? (t !== a ? n : e[i]) : n), w(e[i], s);
- } else e[i] = s;
- }),
- e
- );
- }
- function b(e) {
- let t = e;
- return (
- e instanceof Date || (t = new Date(e)),
- isNaN(t.getTime()) &&
- (console.log(
- 'Unable to convert value "'.concat(e, '" to Date object')
- ),
- (t = !1)),
- t
- );
- }
- function k(e) {
- let t = "\\s|\\.|-|/|\\\\|,|\\$|\\!|\\?|:|;";
- return new RegExp("(^|>|" + t + ")(" + e + ")($|<|" + t + ")", "g");
- }
- function C(e, t, i) {
- return (
- t in e
- ? Object.defineProperty(e, t, {
- value: i,
- enumerable: !0,
- configurable: !0,
- writable: !0,
- })
- : (e[t] = i),
- e
- );
- }
- class _ {
- constructor() {
- let {
- type: e,
- date: t,
- dp: i,
- opts: s,
- body: a,
- } = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {};
- C(this, "focus", () => {
- this.$cell.classList.add("-focus-"), (this.focused = !0);
- }),
- C(this, "removeFocus", () => {
- this.$cell.classList.remove("-focus-"), (this.focused = !1);
- }),
- C(this, "select", () => {
- this.$cell.classList.add("-selected-"), (this.selected = !0);
- }),
- C(this, "removeSelect", () => {
- this.$cell.classList.remove(
- "-selected-",
- "-range-from-",
- "-range-to-"
- ),
- (this.selected = !1);
- }),
- C(this, "onChangeSelectedDate", () => {
- this.isDisabled ||
- (this._handleSelectedStatus(),
- this.opts.range && this._handleRangeStatus());
- }),
- C(this, "onChangeFocusDate", (e) => {
- if (!e) return void (this.focused && this.removeFocus());
- let t = u(e, this.date, this.type);
- t ? this.focus() : !t && this.focused && this.removeFocus(),
- this.opts.range && this._handleRangeStatus();
- }),
- C(
- this,
- "render",
- () => (
- (this.$cell.innerHTML = this._getHtml()),
- (this.$cell.adpCell = this),
- this.$cell
- )
- ),
- (this.type = e),
- (this.singleType = this.type.slice(0, -1)),
- (this.date = t),
- (this.dp = i),
- (this.opts = s),
- (this.body = a),
- (this.customData = !1),
- this.init();
- }
- init() {
- let { range: e, onRenderCell: t } = this.opts;
- t &&
- (this.customData = t({
- date: this.date,
- cellType: this.singleType,
- datepicker: this.dp,
- })),
- this._createElement(),
- this._bindDatepickerEvents(),
- this._handleInitialFocusStatus(),
- this.dp.hasSelectedDates &&
- (this._handleSelectedStatus(), e && this._handleRangeStatus());
- }
- _bindDatepickerEvents() {
- this.dp.on(i.eventChangeSelectedDate, this.onChangeSelectedDate),
- this.dp.on(i.eventChangeFocusDate, this.onChangeFocusDate);
- }
- unbindDatepickerEvents() {
- this.dp.off(i.eventChangeSelectedDate, this.onChangeSelectedDate),
- this.dp.off(i.eventChangeFocusDate, this.onChangeFocusDate);
- }
- _createElement() {
- var e;
- let { year: t, month: i, date: s } = o(this.date),
- a =
- (null === (e = this.customData) || void 0 === e
- ? void 0
- : e.attrs) || {};
- this.$cell = n({
- className: this._getClassName(),
- attrs: { "data-year": t, "data-month": i, "data-date": s, ...a },
- });
- }
- _getClassName() {
- var e, t;
- let s = new Date(),
- { selectOtherMonths: a, selectOtherYears: n } = this.opts,
- { minDate: r, maxDate: h } = this.dp,
- { day: l } = o(this.date),
- d = this._isOutOfMinMaxRange(),
- p =
- null === (e = this.customData) || void 0 === e
- ? void 0
- : e.disabled,
- m = c("air-datepicker-cell", "-".concat(this.singleType, "-"), {
- "-current-": u(s, this.date, this.type),
- "-min-date-": r && u(r, this.date, this.type),
- "-max-date-": h && u(h, this.date, this.type),
- }),
- g = "";
- switch (this.type) {
- case i.days:
- g = c({
- "-weekend-": this.dp.isWeekend(l),
- "-other-month-": this.isOtherMonth,
- "-disabled-": (this.isOtherMonth && !a) || d || p,
- });
- break;
- case i.months:
- g = c({ "-disabled-": d || p });
- break;
- case i.years:
- g = c({
- "-other-decade-": this.isOtherDecade,
- "-disabled-": d || (this.isOtherDecade && !n) || p,
- });
- }
- return c(
- m,
- g,
- null === (t = this.customData) || void 0 === t ? void 0 : t.classes
- );
- }
- _getHtml() {
- var e;
- let { year: t, month: s, date: a } = o(this.date),
- { showOtherMonths: n, showOtherYears: r } = this.opts;
- if (null !== (e = this.customData) && void 0 !== e && e.html)
- return this.customData.html;
- switch (this.type) {
- case i.days:
- return !n && this.isOtherMonth ? "" : a;
- case i.months:
- return this.dp.locale[this.opts.monthsField][s];
- case i.years:
- return !r && this.isOtherDecade ? "" : t;
- }
- }
- _isOutOfMinMaxRange() {
- let { minDate: e, maxDate: t } = this.dp,
- { type: s, date: a } = this,
- { month: n, year: r, date: h } = o(a),
- l = s === i.days,
- d = s === i.years,
- c = !!e && new Date(r, d ? e.getMonth() : n, l ? h : e.getDate()),
- u = !!t && new Date(r, d ? t.getMonth() : n, l ? h : t.getDate());
- return e && t ? m(c, e) || p(u, t) : e ? m(c, e) : t ? p(u, t) : void 0;
- }
- destroy() {
- this.unbindDatepickerEvents();
- }
- _handleRangeStatus() {
- let { rangeDateFrom: e, rangeDateTo: t } = this.dp,
- i = c({
- "-in-range-":
- e && t && ((s = this.date), (a = e), (n = t), p(s, a) && m(s, n)),
- "-range-from-": e && u(this.date, e, this.type),
- "-range-to-": t && u(this.date, t, this.type),
- });
- var s, a, n;
- this.$cell.classList.remove("-range-from-", "-range-to-", "-in-range-"),
- i && this.$cell.classList.add(...i.split(" "));
- }
- _handleSelectedStatus() {
- let e = this.dp._checkIfDateIsSelected(this.date, this.type);
- e ? this.select() : !e && this.selected && this.removeSelect();
- }
- _handleInitialFocusStatus() {
- u(this.dp.focusDate, this.date, this.type) && this.focus();
- }
- get isDisabled() {
- return this.$cell.matches(".-disabled-");
- }
- get isOtherMonth() {
- return this.dp.isOtherMonth(this.date);
- }
- get isOtherDecade() {
- return this.dp.isOtherDecade(this.date);
- }
- }
- function M(e, t, i) {
- return (
- t in e
- ? Object.defineProperty(e, t, {
- value: i,
- enumerable: !0,
- configurable: !0,
- writable: !0,
- })
- : (e[t] = i),
- e
- );
- }
- let $ = {
- [i.days]:
- '' +
- ''),
- [i.months]: ''
- ),
- [i.years]: ''
- ),
- };
- const S = ".air-datepicker-cell";
- class T {
- constructor(e) {
- let { dp: t, type: s, opts: a } = e;
- M(this, "handleClick", (e) => {
- let t = e.target.closest(S).adpCell;
- if (t.isDisabled) return;
- if (!this.dp.isMinViewReached) return void this.dp.down();
- let i = this.dp._checkIfDateIsSelected(t.date, t.type);
- i
- ? this.dp._handleAlreadySelectedDates(i, t.date)
- : this.dp.selectDate(t.date);
- }),
- M(this, "handleDayNameClick", (e) => {
- let t = e.target.getAttribute("data-day-index");
- this.opts.onClickDayName({
- dayIndex: Number(t),
- datepicker: this.dp,
- });
- }),
- M(this, "onChangeCurrentView", (e) => {
- e !== this.type ? this.hide() : (this.show(), this.render());
- }),
- M(this, "onMouseOverCell", (e) => {
- let t = y(e.target, S);
- this.dp.setFocusDate(!!t && t.adpCell.date);
- }),
- M(this, "onMouseOutCell", () => {
- this.dp.setFocusDate(!1);
- }),
- M(this, "onClickBody", (e) => {
- let { onClickDayName: t } = this.opts,
- i = e.target;
- i.closest(S) && this.handleClick(e),
- t &&
- i.closest(".air-datepicker-body--day-name") &&
- this.handleDayNameClick(e);
- }),
- M(this, "onMouseDown", (e) => {
- this.pressed = !0;
- let t = y(e.target, S),
- i = t && t.adpCell;
- u(i.date, this.dp.rangeDateFrom) && (this.rangeFromFocused = !0),
- u(i.date, this.dp.rangeDateTo) && (this.rangeToFocused = !0);
- }),
- M(this, "onMouseMove", (e) => {
- if (!this.pressed || !this.dp.isMinViewReached) return;
- e.preventDefault();
- let t = y(e.target, S),
- i = t && t.adpCell,
- { selectedDates: s, rangeDateTo: a, rangeDateFrom: n } = this.dp;
- if (!i || i.isDisabled) return;
- let { date: r } = i;
- if (2 === s.length) {
- if (this.rangeFromFocused && !p(r, a)) {
- let { hours: e, minutes: t } = o(n);
- r.setHours(e),
- r.setMinutes(t),
- (this.dp.rangeDateFrom = r),
- this.dp.replaceDate(n, r);
- }
- if (this.rangeToFocused && !m(r, n)) {
- let { hours: e, minutes: t } = o(a);
- r.setHours(e),
- r.setMinutes(t),
- (this.dp.rangeDateTo = r),
- this.dp.replaceDate(a, r);
- }
- }
- }),
- M(this, "onMouseUp", () => {
- (this.pressed = !1),
- (this.rangeFromFocused = !1),
- (this.rangeToFocused = !1);
- }),
- M(this, "onChangeViewDate", (e, t) => {
- if (!this.isVisible) return;
- let s = d(e),
- a = d(t);
- switch (this.dp.currentView) {
- case i.days:
- if (u(e, t, i.months)) return;
- break;
- case i.months:
- if (u(e, t, i.years)) return;
- break;
- case i.years:
- if (s[0] === a[0] && s[1] === a[1]) return;
- }
- this.render();
- }),
- M(this, "render", () => {
- this.destroyCells(),
- this._generateCells(),
- this.cells.forEach((e) => {
- this.$cells.appendChild(e.render());
- });
- }),
- (this.dp = t),
- (this.type = s),
- (this.opts = a),
- (this.cells = []),
- (this.$el = ""),
- (this.pressed = !1),
- (this.isVisible = !0),
- this.init();
- }
- init() {
- this._buildBaseHtml(),
- this.type === i.days && this.renderDayNames(),
- this.render(),
- this._bindEvents(),
- this._bindDatepickerEvents();
- }
- _bindEvents() {
- let { range: e, dynamicRange: t } = this.opts;
- v(this.$el, "mouseover", this.onMouseOverCell),
- v(this.$el, "mouseout", this.onMouseOutCell),
- v(this.$el, "click", this.onClickBody),
- e &&
- t &&
- (v(this.$el, "mousedown", this.onMouseDown),
- v(this.$el, "mousemove", this.onMouseMove),
- v(window.document, "mouseup", this.onMouseUp));
- }
- _bindDatepickerEvents() {
- this.dp.on(i.eventChangeViewDate, this.onChangeViewDate),
- this.dp.on(i.eventChangeCurrentView, this.onChangeCurrentView);
- }
- _buildBaseHtml() {
- (this.$el = n({
- className: "air-datepicker-body -".concat(this.type, "-"),
- innerHtml: $[this.type],
- })),
- (this.$names = a(".air-datepicker-body--day-names", this.$el)),
- (this.$cells = a(".air-datepicker-body--cells", this.$el));
- }
- _getDayNamesHtml() {
- let e =
- arguments.length > 0 && void 0 !== arguments[0]
- ? arguments[0]
- : this.dp.locale.firstDay,
- t = "",
- s = this.dp.isWeekend,
- { onClickDayName: a } = this.opts,
- n = e,
- r = 0;
- for (; r < 7; ) {
- let e = n % 7,
- h = c("air-datepicker-body--day-name", {
- [i.cssClassWeekend]: s(e),
- "-clickable-": !!a,
- }),
- o = this.dp.locale.daysMin[e];
- (t += '")
- .concat(o, "
")),
- r++,
- n++;
- }
- return t;
- }
- _getDaysCells() {
- let {
- viewDate: e,
- locale: { firstDay: t },
- } = this.dp,
- i = h(e),
- { year: s, month: a } = o(e),
- n = new Date(s, a, 1),
- r = new Date(s, a, i),
- l = n.getDay() - t,
- d = 6 - r.getDay() + t;
- (l = l < 0 ? l + 7 : l), (d = d > 6 ? d - 7 : d);
- let c = (function (e, t) {
- let { year: i, month: s, date: a } = o(e);
- return new Date(i, s, a - t);
- })(n, l),
- u = i + l + d,
- p = c.getDate(),
- { year: m, month: g } = o(c),
- D = 0;
- for (; D < u; ) {
- let e = new Date(m, g, p + D);
- this._generateCell(e), D++;
- }
- }
- _generateCell(e) {
- let { type: t, dp: i, opts: s } = this,
- a = new _({ type: t, dp: i, opts: s, date: e, body: this });
- return this.cells.push(a), a;
- }
- _generateDayCells() {
- this._getDaysCells();
- }
- _generateMonthCells() {
- let { year: e } = this.dp.parsedViewDate,
- t = 0;
- for (; t < 12; )
- this.cells.push(this._generateCell(new Date(e, t))), t++;
- }
- _generateYearCells() {
- let e = d(this.dp.viewDate),
- t = e[0] - 1,
- i = e[1] + 1,
- s = t;
- for (; s <= i; )
- this.cells.push(this._generateCell(new Date(s, 0))), s++;
- }
- renderDayNames() {
- this.$names.innerHTML = this._getDayNamesHtml();
- }
- _generateCells() {
- switch (this.type) {
- case i.days:
- this._generateDayCells();
- break;
- case i.months:
- this._generateMonthCells();
- break;
- case i.years:
- this._generateYearCells();
- }
- }
- show() {
- (this.isVisible = !0), this.$el.classList.remove("-hidden-");
- }
- hide() {
- (this.isVisible = !1), this.$el.classList.add("-hidden-");
- }
- destroyCells() {
- this.cells.forEach((e) => e.destroy()),
- (this.cells = []),
- (this.$cells.innerHTML = "");
- }
- destroy() {
- this.destroyCells(),
- this.dp.off(i.eventChangeViewDate, this.onChangeViewDate),
- this.dp.off(i.eventChangeCurrentView, this.onChangeCurrentView);
- }
- }
- function F(e, t, i) {
- return (
- t in e
- ? Object.defineProperty(e, t, {
- value: i,
- enumerable: !0,
- configurable: !0,
- writable: !0,
- })
- : (e[t] = i),
- e
- );
- }
- class V {
- constructor(e) {
- let { dp: t, opts: i } = e;
- F(this, "onClickNav", (e) => {
- let t = y(e.target, ".air-datepicker-nav--action");
- if (!t) return;
- let i = t.dataset.action;
- this.dp[i]();
- }),
- F(this, "onChangeViewDate", () => {
- this.render(), this._resetNavStatus(), this.handleNavStatus();
- }),
- F(this, "onChangeCurrentView", () => {
- this.render(), this._resetNavStatus(), this.handleNavStatus();
- }),
- F(this, "onClickNavTitle", () => {
- this.dp.isFinalView || this.dp.up();
- }),
- F(this, "update", () => {
- let { prevHtml: e, nextHtml: t } = this.opts;
- (this.$prev.innerHTML = e),
- (this.$next.innerHTML = t),
- this._resetNavStatus(),
- this.render(),
- this.handleNavStatus();
- }),
- F(this, "renderDelay", () => {
- setTimeout(this.render);
- }),
- F(this, "render", () => {
- (this.$title.innerHTML = this._getTitle()),
- (function (e, t) {
- for (let i in t)
- t[i] ? e.classList.add(i) : e.classList.remove(i);
- })(this.$title, { "-disabled-": this.dp.isFinalView });
- }),
- (this.dp = t),
- (this.opts = i),
- this.init();
- }
- init() {
- this._createElement(),
- this._buildBaseHtml(),
- this._defineDOM(),
- this.render(),
- this.handleNavStatus(),
- this._bindEvents(),
- this._bindDatepickerEvents();
- }
- _defineDOM() {
- (this.$title = a(".air-datepicker-nav--title", this.$el)),
- (this.$prev = a('[data-action="prev"]', this.$el)),
- (this.$next = a('[data-action="next"]', this.$el));
- }
- _bindEvents() {
- this.$el.addEventListener("click", this.onClickNav),
- this.$title.addEventListener("click", this.onClickNavTitle);
- }
- _bindDatepickerEvents() {
- this.dp.on(i.eventChangeViewDate, this.onChangeViewDate),
- this.dp.on(i.eventChangeCurrentView, this.onChangeCurrentView),
- this.isNavIsFunction &&
- (this.dp.on(i.eventChangeSelectedDate, this.renderDelay),
- this.dp.opts.timepicker &&
- this.dp.on(i.eventChangeTime, this.render));
- }
- destroy() {
- this.dp.off(i.eventChangeViewDate, this.onChangeViewDate),
- this.dp.off(i.eventChangeCurrentView, this.onChangeCurrentView),
- this.isNavIsFunction &&
- (this.dp.off(i.eventChangeSelectedDate, this.renderDelay),
- this.dp.opts.timepicker &&
- this.dp.off(i.eventChangeTime, this.render));
- }
- _createElement() {
- this.$el = n({ tagName: "nav", className: "air-datepicker-nav" });
- }
- _getTitle() {
- let { dp: e, opts: t } = this,
- i = t.navTitles[e.currentView];
- return "function" == typeof i ? i(e) : e.formatDate(e.viewDate, i);
- }
- handleNavStatus() {
- let { disableNavWhenOutOfRange: e } = this.opts,
- { minDate: t, maxDate: s } = this.dp;
- if ((!t && !s) || !e) return;
- let { year: a, month: n } = this.dp.parsedViewDate,
- r = !!t && o(t),
- h = !!s && o(s);
- switch (this.dp.currentView) {
- case i.days:
- t && r.month >= n && r.year >= a && this._disableNav("prev"),
- s && h.month <= n && h.year <= a && this._disableNav("next");
- break;
- case i.months:
- t && r.year >= a && this._disableNav("prev"),
- s && h.year <= a && this._disableNav("next");
- break;
- case i.years: {
- let e = d(this.dp.viewDate);
- t && r.year >= e[0] && this._disableNav("prev"),
- s && h.year <= e[1] && this._disableNav("next");
- break;
- }
- }
- }
- _disableNav(e) {
- a('[data-action="' + e + '"]', this.$el).classList.add("-disabled-");
- }
- _resetNavStatus() {
- !(function (e) {
- for (
- var t = arguments.length, i = new Array(t > 1 ? t - 1 : 0), s = 1;
- s < t;
- s++
- )
- i[s - 1] = arguments[s];
- e.length
- ? e.forEach((e) => {
- e.classList.remove(...i);
- })
- : e.classList.remove(...i);
- })(
- this.$el.querySelectorAll(".air-datepicker-nav--action"),
- "-disabled-"
- );
- }
- _buildBaseHtml() {
- let { prevHtml: e, nextHtml: t } = this.opts;
- this.$el.innerHTML =
- ''.concat(
- e,
- "
"
- ) +
- '' +
- ''.concat(
- t,
- "
"
- );
- }
- get isNavIsFunction() {
- let { navTitles: e } = this.opts;
- return Object.keys(e).find((t) => "function" == typeof e[t]);
- }
- }
- var x = {
- today: {
- content: (e) => e.locale.today,
- onClick: (e) => e.setViewDate(new Date()),
- },
- clear: { content: (e) => e.locale.clear, onClick: (e) => e.clear() },
- };
- class H {
- constructor(e) {
- let { dp: t, opts: i } = e;
- (this.dp = t), (this.opts = i), this.init();
- }
- init() {
- this.createElement(), this.render();
- }
- createElement() {
- this.$el = n({ className: "air-datepicker-buttons" });
- }
- destroy() {
- this.$el.parentNode.removeChild(this.$el);
- }
- clearHtml() {
- return (this.$el.innerHTML = ""), this;
- }
- generateButtons() {
- let { buttons: e } = this.opts;
- Array.isArray(e) || (e = [e]),
- e.forEach((e) => {
- let t = e;
- "string" == typeof e && x[e] && (t = x[e]);
- let i = this.createButton(t);
- t.onClick && this.attachEventToButton(i, t.onClick),
- this.$el.appendChild(i);
- });
- }
- attachEventToButton(e, t) {
- e.addEventListener("click", () => {
- t(this.dp);
- });
- }
- createButton(e) {
- let {
- content: t,
- className: i,
- tagName: s = "button",
- attrs: a = {},
- } = e,
- r = "function" == typeof t ? t(this.dp) : t;
- return n({
- tagName: s,
- innerHtml: "".concat(r, ""),
- className: c("air-datepicker-button", i),
- attrs: a,
- });
- }
- render() {
- this.generateButtons();
- }
- }
- function L(e, t, i) {
- return (
- t in e
- ? Object.defineProperty(e, t, {
- value: i,
- enumerable: !0,
- configurable: !0,
- writable: !0,
- })
- : (e[t] = i),
- e
- );
- }
- class O {
- constructor() {
- let { opts: e, dp: t } =
- arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {};
- L(this, "toggleTimepickerIsActive", (e) => {
- this.dp.timepickerIsActive = e;
- }),
- L(this, "onChangeSelectedDate", (e) => {
- let { date: t, updateTime: i = !1 } = e;
- t &&
- (this.setMinMaxTime(t),
- this.setCurrentTime(!!i && t),
- this.addTimeToDate(t));
- }),
- L(this, "onChangeLastSelectedDate", (e) => {
- e && (this.setTime(e), this.render());
- }),
- L(this, "onChangeInputRange", (e) => {
- let t = e.target;
- (this[t.getAttribute("name")] = t.value),
- this.updateText(),
- this.dp.trigger(i.eventChangeTime, {
- hours: this.hours,
- minutes: this.minutes,
- });
- }),
- L(this, "onMouseEnterLeave", (e) => {
- let t = e.target.getAttribute("name"),
- i = this.$minutesText;
- "hours" === t && (i = this.$hoursText),
- i.classList.toggle("-focus-");
- }),
- L(this, "onFocus", () => {
- this.toggleTimepickerIsActive(!0);
- }),
- L(this, "onBlur", () => {
- this.toggleTimepickerIsActive(!1);
- }),
- (this.opts = e),
- (this.dp = t);
- let { timeFormat: s } = this.dp.locale;
- s && (s.match(k("h")) || s.match(k("hh"))) && (this.ampm = !0),
- this.init();
- }
- init() {
- this.setTime(this.dp.lastSelectedDate || this.dp.viewDate),
- this.createElement(),
- this.buildHtml(),
- this.defineDOM(),
- this.render(),
- this.bindDatepickerEvents(),
- this.bindDOMEvents();
- }
- bindDatepickerEvents() {
- this.dp.on(i.eventChangeSelectedDate, this.onChangeSelectedDate),
- this.dp.on(
- i.eventChangeLastSelectedDate,
- this.onChangeLastSelectedDate
- );
- }
- bindDOMEvents() {
- let e = "input";
- navigator.userAgent.match(/trident/gi) && (e = "change"),
- v(this.$ranges, e, this.onChangeInputRange),
- v(this.$ranges, "mouseenter", this.onMouseEnterLeave),
- v(this.$ranges, "mouseleave", this.onMouseEnterLeave),
- v(this.$ranges, "focus", this.onFocus),
- v(this.$ranges, "mousedown", this.onFocus),
- v(this.$ranges, "blur", this.onBlur);
- }
- createElement() {
- this.$el = n({
- className: c("air-datepicker-time", { "-am-pm-": this.dp.ampm }),
- });
- }
- destroy() {
- this.dp.off(i.eventChangeSelectedDate, this.onChangeSelectedDate),
- this.dp.off(
- i.eventChangeLastSelectedDate,
- this.onChangeLastSelectedDate
- ),
- this.$el.parentNode.removeChild(this.$el);
- }
- buildHtml() {
- let {
- ampm: e,
- hours: t,
- displayHours: i,
- minutes: s,
- minHours: a,
- minMinutes: n,
- maxHours: r,
- maxMinutes: h,
- dayPeriod: o,
- opts: { hoursStep: d, minutesStep: c },
- } = this;
- this.$el.innerHTML =
- '' +
- ' '.concat(
- l(i),
- ""
- ) +
- ' :' +
- ' '.concat(
- l(s),
- ""
- ) +
- " ".concat(
- e
- ? "".concat(
- o,
- ""
- )
- : ""
- ) +
- '
";
- }
- defineDOM() {
- let e = (e) => a(e, this.$el);
- (this.$ranges = this.$el.querySelectorAll('[type="range"]')),
- (this.$hours = e('[name="hours"]')),
- (this.$minutes = e('[name="minutes"]')),
- (this.$hoursText = e(".air-datepicker-time--current-hours")),
- (this.$minutesText = e(".air-datepicker-time--current-minutes")),
- (this.$ampm = e(".air-datepicker-time--current-ampm"));
- }
- setTime(e) {
- this.setMinMaxTime(e), this.setCurrentTime(e);
- }
- addTimeToDate(e) {
- e && (e.setHours(this.hours), e.setMinutes(this.minutes));
- }
- setMinMaxTime(e) {
- if ((this.setMinMaxTimeFromOptions(), e)) {
- let { minDate: t, maxDate: i } = this.dp;
- t && u(e, t) && this.setMinTimeFromMinDate(t),
- i && u(e, i) && this.setMaxTimeFromMaxDate(i);
- }
- }
- setCurrentTime(e) {
- let { hours: t, minutes: i } = e ? o(e) : this;
- (this.hours = f(t, this.minHours, this.maxHours)),
- (this.minutes = f(i, this.minMinutes, this.maxMinutes));
- }
- setMinMaxTimeFromOptions() {
- let {
- minHours: e,
- minMinutes: t,
- maxHours: i,
- maxMinutes: s,
- } = this.opts;
- (this.minHours = f(e, 0, 23)),
- (this.minMinutes = f(t, 0, 59)),
- (this.maxHours = f(i, 0, 23)),
- (this.maxMinutes = f(s, 0, 59));
- }
- setMinTimeFromMinDate(e) {
- let { lastSelectedDate: t } = this.dp;
- (this.minHours = e.getHours()),
- t && t.getHours() > e.getHours()
- ? (this.minMinutes = this.opts.minMinutes)
- : (this.minMinutes = e.getMinutes());
- }
- setMaxTimeFromMaxDate(e) {
- let { lastSelectedDate: t } = this.dp;
- (this.maxHours = e.getHours()),
- t && t.getHours() < e.getHours()
- ? (this.maxMinutes = this.opts.maxMinutes)
- : (this.maxMinutes = e.getMinutes());
- }
- getDayPeriod(e, t) {
- let i = e,
- s = Number(e);
- e instanceof Date && ((i = o(e)), (s = Number(i.hours)));
- let a = "am";
- if (t || this.ampm) {
- switch (!0) {
- case 12 === s:
- case s > 11:
- a = "pm";
- }
- s = s % 12 == 0 ? 12 : s % 12;
- }
- return { hours: s, dayPeriod: a };
- }
- updateSliders() {
- (r(this.$hours, { min: this.minHours, max: this.maxHours }).value =
- this.hours),
- (r(this.$minutes, {
- min: this.minMinutes,
- max: this.maxMinutes,
- }).value = this.minutes);
- }
- updateText() {
- (this.$hoursText.innerHTML = l(this.displayHours)),
- (this.$minutesText.innerHTML = l(this.minutes)),
- this.ampm && (this.$ampm.innerHTML = this.dayPeriod);
- }
- set hours(e) {
- this._hours = e;
- let { hours: t, dayPeriod: i } = this.getDayPeriod(e);
- (this.displayHours = t), (this.dayPeriod = i);
- }
- get hours() {
- return this._hours;
- }
- render() {
- this.updateSliders(), this.updateText();
- }
- }
- function E(e, t, i) {
- return (
- t in e
- ? Object.defineProperty(e, t, {
- value: i,
- enumerable: !0,
- configurable: !0,
- writable: !0,
- })
- : (e[t] = i),
- e
- );
- }
- class A {
- constructor(e) {
- let { dp: t, opts: i } = e;
- E(this, "pressedKeys", new Set()),
- E(
- this,
- "hotKeys",
- new Map([
- [
- [
- ["Control", "ArrowRight"],
- ["Control", "ArrowUp"],
- ],
- (e) => e.month++,
- ],
- [
- [
- ["Control", "ArrowLeft"],
- ["Control", "ArrowDown"],
- ],
- (e) => e.month--,
- ],
- [
- [
- ["Shift", "ArrowRight"],
- ["Shift", "ArrowUp"],
- ],
- (e) => e.year++,
- ],
- [
- [
- ["Shift", "ArrowLeft"],
- ["Shift", "ArrowDown"],
- ],
- (e) => e.year--,
- ],
- [
- [
- ["Alt", "ArrowRight"],
- ["Alt", "ArrowUp"],
- ],
- (e) => (e.year += 10),
- ],
- [
- [
- ["Alt", "ArrowLeft"],
- ["Alt", "ArrowDown"],
- ],
- (e) => (e.year -= 10),
- ],
- [["Control", "Shift", "ArrowUp"], (e, t) => t.up()],
- ])
- ),
- E(this, "handleHotKey", (e) => {
- let t = this.hotKeys.get(e),
- i = o(this.getInitialFocusDate());
- t(i, this.dp);
- let { year: s, month: a, date: n } = i,
- r = h(new Date(s, a));
- r < n && (n = r);
- let l = this.dp.getClampedDate(new Date(s, a, n));
- this.dp.setFocusDate(l, { viewDateTransition: !0 });
- }),
- E(this, "isHotKeyPressed", () => {
- let e = !1,
- t = this.pressedKeys.size,
- i = (e) => this.pressedKeys.has(e);
- for (let [s] of this.hotKeys) {
- if (e) break;
- if (Array.isArray(s[0]))
- s.forEach((a) => {
- e || t !== a.length || (e = a.every(i) && s);
- });
- else {
- if (t !== s.length) continue;
- e = s.every(i) && s;
- }
- }
- return e;
- }),
- E(this, "isArrow", (e) => e >= 37 && e <= 40),
- E(this, "onKeyDown", (e) => {
- let { key: t, which: i } = e,
- {
- dp: s,
- dp: { focusDate: a },
- opts: n,
- } = this;
- this.registerKey(t);
- let r = this.isHotKeyPressed();
- if (r) return e.preventDefault(), void this.handleHotKey(r);
- if (this.isArrow(i))
- return e.preventDefault(), void this.focusNextCell(t);
- if ("Enter" === t) {
- if (s.currentView !== n.minView) return void s.down();
- if (a) {
- let e = s._checkIfDateIsSelected(a);
- return void (e
- ? s._handleAlreadySelectedDates(e, a)
- : s.selectDate(a));
- }
- }
- "Escape" === t && this.dp.hide();
- }),
- E(this, "onKeyUp", (e) => {
- this.removeKey(e.key);
- }),
- (this.dp = t),
- (this.opts = i),
- this.init();
- }
- init() {
- this.bindKeyboardEvents();
- }
- bindKeyboardEvents() {
- let { $el: e } = this.dp;
- e.addEventListener("keydown", this.onKeyDown),
- e.addEventListener("keyup", this.onKeyUp);
- }
- destroy() {
- let { $el: e } = this.dp;
- e.removeEventListener("keydown", this.onKeyDown),
- e.removeEventListener("keyup", this.onKeyUp),
- (this.hotKeys = null),
- (this.pressedKeys = null);
- }
- getInitialFocusDate() {
- let {
- focusDate: e,
- currentView: t,
- selectedDates: s,
- parsedViewDate: { year: a, month: n },
- } = this.dp,
- r = e || s[s.length - 1];
- if (!r)
- switch (t) {
- case i.days:
- r = new Date(a, n, new Date().getDate());
- break;
- case i.months:
- r = new Date(a, n, 1);
- break;
- case i.years:
- r = new Date(a, 0, 1);
- }
- return r;
- }
- focusNextCell(e) {
- let t = this.getInitialFocusDate(),
- { currentView: s } = this.dp,
- { days: a, months: n, years: r } = i,
- h = o(t),
- l = h.year,
- d = h.month,
- c = h.date;
- switch (e) {
- case "ArrowLeft":
- s === a && (c -= 1), s === n && (d -= 1), s === r && (l -= 1);
- break;
- case "ArrowUp":
- s === a && (c -= 7), s === n && (d -= 3), s === r && (l -= 4);
- break;
- case "ArrowRight":
- s === a && (c += 1), s === n && (d += 1), s === r && (l += 1);
- break;
- case "ArrowDown":
- s === a && (c += 7), s === n && (d += 3), s === r && (l += 4);
- }
- let u = this.dp.getClampedDate(new Date(l, d, c));
- this.dp.setFocusDate(u, { viewDateTransition: !0 });
- }
- registerKey(e) {
- this.pressedKeys.add(e);
- }
- removeKey(e) {
- this.pressedKeys.delete(e);
- }
- }
- let N = {
- on(e, t) {
- this.__events || (this.__events = {}),
- this.__events[e]
- ? this.__events[e].push(t)
- : (this.__events[e] = [t]);
- },
- off(e, t) {
- this.__events &&
- this.__events[e] &&
- (this.__events[e] = this.__events[e].filter((e) => e !== t));
- },
- removeAllEvents() {
- this.__events = {};
- },
- trigger(e) {
- for (
- var t = arguments.length, i = new Array(t > 1 ? t - 1 : 0), s = 1;
- s < t;
- s++
- )
- i[s - 1] = arguments[s];
- this.__events &&
- this.__events[e] &&
- this.__events[e].forEach((e) => {
- e(...i);
- });
- },
- };
- function I(e, t, i) {
- return (
- t in e
- ? Object.defineProperty(e, t, {
- value: i,
- enumerable: !0,
- configurable: !0,
- writable: !0,
- })
- : (e[t] = i),
- e
- );
- }
- let R = "",
- P = "",
- B = "",
- K = !1;
- class j {
- constructor(e, t) {
- var r = this;
- if (
- (I(this, "viewIndexes", [i.days, i.months, i.years]),
- I(this, "next", () => {
- let { year: e, month: t } = this.parsedViewDate;
- switch (this.currentView) {
- case i.days:
- this.setViewDate(new Date(e, t + 1, 1));
- break;
- case i.months:
- this.setViewDate(new Date(e + 1, t, 1));
- break;
- case i.years:
- this.setViewDate(new Date(e + 10, 0, 1));
- }
- }),
- I(this, "prev", () => {
- let { year: e, month: t } = this.parsedViewDate;
- switch (this.currentView) {
- case i.days:
- this.setViewDate(new Date(e, t - 1, 1));
- break;
- case i.months:
- this.setViewDate(new Date(e - 1, t, 1));
- break;
- case i.years:
- this.setViewDate(new Date(e - 10, 0, 1));
- }
- }),
- I(this, "_finishHide", () => {
- (this.hideAnimation = !1),
- this._destroyComponents(),
- this.$container.removeChild(this.$datepicker);
- }),
- I(this, "setPosition", function (e) {
- let t =
- arguments.length > 1 && void 0 !== arguments[1] && arguments[1];
- if ("function" == typeof (e = e || r.opts.position))
- return void (r.customHide = e({
- $datepicker: r.$datepicker,
- $target: r.$el,
- $pointer: r.$pointer,
- isViewChange: t,
- done: r._finishHide,
- }));
- let i,
- s,
- { isMobile: a } = r.opts,
- n = r.$el.getBoundingClientRect(),
- h = r.$el.getBoundingClientRect(),
- o = r.$datepicker.offsetParent,
- l = r.$el.offsetParent,
- d = r.$datepicker.getBoundingClientRect(),
- c = e.split(" "),
- u = window.scrollY,
- p = window.scrollX,
- m = r.opts.offset,
- g = c[0],
- D = c[1];
- if (a) r.$datepicker.style.cssText = "left: 50%; top: 50%";
- else {
- if (
- (o === l &&
- o !== document.body &&
- ((h = {
- top: r.$el.offsetTop,
- left: r.$el.offsetLeft,
- width: n.width,
- height: r.$el.offsetHeight,
- }),
- (u = 0),
- (p = 0)),
- o !== l && o !== document.body)
- ) {
- let e = o.getBoundingClientRect();
- (h = {
- top: n.top - e.top,
- left: n.left - e.left,
- width: n.width,
- height: n.height,
- }),
- (u = 0),
- (p = 0);
- }
- switch (g) {
- case "top":
- i = h.top - d.height - m;
- break;
- case "right":
- s = h.left + h.width + m;
- break;
- case "bottom":
- i = h.top + h.height + m;
- break;
- case "left":
- s = h.left - d.width - m;
- }
- switch (D) {
- case "top":
- i = h.top;
- break;
- case "right":
- s = h.left + h.width - d.width;
- break;
- case "bottom":
- i = h.top + h.height - d.height;
- break;
- case "left":
- s = h.left;
- break;
- case "center":
- /left|right/.test(g)
- ? (i = h.top + h.height / 2 - d.height / 2)
- : (s = h.left + h.width / 2 - d.width / 2);
- }
- r.$datepicker.style.cssText = "left: "
- .concat(s + p, "px; top: ")
- .concat(i + u, "px");
- }
- }),
- I(this, "_setInputValue", () => {
- let {
- opts: e,
- $altField: t,
- locale: { dateFormat: i },
- } = this,
- { altFieldDateFormat: s, altField: a } = e;
- a && t && (t.value = this._getInputValue(s)),
- (this.$el.value = this._getInputValue(i));
- }),
- I(this, "_getInputValue", (e) => {
- let { selectedDates: t, opts: i } = this,
- { multipleDates: s, multipleDatesSeparator: a } = i;
- if (!t.length) return "";
- let n = "function" == typeof e,
- r = n ? e(s ? t : t[0]) : t.map((t) => this.formatDate(t, e));
- return (r = n ? r : r.join(a)), r;
- }),
- I(this, "_checkIfDateIsSelected", function (e) {
- let t =
- arguments.length > 1 && void 0 !== arguments[1]
- ? arguments[1]
- : i.days,
- s = !1;
- return (
- r.selectedDates.some((i) => {
- let a = u(e, i, t);
- return (s = a && i), a;
- }),
- s
- );
- }),
- I(this, "_scheduleCallAfterTransition", (e) => {
- this._cancelScheduledCall(),
- e && e(!1),
- (this._onTransitionEnd = () => {
- e && e(!0);
- }),
- this.$datepicker.addEventListener(
- "transitionend",
- this._onTransitionEnd,
- { once: !0 }
- );
- }),
- I(this, "_cancelScheduledCall", () => {
- this.$datepicker.removeEventListener(
- "transitionend",
- this._onTransitionEnd
- );
- }),
- I(this, "setViewDate", (e) => {
- if (!((e = b(e)) instanceof Date)) return;
- if (u(e, this.viewDate)) return;
- let t = this.viewDate;
- this.viewDate = e;
- let { onChangeViewDate: s } = this.opts;
- if (s) {
- let { month: e, year: t } = this.parsedViewDate;
- s({ month: e, year: t, decade: this.curDecade });
- }
- this.trigger(i.eventChangeViewDate, e, t);
- }),
- I(this, "setFocusDate", function (e) {
- let t =
- arguments.length > 1 && void 0 !== arguments[1]
- ? arguments[1]
- : {};
- (!e || (e = b(e)) instanceof Date) &&
- ((r.focusDate = e),
- r.opts.range && e && r._handleRangeOnFocus(),
- r.trigger(i.eventChangeFocusDate, e, t));
- }),
- I(this, "setCurrentView", (e) => {
- if (this.viewIndexes.includes(e)) {
- if (
- ((this.currentView = e),
- this.elIsInput && this.visible && this.setPosition(void 0, !0),
- this.trigger(i.eventChangeCurrentView, e),
- !this.views[e])
- ) {
- let t = (this.views[e] = new T({
- dp: this,
- opts: this.opts,
- type: e,
- }));
- this.shouldUpdateDOM && this.$content.appendChild(t.$el);
- }
- this.opts.onChangeView && this.opts.onChangeView(e);
- }
- }),
- I(this, "_updateLastSelectedDate", (e) => {
- (this.lastSelectedDate = e),
- this.trigger(i.eventChangeLastSelectedDate, e);
- }),
- I(this, "destroy", () => {
- let { showEvent: e, isMobile: t } = this.opts,
- i = this.$datepicker.parentNode;
- i && i.removeChild(this.$datepicker),
- this.$el.removeEventListener(e, this._onFocus),
- this.$el.removeEventListener("blur", this._onBlur),
- window.removeEventListener("resize", this._onResize),
- t && this._removeMobileAttributes(),
- this.keyboardNav && this.keyboardNav.destroy(),
- (this.views = null),
- (this.nav = null),
- (this.$datepicker = null),
- (this.opts = null),
- (this.$customContainer = null),
- (this.viewDate = null),
- (this.focusDate = null),
- (this.selectedDates = null),
- (this.rangeDateFrom = null),
- (this.rangeDateTo = null);
- }),
- I(this, "update", (e) => {
- let t = w({}, this.opts);
- w(this.opts, e);
- let {
- timepicker: s,
- buttons: a,
- range: n,
- selectedDates: r,
- isMobile: h,
- } = this.opts,
- o = this.visible || this.treatAsInline;
- this._createMinMaxDates(),
- this._limitViewDateByMaxMinDates(),
- this._handleLocale(),
- !t.selectedDates && r && this.selectDate(r),
- e.view && this.setCurrentView(e.view),
- this._setInputValue(),
- t.range && !n
- ? ((this.rangeDateTo = !1), (this.rangeDateFrom = !1))
- : !t.range &&
- n &&
- this.selectedDates.length &&
- ((this.rangeDateFrom = this.selectedDates[0]),
- (this.rangeDateTo = this.selectedDates[1])),
- t.timepicker && !s
- ? (o && this.timepicker.destroy(),
- (this.timepicker = !1),
- this.$timepicker.parentNode.removeChild(this.$timepicker))
- : !t.timepicker && s && this._addTimepicker(),
- !t.buttons && a
- ? this._addButtons()
- : t.buttons && !a
- ? (this.buttons.destroy(),
- this.$buttons.parentNode.removeChild(this.$buttons))
- : o && t.buttons && a && this.buttons.clearHtml().render(),
- !t.isMobile && h
- ? (this.treatAsInline || B || this._createMobileOverlay(),
- this._addMobileAttributes(),
- this.visible && this._showMobileOverlay())
- : t.isMobile &&
- !h &&
- (this._removeMobileAttributes(),
- this.visible &&
- (B.classList.remove("-active-"),
- "function" != typeof this.opts.position &&
- this.setPosition())),
- o &&
- (this.nav.update(),
- this.views[this.currentView].render(),
- this.currentView === i.days &&
- this.views[this.currentView].renderDayNames());
- }),
- I(this, "isOtherMonth", (e) => {
- let { month: t } = o(e);
- return t !== this.parsedViewDate.month;
- }),
- I(this, "isOtherYear", (e) => {
- let { year: t } = o(e);
- return t !== this.parsedViewDate.year;
- }),
- I(this, "isOtherDecade", (e) => {
- let { year: t } = o(e),
- [i, s] = d(this.viewDate);
- return t < i || t > s;
- }),
- I(this, "_onChangeSelectedDate", (e) => {
- let { silent: t } = e;
- setTimeout(() => {
- this._setInputValue(),
- this.opts.onSelect && !t && this._triggerOnSelect();
- });
- }),
- I(this, "_onChangeFocusedDate", function (e) {
- let { viewDateTransition: t } =
- arguments.length > 1 && void 0 !== arguments[1]
- ? arguments[1]
- : {};
- if (!e) return;
- let i = !1;
- t &&
- (i = r.isOtherMonth(e) || r.isOtherYear(e) || r.isOtherDecade(e)),
- i && r.setViewDate(e);
- }),
- I(this, "_onChangeTime", (e) => {
- let { hours: t, minutes: i } = e,
- s = new Date(),
- {
- lastSelectedDate: a,
- opts: { onSelect: n },
- } = this,
- r = a;
- a || (r = s);
- let h = this.getCell(r, this.currentViewSingular),
- o = h && h.adpCell;
- (o && o.isDisabled) ||
- (r.setHours(t),
- r.setMinutes(i),
- a
- ? (this._setInputValue(), n && this._triggerOnSelect())
- : this.selectDate(r));
- }),
- I(this, "_onFocus", (e) => {
- this.visible || this.show();
- }),
- I(this, "_onBlur", (e) => {
- this.inFocus || !this.visible || this.opts.isMobile || this.hide();
- }),
- I(this, "_onMouseDown", (e) => {
- this.inFocus = !0;
- }),
- I(this, "_onMouseUp", (e) => {
- (this.inFocus = !1), this.$el.focus();
- }),
- I(this, "_onResize", () => {
- this.visible &&
- "function" != typeof this.opts.position &&
- this.setPosition();
- }),
- I(this, "_onClickOverlay", () => {
- this.visible && this.hide();
- }),
- I(this, "isWeekend", (e) => this.opts.weekends.includes(e)),
- I(this, "getClampedDate", (e) => {
- let { minDate: t, maxDate: i } = this,
- s = e;
- return i && p(e, i) ? (s = i) : t && m(e, t) && (s = t), s;
- }),
- (this.$el = a(e)),
- !this.$el)
- )
- return;
- (this.$datepicker = n({ className: "air-datepicker" })),
- (this.opts = w({}, s, t)),
- (this.$customContainer =
- !!this.opts.container && a(this.opts.container)),
- (this.$altField = a(this.opts.altField || !1)),
- R || (R = a("body"));
- let { view: h, startDate: l } = this.opts;
- l || (this.opts.startDate = new Date()),
- "INPUT" === this.$el.nodeName && (this.elIsInput = !0),
- (this.inited = !1),
- (this.visible = !1),
- (this.viewDate = b(this.opts.startDate)),
- (this.focusDate = !1),
- (this.initialReadonly = this.$el.getAttribute("readonly")),
- (this.customHide = !1),
- (this.currentView = h),
- (this.selectedDates = []),
- (this.views = {}),
- (this.keys = []),
- (this.rangeDateFrom = ""),
- (this.rangeDateTo = ""),
- (this.timepickerIsActive = !1),
- (this.treatAsInline = this.opts.inline || !this.elIsInput),
- this.init();
- }
- init() {
- let {
- opts: e,
- treatAsInline: t,
- opts: {
- inline: i,
- isMobile: s,
- selectedDates: a,
- keyboardNav: r,
- onlyTimepicker: h,
- },
- } = this;
- var o;
- K ||
- i ||
- !this.elIsInput ||
- ((K = !0),
- (P = n({ className: (o = j.defaultContainerId), id: o })),
- R.appendChild(P)),
- !s || B || t || this._createMobileOverlay(),
- this._handleLocale(),
- this._bindSubEvents(),
- this._createMinMaxDates(),
- this._limitViewDateByMaxMinDates(),
- this.elIsInput &&
- (i || this._bindEvents(),
- r && !h && (this.keyboardNav = new A({ dp: this, opts: e }))),
- a && this.selectDate(a, { silent: !0 }),
- this.opts.visible && !t && this.show(),
- s && !t && this.$el.setAttribute("readonly", !0),
- t && this._createComponents();
- }
- _createMobileOverlay() {
- (B = n({ className: "air-datepicker-overlay" })), P.appendChild(B);
- }
- _createComponents() {
- let {
- opts: e,
- treatAsInline: t,
- opts: {
- inline: i,
- buttons: s,
- timepicker: a,
- position: n,
- classes: r,
- onlyTimepicker: h,
- isMobile: o,
- },
- } = this;
- this._buildBaseHtml(),
- this.elIsInput && (i || this._setPositionClasses(n)),
- (!i && this.elIsInput) || this.$datepicker.classList.add("-inline-"),
- r && this.$datepicker.classList.add(...r.split(" ")),
- h && this.$datepicker.classList.add("-only-timepicker-"),
- o && !t && this._addMobileAttributes(),
- (this.views[this.currentView] = new T({
- dp: this,
- type: this.currentView,
- opts: e,
- })),
- (this.nav = new V({ dp: this, opts: e })),
- a && this._addTimepicker(),
- s && this._addButtons(),
- this.$content.appendChild(this.views[this.currentView].$el),
- this.$nav.appendChild(this.nav.$el);
- }
- _destroyComponents() {
- for (let e in this.views) this.views[e].destroy();
- (this.views = {}),
- this.nav.destroy(),
- this.timepicker && this.timepicker.destroy();
- }
- _addMobileAttributes() {
- B.addEventListener("click", this._onClickOverlay),
- this.$datepicker.classList.add("-is-mobile-"),
- this.$el.setAttribute("readonly", !0);
- }
- _removeMobileAttributes() {
- B.removeEventListener("click", this._onClickOverlay),
- this.$datepicker.classList.remove("-is-mobile-"),
- this.initialReadonly ||
- "" === this.initialReadonly ||
- this.$el.removeAttribute("readonly");
- }
- _createMinMaxDates() {
- let { minDate: e, maxDate: t } = this.opts;
- (this.minDate = !!e && b(e)), (this.maxDate = !!t && b(t));
- }
- _addTimepicker() {
- (this.$timepicker = n({ className: "air-datepicker--time" })),
- this.$datepicker.appendChild(this.$timepicker),
- (this.timepicker = new O({ dp: this, opts: this.opts })),
- this.$timepicker.appendChild(this.timepicker.$el);
- }
- _addButtons() {
- (this.$buttons = n({ className: "air-datepicker--buttons" })),
- this.$datepicker.appendChild(this.$buttons),
- (this.buttons = new H({ dp: this, opts: this.opts })),
- this.$buttons.appendChild(this.buttons.$el);
- }
- _bindSubEvents() {
- this.on(i.eventChangeSelectedDate, this._onChangeSelectedDate),
- this.on(i.eventChangeFocusDate, this._onChangeFocusedDate),
- this.on(i.eventChangeTime, this._onChangeTime);
- }
- _buildBaseHtml() {
- let { inline: e } = this.opts;
- var t, i;
- this.elIsInput
- ? e
- ? ((t = this.$datepicker),
- (i = this.$el).parentNode.insertBefore(t, i.nextSibling))
- : this.$container.appendChild(this.$datepicker)
- : this.$el.appendChild(this.$datepicker),
- (this.$datepicker.innerHTML =
- ''),
- (this.$content = a(".air-datepicker--content", this.$datepicker)),
- (this.$pointer = a(".air-datepicker--pointer", this.$datepicker)),
- (this.$nav = a(".air-datepicker--navigation", this.$datepicker));
- }
- _handleLocale() {
- let {
- locale: e,
- dateFormat: t,
- firstDay: i,
- timepicker: s,
- onlyTimepicker: a,
- timeFormat: n,
- dateTimeSeparator: r,
- } = this.opts;
- var h;
- (this.locale = ((h = e), JSON.parse(JSON.stringify(h)))),
- t && (this.locale.dateFormat = t),
- void 0 !== n && "" !== n && (this.locale.timeFormat = n);
- let { timeFormat: o } = this.locale;
- if (
- ("" !== i && (this.locale.firstDay = i), s && "function" != typeof t)
- ) {
- let e = o ? r : "";
- this.locale.dateFormat = [this.locale.dateFormat, o || ""].join(e);
- }
- a &&
- "function" != typeof t &&
- (this.locale.dateFormat = this.locale.timeFormat);
- }
- _setPositionClasses(e) {
- if ("function" == typeof e)
- return void this.$datepicker.classList.add("-custom-position-");
- let t = (e = e.split(" "))[0],
- i = e[1],
- s = "air-datepicker -"
- .concat(t, "-")
- .concat(i, "- -from-")
- .concat(t, "-");
- this.$datepicker.classList.add(...s.split(" "));
- }
- _bindEvents() {
- this.$el.addEventListener(this.opts.showEvent, this._onFocus),
- this.$el.addEventListener("blur", this._onBlur),
- this.$datepicker.addEventListener("mousedown", this._onMouseDown),
- this.$datepicker.addEventListener("mouseup", this._onMouseUp),
- window.addEventListener("resize", this._onResize);
- }
- _limitViewDateByMaxMinDates() {
- let { viewDate: e, minDate: t, maxDate: i } = this;
- i && p(e, i) && this.setViewDate(i),
- t && m(e, t) && this.setViewDate(t);
- }
- formatDate() {
- let e =
- arguments.length > 0 && void 0 !== arguments[0]
- ? arguments[0]
- : this.viewDate,
- t = arguments.length > 1 ? arguments[1] : void 0;
- if (((e = b(e)), !(e instanceof Date))) return;
- let i = t,
- s = this.locale,
- a = o(e),
- n = d(e),
- r = j.replacer,
- h = "am";
- this.opts.timepicker &&
- this.timepicker &&
- (h = this.timepicker.getDayPeriod(e).dayPeriod);
- let l = {
- T: e.getTime(),
- m: a.minutes,
- mm: a.fullMinutes,
- h: a.hours12,
- hh: a.fullHours12,
- H: a.hours,
- HH: a.fullHours,
- aa: h,
- AA: h.toUpperCase(),
- E: s.daysShort[a.day],
- EEEE: s.days[a.day],
- d: a.date,
- dd: a.fullDate,
- M: a.month + 1,
- MM: a.fullMonth,
- MMM: s.monthsShort[a.month],
- MMMM: s.months[a.month],
- yy: a.year.toString().slice(-2),
- yyyy: a.year,
- yyyy1: n[0],
- yyyy2: n[1],
- };
- for (let [e, t] of Object.entries(l)) i = r(i, k(e), t);
- return i;
- }
- down(e) {
- this._handleUpDownActions(e, "down");
- }
- up(e) {
- this._handleUpDownActions(e, "up");
- }
- selectDate(e) {
- let t,
- s =
- arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {},
- { currentView: a, parsedViewDate: n, selectedDates: r } = this,
- { updateTime: h } = s,
- {
- moveToOtherMonthsOnSelect: o,
- moveToOtherYearsOnSelect: l,
- multipleDates: d,
- range: c,
- autoClose: u,
- } = this.opts,
- m = r.length;
- if (Array.isArray(e))
- return (
- e.forEach((e) => {
- this.selectDate(e, s);
- }),
- new Promise((e) => {
- setTimeout(e);
- })
- );
- if ((e = b(e)) instanceof Date) {
- if (
- (a === i.days &&
- e.getMonth() !== n.month &&
- o &&
- (t = new Date(e.getFullYear(), e.getMonth(), 1)),
- a === i.years &&
- e.getFullYear() !== n.year &&
- l &&
- (t = new Date(e.getFullYear(), 0, 1)),
- t && this.setViewDate(t),
- d && !c)
- ) {
- if (m === d) return;
- this._checkIfDateIsSelected(e) || r.push(e);
- } else if (c)
- switch (m) {
- case 1:
- r.push(e),
- this.rangeDateTo || (this.rangeDateTo = e),
- p(this.rangeDateFrom, this.rangeDateTo) &&
- ((this.rangeDateTo = this.rangeDateFrom),
- (this.rangeDateFrom = e)),
- (this.selectedDates = [this.rangeDateFrom, this.rangeDateTo]);
- break;
- case 2:
- (this.selectedDates = [e]),
- (this.rangeDateFrom = e),
- (this.rangeDateTo = "");
- break;
- default:
- (this.selectedDates = [e]), (this.rangeDateFrom = e);
- }
- else this.selectedDates = [e];
- return (
- this.trigger(i.eventChangeSelectedDate, {
- action: i.actionSelectDate,
- silent: null == s ? void 0 : s.silent,
- date: e,
- updateTime: h,
- }),
- this._updateLastSelectedDate(e),
- u &&
- !this.timepickerIsActive &&
- this.visible &&
- (d || c ? c && 1 === m && this.hide() : this.hide()),
- new Promise((e) => {
- setTimeout(e);
- })
- );
- }
- }
- unselectDate(e) {
- let t = this.selectedDates,
- s = this;
- if ((e = b(e)) instanceof Date)
- return t.some((a, n) => {
- if (u(a, e))
- return (
- t.splice(n, 1),
- s.selectedDates.length
- ? s._updateLastSelectedDate(
- s.selectedDates[s.selectedDates.length - 1]
- )
- : ((s.rangeDateFrom = ""),
- (s.rangeDateTo = ""),
- s._updateLastSelectedDate(!1)),
- this.trigger(i.eventChangeSelectedDate, {
- action: i.actionUnselectDate,
- date: e,
- }),
- !0
- );
- });
- }
- replaceDate(e, t) {
- let s = this.selectedDates.find((t) => u(t, e, this.currentView)),
- a = this.selectedDates.indexOf(s);
- a < 0 ||
- u(this.selectedDates[a], t, this.currentView) ||
- ((this.selectedDates[a] = t),
- this.trigger(i.eventChangeSelectedDate, {
- action: i.actionSelectDate,
- date: t,
- updateTime: !0,
- }),
- this._updateLastSelectedDate(t));
- }
- clear() {
- let e =
- arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {};
- return (
- (this.selectedDates = []),
- (this.rangeDateFrom = !1),
- (this.rangeDateTo = !1),
- this.trigger(i.eventChangeSelectedDate, {
- action: i.actionUnselectDate,
- silent: e.silent,
- }),
- new Promise((e) => {
- setTimeout(e);
- })
- );
- }
- show() {
- let { onShow: e, isMobile: t } = this.opts;
- this._cancelScheduledCall(),
- this.visible || this.hideAnimation || this._createComponents(),
- this.setPosition(this.opts.position),
- this.$datepicker.classList.add("-active-"),
- (this.visible = !0),
- e && this._scheduleCallAfterTransition(e),
- t && this._showMobileOverlay();
- }
- hide() {
- let { onHide: e, isMobile: t } = this.opts,
- i = this._hasTransition();
- (this.visible = !1),
- (this.hideAnimation = !0),
- this.$datepicker.classList.remove("-active-"),
- this.customHide && this.customHide(),
- this.elIsInput && this.$el.blur(),
- this._scheduleCallAfterTransition((t) => {
- !this.customHide && ((t && i) || (!t && !i)) && this._finishHide(),
- e && e(t);
- }),
- t && B.classList.remove("-active-");
- }
- _triggerOnSelect() {
- let e = [],
- t = [],
- {
- selectedDates: i,
- locale: s,
- opts: { onSelect: a, multipleDates: n, range: r },
- } = this,
- h = n || r,
- o = "function" == typeof s.dateFormat;
- i.length &&
- ((e = i.map(g)),
- (t = o
- ? n
- ? s.dateFormat(e)
- : e.map((e) => s.dateFormat(e))
- : e.map((e) => this.formatDate(e, s.dateFormat)))),
- a({
- date: h ? e : e[0],
- formattedDate: h ? t : t[0],
- datepicker: this,
- });
- }
- _handleAlreadySelectedDates(e, t) {
- let { range: i, toggleSelected: s } = this.opts;
- i
- ? s
- ? this.unselectDate(t)
- : 2 !== this.selectedDates.length && this.selectDate(t)
- : s && this.unselectDate(t),
- s || this._updateLastSelectedDate(e);
- }
- _handleUpDownActions(e, t) {
- if (!((e = b(e || this.focusDate || this.viewDate)) instanceof Date))
- return;
- let i = "up" === t ? this.viewIndex + 1 : this.viewIndex - 1;
- i > 2 && (i = 2),
- i < 0 && (i = 0),
- this.setViewDate(new Date(e.getFullYear(), e.getMonth(), 1)),
- this.setCurrentView(this.viewIndexes[i]);
- }
- _handleRangeOnFocus() {
- 1 === this.selectedDates.length &&
- (p(this.selectedDates[0], this.focusDate)
- ? ((this.rangeDateTo = this.selectedDates[0]),
- (this.rangeDateFrom = this.focusDate))
- : ((this.rangeDateTo = this.focusDate),
- (this.rangeDateFrom = this.selectedDates[0])));
- }
- getCell(e) {
- let t =
- arguments.length > 1 && void 0 !== arguments[1]
- ? arguments[1]
- : i.day;
- if (!((e = b(e)) instanceof Date)) return;
- let { year: s, month: a, date: n } = o(e),
- r = '[data-year="'.concat(s, '"]'),
- h = '[data-month="'.concat(a, '"]'),
- l = '[data-date="'.concat(n, '"]'),
- d = {
- [i.day]: "".concat(r).concat(h).concat(l),
- [i.month]: "".concat(r).concat(h),
- [i.year]: "".concat(r),
- };
- return this.views[this.currentView].$el.querySelector(d[t]);
- }
- _showMobileOverlay() {
- B.classList.add("-active-");
- }
- _hasTransition() {
- return (
- window
- .getComputedStyle(this.$datepicker)
- .getPropertyValue("transition-duration")
- .split(", ")
- .reduce((e, t) => parseFloat(t) + e, 0) > 0
- );
- }
- get shouldUpdateDOM() {
- return this.visible || this.treatAsInline;
- }
- get parsedViewDate() {
- return o(this.viewDate);
- }
- get currentViewSingular() {
- return this.currentView.slice(0, -1);
- }
- get curDecade() {
- return d(this.viewDate);
- }
- get viewIndex() {
- return this.viewIndexes.indexOf(this.currentView);
- }
- get isFinalView() {
- return this.currentView === i.years;
- }
- get hasSelectedDates() {
- return this.selectedDates.length > 0;
- }
- get isMinViewReached() {
- return (
- this.currentView === this.opts.minView || this.currentView === i.days
- );
- }
- get $container() {
- return this.$customContainer || P;
- }
- static replacer(e, t, i) {
- return e.replace(t, function (e, t, s, a) {
- return t + i + a;
- });
- }
- }
- var U;
- const datepicker = new AirDatepicker("input[fetchdatetimepicker]");
- return (
- I(j, "defaults", s),
- I(j, "version", "3.3.1"),
- I(j, "defaultContainerId", "air-datepicker-global-container"),
- (U = j.prototype),
- Object.assign(U, N),
- t.default
- );
- })();
-});
diff --git a/src/ui/static/js/air-datepicker/index.es.js b/src/ui/static/js/air-datepicker/index.es.js
deleted file mode 100644
index 4074f38eb..000000000
--- a/src/ui/static/js/air-datepicker/index.es.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import AirDatepicker from "air-datepicker";
-export default AirDatepicker;
diff --git a/src/ui/static/js/air-datepicker/locale/ar.d.ts b/src/ui/static/js/air-datepicker/locale/ar.d.ts
deleted file mode 100644
index 5b79468ec..000000000
--- a/src/ui/static/js/air-datepicker/locale/ar.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/ar' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const ar: AirDatepickerLocale;
-
- export default ar;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/ar.js b/src/ui/static/js/air-datepicker/locale/ar.js
deleted file mode 100644
index 828683a7b..000000000
--- a/src/ui/static/js/air-datepicker/locale/ar.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['الأحد', 'الأثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعه', 'السبت'],
- daysShort: ['الأحد', 'الأثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعه', 'السبت'],
- daysMin: ['الأحد', 'الأثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعه', 'السبت'],
- months: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'اكتوبر', 'نوفمبر', 'ديسمبر'],
- monthsShort: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'اكتوبر', 'نوفمبر', 'ديسمبر'],
- today: 'اليوم',
- clear: 'حذف',
- dateFormat: 'dd/MM/yyyy',
- timeFormat: 'hh:mm aa',
- firstDay: 0
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/cs.d.ts b/src/ui/static/js/air-datepicker/locale/cs.d.ts
deleted file mode 100644
index 5e91529bb..000000000
--- a/src/ui/static/js/air-datepicker/locale/cs.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/cs' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const cs: AirDatepickerLocale;
-
- export default cs;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/cs.js b/src/ui/static/js/air-datepicker/locale/cs.js
deleted file mode 100644
index 65062439c..000000000
--- a/src/ui/static/js/air-datepicker/locale/cs.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Neděle', 'Pondělí', 'Úterý', 'Středa', 'Čtvrtek', 'Pátek', 'Sobota'],
- daysShort: ['Ne', 'Po', 'Út', 'St', 'Čt', 'Pá', 'So'],
- daysMin: ['Ne', 'Po', 'Út', 'St', 'Čt', 'Pá', 'So'],
- months: ['Leden', 'Únor', 'Březen', 'Duben', 'Květen', 'Červen', 'Červenec', 'Srpen', 'Září', 'Říjen', 'Listopad', 'Prosinec'],
- monthsShort: ['Led', 'Úno', 'Bře', 'Dub', 'Kvě', 'Čvn', 'Čvc', 'Srp', 'Zář', 'Říj', 'Lis', 'Pro'],
- today: 'Dnes',
- clear: 'Vymazat',
- dateFormat: 'dd.MM.yyyy',
- timeFormat: 'HH:mm',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/da.d.ts b/src/ui/static/js/air-datepicker/locale/da.d.ts
deleted file mode 100644
index 173e75805..000000000
--- a/src/ui/static/js/air-datepicker/locale/da.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/da' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const da: AirDatepickerLocale;
-
- export default da;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/da.js b/src/ui/static/js/air-datepicker/locale/da.js
deleted file mode 100644
index 7f527b4a2..000000000
--- a/src/ui/static/js/air-datepicker/locale/da.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag'],
- daysShort: ['Søn', 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør'],
- daysMin: ['Sø', 'Ma', 'Ti', 'On', 'To', 'Fr', 'Lø'],
- months: ['Januar', 'Februar', 'Marts', 'April', 'Maj', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'December'],
- monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
- today: 'I dag',
- clear: 'Nulstil',
- dateFormat: 'dd/MM/yyyy',
- timeFormat: 'HH:mm',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/de.d.ts b/src/ui/static/js/air-datepicker/locale/de.d.ts
deleted file mode 100644
index ac35fc8ec..000000000
--- a/src/ui/static/js/air-datepicker/locale/de.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/de' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const de: AirDatepickerLocale;
-
- export default de;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/de.js b/src/ui/static/js/air-datepicker/locale/de.js
deleted file mode 100644
index da9c582a1..000000000
--- a/src/ui/static/js/air-datepicker/locale/de.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
- daysShort: ['Son', 'Mon', 'Die', 'Mit', 'Don', 'Fre', 'Sam'],
- daysMin: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
- months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
- monthsShort: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
- today: 'Heute',
- clear: 'Aufräumen',
- dateFormat: 'dd.MM.yyyy',
- timeFormat: 'HH:ii',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/en.d.ts b/src/ui/static/js/air-datepicker/locale/en.d.ts
deleted file mode 100644
index 060236fc8..000000000
--- a/src/ui/static/js/air-datepicker/locale/en.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/en' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const en: AirDatepickerLocale;
-
- export default en;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/en.js b/src/ui/static/js/air-datepicker/locale/en.js
deleted file mode 100644
index 7788883c2..000000000
--- a/src/ui/static/js/air-datepicker/locale/en.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
- daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
- daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
- months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
- monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
- today: 'Today',
- clear: 'Clear',
- dateFormat: 'MM/dd/yyyy',
- timeFormat: 'hh:mm aa',
- firstDay: 0
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/es.d.ts b/src/ui/static/js/air-datepicker/locale/es.d.ts
deleted file mode 100644
index 2c7e2b041..000000000
--- a/src/ui/static/js/air-datepicker/locale/es.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/es' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const es: AirDatepickerLocale;
-
- export default es;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/es.js b/src/ui/static/js/air-datepicker/locale/es.js
deleted file mode 100644
index a5a1886fe..000000000
--- a/src/ui/static/js/air-datepicker/locale/es.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
- daysShort: ['Dom', 'Lun', 'Mar', 'Mie', 'Jue', 'Vie', 'Sab'],
- daysMin: ['Do', 'Lu', 'Ma', 'Mi', 'Ju', 'Vi', 'Sa'],
- months: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
- monthsShort: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'],
- today: 'Hoy',
- clear: 'Limpiar',
- dateFormat: 'dd/MM/yyyy',
- timeFormat: 'hh:mm aa',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/fi.d.ts b/src/ui/static/js/air-datepicker/locale/fi.d.ts
deleted file mode 100644
index 6b8f7e5d0..000000000
--- a/src/ui/static/js/air-datepicker/locale/fi.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/fi' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const fi: AirDatepickerLocale;
-
- export default fi;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/fi.js b/src/ui/static/js/air-datepicker/locale/fi.js
deleted file mode 100644
index c4b538b54..000000000
--- a/src/ui/static/js/air-datepicker/locale/fi.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Sunnuntai', 'Maanantai', 'Tiistai', 'Keskiviikko', 'Torstai', 'Perjantai', 'Lauantai'],
- daysShort: ['Su', 'Ma', 'Ti', 'Ke', 'To', 'Pe', 'La'],
- daysMin: ['Su', 'Ma', 'Ti', 'Ke', 'To', 'Pe', 'La'],
- months: ['Tammikuu', 'Helmikuu', 'Maaliskuu', 'Huhtikuu', 'Toukokuu', 'Kesäkuu', 'Heinäkuu', 'Elokuu', 'Syyskuu', 'Lokakuu', 'Marraskuu', 'Joulukuu'],
- monthsShort: ['Tammi', 'Helmi', 'Maalis', 'Huhti', 'Touko', 'Kesä', 'Heinä', 'Elo', 'Syys', 'Loka', 'Marras', 'Joulu'],
- today: 'Tänään',
- clear: 'Tyhjennä',
- dateFormat: 'dd.MM.yyyy',
- timeFormat: 'HH:mm',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/fr.d.ts b/src/ui/static/js/air-datepicker/locale/fr.d.ts
deleted file mode 100644
index 9cc25a55b..000000000
--- a/src/ui/static/js/air-datepicker/locale/fr.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/fr' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const fr: AirDatepickerLocale;
-
- export default fr;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/fr.js b/src/ui/static/js/air-datepicker/locale/fr.js
deleted file mode 100644
index 11ab7fe31..000000000
--- a/src/ui/static/js/air-datepicker/locale/fr.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'],
- daysShort: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
- daysMin: ['Di', 'Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa'],
- months: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
- monthsShort: ['Jan', 'Fév', 'Mars', 'Avr', 'Mai', 'Juin', 'Juil', 'Août', 'Sep', 'Oct', 'Nov', 'Dec'],
- today: "Aujourd'hui",
- clear: 'Effacer',
- dateFormat: 'dd/MM/yyyy',
- timeFormat: 'HH:mm',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/hu.d.ts b/src/ui/static/js/air-datepicker/locale/hu.d.ts
deleted file mode 100644
index a6558b97a..000000000
--- a/src/ui/static/js/air-datepicker/locale/hu.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/hu' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const hu: AirDatepickerLocale;
-
- export default hu;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/hu.js b/src/ui/static/js/air-datepicker/locale/hu.js
deleted file mode 100644
index 30c5d38ef..000000000
--- a/src/ui/static/js/air-datepicker/locale/hu.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Vasárnap', 'Hétfő', 'Kedd', 'Szerda', 'Csütörtök', 'Péntek', 'Szombat'],
- daysShort: ['Va', 'Hé', 'Ke', 'Sze', 'Cs', 'Pé', 'Szo'],
- daysMin: ['V', 'H', 'K', 'Sz', 'Cs', 'P', 'Sz'],
- months: ['Január', 'Február', 'Március', 'Április', 'Május', 'Június', 'Július', 'Augusztus', 'Szeptember', 'Október', 'November', 'December'],
- monthsShort: ['Jan', 'Feb', 'Már', 'Ápr', 'Máj', 'Jún', 'Júl', 'Aug', 'Szep', 'Okt', 'Nov', 'Dec'],
- today: 'Ma',
- clear: 'Törlés',
- dateFormat: 'yyyy-MM-dd',
- timeFormat: 'hh:mm aa',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/it.d.ts b/src/ui/static/js/air-datepicker/locale/it.d.ts
deleted file mode 100644
index ca8aecd37..000000000
--- a/src/ui/static/js/air-datepicker/locale/it.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/it' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const it: AirDatepickerLocale;
-
- export default it;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/it.js b/src/ui/static/js/air-datepicker/locale/it.js
deleted file mode 100644
index 12b663d6d..000000000
--- a/src/ui/static/js/air-datepicker/locale/it.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato'],
- daysShort: ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'],
- daysMin: ['Do', 'Lu', 'Ma', 'Me', 'Gi', 'Ve', 'Sa'],
- months: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'],
- monthsShort: ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'],
- today: 'Oggi',
- clear: 'Cancella',
- dateFormat: 'dd/MM/yyyy',
- timeFormat: 'HH:mm',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/ja.d.ts b/src/ui/static/js/air-datepicker/locale/ja.d.ts
deleted file mode 100644
index da763b250..000000000
--- a/src/ui/static/js/air-datepicker/locale/ja.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/ja' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const ja: AirDatepickerLocale;
-
- export default ja;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/ja.js b/src/ui/static/js/air-datepicker/locale/ja.js
deleted file mode 100644
index 1228fc60a..000000000
--- a/src/ui/static/js/air-datepicker/locale/ja.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'],
- daysShort: ['日', '月', '火', '水', '木', '金', '土'],
- daysMin: ['日', '月', '火', '水', '木', '金', '土'],
- months: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
- monthsShort: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
- today: '今日',
- clear: 'クリア',
- dateFormat: 'yyyy/MM/dd',
- timeFormat: 'HH:mm',
- firstDay: 0
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/ko.d.ts b/src/ui/static/js/air-datepicker/locale/ko.d.ts
deleted file mode 100644
index db244b076..000000000
--- a/src/ui/static/js/air-datepicker/locale/ko.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/ko' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const ko: AirDatepickerLocale;
-
- export default ko;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/ko.js b/src/ui/static/js/air-datepicker/locale/ko.js
deleted file mode 100644
index 092901d71..000000000
--- a/src/ui/static/js/air-datepicker/locale/ko.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['일요일', '월요일', '화요일', '수요일', '목요일', '금요일', '토요일'],
- daysShort: ['일', '월', '화', '수', '목', '금', '토'],
- daysMin: ['일', '월', '화', '수', '목', '금', '토'],
- months: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
- monthsShort: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
- today: '오늘',
- clear: '초기화',
- dateFormat: 'MM/dd/yyyy',
- timeFormat: 'hh:mm aa',
- firstDay: 0
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/nl.d.ts b/src/ui/static/js/air-datepicker/locale/nl.d.ts
deleted file mode 100644
index ce294eec9..000000000
--- a/src/ui/static/js/air-datepicker/locale/nl.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/nl' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const nl: AirDatepickerLocale;
-
- export default nl;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/nl.js b/src/ui/static/js/air-datepicker/locale/nl.js
deleted file mode 100644
index 3c03fd2f3..000000000
--- a/src/ui/static/js/air-datepicker/locale/nl.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'],
- daysShort: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
- daysMin: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
- months: ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni', 'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'],
- monthsShort: ['Jan', 'Feb', 'Mrt', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
- today: 'Vandaag',
- clear: 'Legen',
- dateFormat: 'dd-MM-yyyy',
- timeFormat: 'HH:mm',
- firstDay: 0
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/pl.d.ts b/src/ui/static/js/air-datepicker/locale/pl.d.ts
deleted file mode 100644
index a053d09e6..000000000
--- a/src/ui/static/js/air-datepicker/locale/pl.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/pl' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const pl: AirDatepickerLocale;
-
- export default pl;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/pl.js b/src/ui/static/js/air-datepicker/locale/pl.js
deleted file mode 100644
index 6bef2c36d..000000000
--- a/src/ui/static/js/air-datepicker/locale/pl.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Niedziela', 'Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek', 'Sobota'],
- daysShort: ['Nie', 'Pon', 'Wto', 'Śro', 'Czw', 'Pią', 'Sob'],
- daysMin: ['Nd', 'Pn', 'Wt', 'Śr', 'Czw', 'Pt', 'So'],
- months: ['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień'],
- monthsShort: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'],
- today: 'Dzisiaj',
- clear: 'Wyczyść',
- dateFormat: 'yyyy-MM-dd',
- timeFormat: 'hh:mm:aa',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/pt-BR.d.ts b/src/ui/static/js/air-datepicker/locale/pt-BR.d.ts
deleted file mode 100644
index 6c1c6baf4..000000000
--- a/src/ui/static/js/air-datepicker/locale/pt-BR.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/pt-BR' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const pt-BR: AirDatepickerLocale;
-
- export default pt-BR;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/pt-BR.js b/src/ui/static/js/air-datepicker/locale/pt-BR.js
deleted file mode 100644
index 455b3a5aa..000000000
--- a/src/ui/static/js/air-datepicker/locale/pt-BR.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Domingo', 'Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta', 'Sábado'],
- daysShort: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab'],
- daysMin: ['Do', 'Se', 'Te', 'Qu', 'Qu', 'Se', 'Sa'],
- months: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
- monthsShort: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'],
- today: 'Hoje',
- clear: 'Limpar',
- dateFormat: 'dd/MM/yyyy',
- timeFormat: 'HH:mm',
- firstDay: 0
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/pt.d.ts b/src/ui/static/js/air-datepicker/locale/pt.d.ts
deleted file mode 100644
index 8881f43e7..000000000
--- a/src/ui/static/js/air-datepicker/locale/pt.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/pt' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const pt: AirDatepickerLocale;
-
- export default pt;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/pt.js b/src/ui/static/js/air-datepicker/locale/pt.js
deleted file mode 100644
index ff2c0cda6..000000000
--- a/src/ui/static/js/air-datepicker/locale/pt.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Domingo', 'Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta', 'Sábado'],
- daysShort: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab'],
- daysMin: ['Do', 'Se', 'Te', 'Qa', 'Qi', 'Sx', 'Sa'],
- months: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
- monthsShort: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'],
- today: 'Hoje',
- clear: 'Limpar',
- dateFormat: 'dd/MM/yyyy',
- timeFormat: 'HH:mm',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/ro.d.ts b/src/ui/static/js/air-datepicker/locale/ro.d.ts
deleted file mode 100644
index 7bfde1a1f..000000000
--- a/src/ui/static/js/air-datepicker/locale/ro.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/ro' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const ro: AirDatepickerLocale;
-
- export default ro;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/ro.js b/src/ui/static/js/air-datepicker/locale/ro.js
deleted file mode 100644
index a65920d0d..000000000
--- a/src/ui/static/js/air-datepicker/locale/ro.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Duminică', 'Luni', 'Marţi', 'Miercuri', 'Joi', 'Vineri', 'Sâmbătă'],
- daysShort: ['Dum', 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sâm'],
- daysMin: ['D', 'L', 'Ma', 'Mi', 'J', 'V', 'S'],
- months: ['Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai', 'Iunie', 'Iulie', 'August', 'Septembrie', 'Octombrie', 'Noiembrie', 'Decembrie'],
- monthsShort: ['Ian', 'Feb', 'Mar', 'Apr', 'Mai', 'Iun', 'Iul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'],
- today: 'Azi',
- clear: 'Şterge',
- dateFormat: 'dd.MM.yyyy',
- timeFormat: 'HH:mm',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/ru.d.ts b/src/ui/static/js/air-datepicker/locale/ru.d.ts
deleted file mode 100644
index 0d2e8fe5c..000000000
--- a/src/ui/static/js/air-datepicker/locale/ru.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/ru' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const ru: AirDatepickerLocale;
-
- export default ru;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/ru.js b/src/ui/static/js/air-datepicker/locale/ru.js
deleted file mode 100644
index 8910ba5bf..000000000
--- a/src/ui/static/js/air-datepicker/locale/ru.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'],
- daysShort: ['Вос', 'Пон', 'Вто', 'Сре', 'Чет', 'Пят', 'Суб'],
- daysMin: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
- months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
- monthsShort: ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
- today: 'Сегодня',
- clear: 'Очистить',
- dateFormat: 'dd.MM.yyyy',
- timeFormat: 'HH:mm',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/si.d.ts b/src/ui/static/js/air-datepicker/locale/si.d.ts
deleted file mode 100644
index 59b6c2b1a..000000000
--- a/src/ui/static/js/air-datepicker/locale/si.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/si' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const si: AirDatepickerLocale;
-
- export default si;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/si.js b/src/ui/static/js/air-datepicker/locale/si.js
deleted file mode 100644
index fa5878642..000000000
--- a/src/ui/static/js/air-datepicker/locale/si.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['ඉරිදා', 'සදුදා', 'අඟහරැවදා', 'බදාදා', 'බ්රහස්පතින්', 'සිකුරාදා', 'සෙනසුරාදා'],
- daysShort: ['ඉරිදා', 'සදුදා', 'අඟහ', 'බදාදා', 'බ්රහස්', 'සිකුරා', 'සෙන'],
- daysMin: ['ඉරි', 'සදු', 'අඟ', 'බදා', 'බ්රහ', 'සිකු', 'සෙ'],
- months: ['ජනවාරි', 'පෙබරවාරි', 'මාර්තු', 'අප්රේල්', 'මැයි', 'ජූනි', 'ජූලි', 'අගෝස්තු', 'සැප්තැම්බර්', 'ඔක්තෝබර්', 'නොවැම්බර්', 'දෙසැම්බර්'],
- monthsShort: ['ජන', 'පෙබ', 'මාර්', 'අප්රේල්', 'මැයි', 'ජූනි', 'ජූලි', 'අගෝ', 'සැප්', 'ඔක්', 'නොවැ', 'දෙසැ'],
- today: 'අද',
- clear: 'යලි සකසන්න',
- dateFormat: 'yyyy-mm-dd',
- timeFormat: 'hh:ii aa',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/sk.d.ts b/src/ui/static/js/air-datepicker/locale/sk.d.ts
deleted file mode 100644
index e0f2d0a28..000000000
--- a/src/ui/static/js/air-datepicker/locale/sk.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/sk' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const sk: AirDatepickerLocale;
-
- export default sk;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/sk.js b/src/ui/static/js/air-datepicker/locale/sk.js
deleted file mode 100644
index b3146e3d9..000000000
--- a/src/ui/static/js/air-datepicker/locale/sk.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Nedeľa', 'Pondelok', 'Utorok', 'Streda', 'Štvrtok', 'Piatok', 'Sobota'],
- daysShort: ['Ned', 'Pon', 'Uto', 'Str', 'Štv', 'Pia', 'Sob'],
- daysMin: ['Ne', 'Po', 'Ut', 'St', 'Št', 'Pi', 'So'],
- months: ['Január', 'Február', 'Marec', 'Apríl', 'Máj', 'Jún', 'Júl', 'August', 'September', 'Október', 'November', 'December'],
- monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'Máj', 'Jún', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
- today: 'Dnes',
- clear: 'Vymazať',
- dateFormat: 'dd.MM.yyyy',
- timeFormat: 'HH:mm',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/sv.d.ts b/src/ui/static/js/air-datepicker/locale/sv.d.ts
deleted file mode 100644
index c8f20bd1b..000000000
--- a/src/ui/static/js/air-datepicker/locale/sv.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/sv' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const sv: AirDatepickerLocale;
-
- export default sv;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/sv.js b/src/ui/static/js/air-datepicker/locale/sv.js
deleted file mode 100644
index 5d775c3d8..000000000
--- a/src/ui/static/js/air-datepicker/locale/sv.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Söndag', 'Måndag', 'Tisdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lördag'],
- daysShort: ['Sön', 'Mån', 'Tis', 'Ons', 'Tor', 'Fre', 'Lör'],
- daysMin: ['Sö', 'Må', 'Ti', 'On', 'To', 'Fr', 'Lö'],
- months: ['Januari', 'Februari', 'Mars', 'April', 'Maj', 'Juni', 'Juli', 'Augusti', 'September', 'Oktober', 'November', 'December'],
- monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
- today: 'I dag',
- clear: 'Nollställ',
- dateFormat: 'yyyy-MM-dd',
- timeFormat: 'HH:mm',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/th.d.ts b/src/ui/static/js/air-datepicker/locale/th.d.ts
deleted file mode 100644
index 79f604363..000000000
--- a/src/ui/static/js/air-datepicker/locale/th.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/th' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const th: AirDatepickerLocale;
-
- export default th;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/th.js b/src/ui/static/js/air-datepicker/locale/th.js
deleted file mode 100644
index bf46ca49c..000000000
--- a/src/ui/static/js/air-datepicker/locale/th.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['วันอาทิตย์', 'วันจันทร์', 'วันอังคาร', 'วันพุธ', 'วันพฤหัสบดี', 'วันศุกร์', 'วันเสาร์'],
- daysShort: ['อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.'],
- daysMin: ['อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.'],
- months: ['มกราคม', 'กุมภาพันธ์', 'มีนาคม', 'เมษายน', 'พฤษภาคม', 'มิถุนายน', 'กรกฎาคม', 'สิงหาคม', 'กันยายน', 'ตุลาคม', 'พฤศจิกายน', 'ธันวาคม'],
- monthsShort: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.', 'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.', 'พ.ย.', 'ธ.ค.'],
- today: 'วันนี้',
- clear: 'ล้าง',
- dateFormat: 'dd/MM/yyyy',
- timeFormat: 'HH:mm',
- firstDay: 0
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/tr.d.ts b/src/ui/static/js/air-datepicker/locale/tr.d.ts
deleted file mode 100644
index d5b63430f..000000000
--- a/src/ui/static/js/air-datepicker/locale/tr.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/tr' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const tr: AirDatepickerLocale;
-
- export default tr;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/tr.js b/src/ui/static/js/air-datepicker/locale/tr.js
deleted file mode 100644
index 94939b0ab..000000000
--- a/src/ui/static/js/air-datepicker/locale/tr.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Pazar', 'Pazartesi', 'Salı', 'Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi'],
- daysShort: ['Pzr', 'Pts', 'Sl', 'Çar', 'Per', 'Cum', 'Cts'],
- daysMin: ['Pa', 'Pt', 'Sl', 'Ça', 'Pe', 'Cu', 'Ct'],
- months: ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'],
- monthsShort: ['Oca', 'Şbt', 'Mrt', 'Nsn', 'Mys', 'Hzr', 'Tmz', 'Ağt', 'Eyl', 'Ekm', 'Ksm', 'Arl'],
- today: 'Bugün',
- clear: 'Temizle',
- dateFormat: 'dd.MM.yyyy',
- timeFormat: 'hh:mm aa',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/uk.d.ts b/src/ui/static/js/air-datepicker/locale/uk.d.ts
deleted file mode 100644
index e09b47450..000000000
--- a/src/ui/static/js/air-datepicker/locale/uk.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/uk' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const uk: AirDatepickerLocale;
-
- export default uk;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/uk.js b/src/ui/static/js/air-datepicker/locale/uk.js
deleted file mode 100644
index f71db2b99..000000000
--- a/src/ui/static/js/air-datepicker/locale/uk.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['Неділя', 'Понеділок', 'Вівторок', 'Середа', 'Четвер', 'П’ятниця', 'Субота'],
- daysShort: ['Нед', 'Пнд', 'Вів', 'Срд', 'Чтв', 'Птн', 'Сбт'],
- daysMin: ['Нд', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
- months: ['Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', 'Серпень', 'Вересень', 'Жовтень', 'Листопад', 'Грудень'],
- monthsShort: ['Січ', 'Лют', 'Бер', 'Кві', 'Тра', 'Чер', 'Лип', 'Сер', 'Вер', 'Жов', 'Лис', 'Гру'],
- today: 'Сьогодні',
- clear: 'Очистити',
- dateFormat: 'dd.MM.yyyy',
- timeFormat: 'HH:mm',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/air-datepicker/locale/zh.d.ts b/src/ui/static/js/air-datepicker/locale/zh.d.ts
deleted file mode 100644
index 5e6be4f98..000000000
--- a/src/ui/static/js/air-datepicker/locale/zh.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module 'air-datepicker/locale/zh' {
- import {AirDatepickerLocale} from 'air-datepicker';
- const zh: AirDatepickerLocale;
-
- export default zh;
-}
diff --git a/src/ui/static/js/air-datepicker/locale/zh.js b/src/ui/static/js/air-datepicker/locale/zh.js
deleted file mode 100644
index 4a2b48ba4..000000000
--- a/src/ui/static/js/air-datepicker/locale/zh.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-var _default = {
- days: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
- daysShort: ['日', '一', '二', '三', '四', '五', '六'],
- daysMin: ['日', '一', '二', '三', '四', '五', '六'],
- months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
- monthsShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
- today: '今天',
- clear: '清除',
- dateFormat: 'yyyy-MM-dd',
- timeFormat: 'HH:mm',
- firstDay: 1
-};
-exports.default = _default;
\ No newline at end of file
diff --git a/src/ui/static/js/dashboard.js b/src/ui/static/js/dashboard.js
index fbb264ca5..a9b6e35a5 100644
--- a/src/ui/static/js/dashboard.js
+++ b/src/ui/static/js/dashboard.js
@@ -44,6 +44,7 @@ class darkMode {
this.htmlEl = document.querySelector("html");
this.darkToggleEl = document.querySelector("[dark-toggle]");
this.darkToggleLabel = document.querySelector("[dark-toggle-label]");
+ this.csrf = document.querySelector("input#csrf_token");
this.init();
}
@@ -63,14 +64,13 @@ class darkMode {
async saveMode() {
const isDark = this.darkToggleEl.checked ? "true" : "false";
- console.log(isDark);
const data = {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
- body: JSON.stringify({ darkmode: isDark }),
+ body: JSON.stringify({ darkmode: isDark, csrf_token: this.csrf.value }),
};
const send = await fetch(`${location.href}/darkmode}`, data);
}
diff --git a/src/ui/static/js/datepicker/DateRangePicker.js b/src/ui/static/js/datepicker/DateRangePicker.js
new file mode 100644
index 000000000..0f5fa702e
--- /dev/null
+++ b/src/ui/static/js/datepicker/DateRangePicker.js
@@ -0,0 +1,210 @@
+import {registerListeners, unregisterListeners} from './lib/event.js';
+import {formatDate} from './lib/date-format.js';
+import Datepicker from './Datepicker.js';
+
+// filter out the config options inapproprite to pass to Datepicker
+function filterOptions(options) {
+ const newOpts = Object.assign({}, options);
+
+ delete newOpts.inputs;
+ delete newOpts.allowOneSidedRange;
+ delete newOpts.maxNumberOfDates; // to ensure each datepicker handles a single date
+
+ return newOpts;
+}
+
+function setupDatepicker(rangepicker, changeDateListener, el, options) {
+ registerListeners(rangepicker, [
+ [el, 'changeDate', changeDateListener],
+ ]);
+ new Datepicker(el, options, rangepicker);
+}
+
+function onChangeDate(rangepicker, ev) {
+ // to prevent both datepickers trigger the other side's update each other
+ if (rangepicker._updating) {
+ return;
+ }
+ rangepicker._updating = true;
+
+ const target = ev.target;
+ if (target.datepicker === undefined) {
+ return;
+ }
+
+ const datepickers = rangepicker.datepickers;
+ const setDateOptions = {render: false};
+ const changedSide = rangepicker.inputs.indexOf(target);
+ const otherSide = changedSide === 0 ? 1 : 0;
+ const changedDate = datepickers[changedSide].dates[0];
+ const otherDate = datepickers[otherSide].dates[0];
+
+ if (changedDate !== undefined && otherDate !== undefined) {
+ // if the start of the range > the end, swap them
+ if (changedSide === 0 && changedDate > otherDate) {
+ datepickers[0].setDate(otherDate, setDateOptions);
+ datepickers[1].setDate(changedDate, setDateOptions);
+ } else if (changedSide === 1 && changedDate < otherDate) {
+ datepickers[0].setDate(changedDate, setDateOptions);
+ datepickers[1].setDate(otherDate, setDateOptions);
+ }
+ } else if (!rangepicker.allowOneSidedRange) {
+ // to prevent the range from becoming one-sided, copy changed side's
+ // selection (no matter if it's empty) to the other side
+ if (changedDate !== undefined || otherDate !== undefined) {
+ setDateOptions.clear = true;
+ datepickers[otherSide].setDate(datepickers[changedSide].dates, setDateOptions);
+ }
+ }
+ datepickers[0].picker.update().render();
+ datepickers[1].picker.update().render();
+ delete rangepicker._updating;
+}
+
+/**
+ * Class representing a date range picker
+ */
+export default class DateRangePicker {
+ /**
+ * Create a date range picker
+ * @param {Element} element - element to bind a date range picker
+ * @param {Object} [options] - config options
+ */
+ constructor(element, options = {}) {
+ const inputs = Array.isArray(options.inputs)
+ ? options.inputs
+ : Array.from(element.querySelectorAll('input'));
+ if (inputs.length < 2) {
+ return;
+ }
+
+ element.rangepicker = this;
+ this.element = element;
+ this.inputs = inputs.slice(0, 2);
+ this.allowOneSidedRange = !!options.allowOneSidedRange;
+
+ const changeDateListener = onChangeDate.bind(null, this);
+ const cleanOptions = filterOptions(options);
+ // in order for initial date setup to work right when pcicLvel > 0,
+ // let Datepicker constructor add the instance to the rangepicker
+ const datepickers = [];
+ Object.defineProperty(this, 'datepickers', {
+ get() {
+ return datepickers;
+ },
+ });
+ setupDatepicker(this, changeDateListener, this.inputs[0], cleanOptions);
+ setupDatepicker(this, changeDateListener, this.inputs[1], cleanOptions);
+ Object.freeze(datepickers);
+ // normalize the range if inital dates are given
+ if (datepickers[0].dates.length > 0) {
+ onChangeDate(this, {target: this.inputs[0]});
+ } else if (datepickers[1].dates.length > 0) {
+ onChangeDate(this, {target: this.inputs[1]});
+ }
+ }
+
+ /**
+ * @type {Array} - selected date of the linked date pickers
+ */
+ get dates() {
+ return this.datepickers.length === 2
+ ? [
+ this.datepickers[0].dates[0],
+ this.datepickers[1].dates[0],
+ ]
+ : undefined;
+ }
+
+ /**
+ * Set new values to the config options
+ * @param {Object} options - config options to update
+ */
+ setOptions(options) {
+ this.allowOneSidedRange = !!options.allowOneSidedRange;
+
+ const cleanOptions = filterOptions(options);
+ this.datepickers[0].setOptions(cleanOptions);
+ this.datepickers[1].setOptions(cleanOptions);
+ }
+
+ /**
+ * Destroy the DateRangePicker instance
+ * @return {DateRangePicker} - the instance destroyed
+ */
+ destroy() {
+ this.datepickers[0].destroy();
+ this.datepickers[1].destroy();
+ unregisterListeners(this);
+ delete this.element.rangepicker;
+ }
+
+ /**
+ * Get the start and end dates of the date range
+ *
+ * The method returns Date objects by default. If format string is passed,
+ * it returns date strings formatted in given format.
+ * The result array always contains 2 items (start date/end date) and
+ * undefined is used for unselected side. (e.g. If none is selected,
+ * the result will be [undefined, undefined]. If only the end date is set
+ * when allowOneSidedRange config option is true, [undefined, endDate] will
+ * be returned.)
+ *
+ * @param {String} [format] - Format string to stringify the dates
+ * @return {Array} - Start and end dates
+ */
+ getDates(format = undefined) {
+ const callback = format
+ ? date => formatDate(date, format, this.datepickers[0].config.locale)
+ : date => new Date(date);
+
+ return this.dates.map(date => date === undefined ? date : callback(date));
+ }
+
+ /**
+ * Set the start and end dates of the date range
+ *
+ * The method calls datepicker.setDate() internally using each of the
+ * arguments in start→end order.
+ *
+ * When a clear: true option object is passed instead of a date, the method
+ * clears the date.
+ *
+ * If an invalid date, the same date as the current one or an option object
+ * without clear: true is passed, the method considers that argument as an
+ * "ineffective" argument because calling datepicker.setDate() with those
+ * values makes no changes to the date selection.
+ *
+ * When the allowOneSidedRange config option is false, passing {clear: true}
+ * to clear the range works only when it is done to the last effective
+ * argument (in other words, passed to rangeEnd or to rangeStart along with
+ * ineffective rangeEnd). This is because when the date range is changed,
+ * it gets normalized based on the last change at the end of the changing
+ * process.
+ *
+ * @param {Date|Number|String|Object} rangeStart - Start date of the range
+ * or {clear: true} to clear the date
+ * @param {Date|Number|String|Object} rangeEnd - End date of the range
+ * or {clear: true} to clear the date
+ */
+ setDates(rangeStart, rangeEnd) {
+ const [datepicker0, datepicker1] = this.datepickers;
+ const origDates = this.dates;
+
+ // If range normalization runs on every change, we can't set a new range
+ // that starts after the end of the current range correctly because the
+ // normalization process swaps start↔︎end right after setting the new start
+ // date. To prevent this, the normalization process needs to run once after
+ // both of the new dates are set.
+ this._updating = true;
+ datepicker0.setDate(rangeStart);
+ datepicker1.setDate(rangeEnd);
+ delete this._updating;
+
+ if (datepicker1.dates[0] !== origDates[1]) {
+ onChangeDate(this, {target: this.inputs[1]});
+ } else if (datepicker0.dates[0] !== origDates[0]) {
+ onChangeDate(this, {target: this.inputs[0]});
+ }
+ }
+}
diff --git a/src/ui/static/js/datepicker/Datepicker.js b/src/ui/static/js/datepicker/Datepicker.js
new file mode 100644
index 000000000..babffc06b
--- /dev/null
+++ b/src/ui/static/js/datepicker/Datepicker.js
@@ -0,0 +1,502 @@
+import {lastItemOf, stringToArray, isInRange} from './lib/utils.js';
+import {today, regularizeDate} from './lib/date.js';
+import {parseDate, formatDate} from './lib/date-format.js';
+import {isActiveElement} from './lib/dom.js';
+import {registerListeners, unregisterListeners} from './lib/event.js';
+import {locales} from './i18n/base-locales.js';
+import defaultOptions from './options/defaultOptions.js';
+import processOptions from './options/processOptions.js';
+import Picker from './picker/Picker.js';
+import {triggerDatepickerEvent} from './events/functions.js';
+import {onKeydown, onFocus, onMousedown, onClickInput, onPaste} from './events/inputFieldListeners.js';
+import {onClickOutside} from './events/otherListeners.js';
+
+function stringifyDates(dates, config) {
+ return dates
+ .map(dt => formatDate(dt, config.format, config.locale))
+ .join(config.dateDelimiter);
+}
+
+// parse input dates and create an array of time values for selection
+// returns undefined if there are no valid dates in inputDates
+// when origDates (current selection) is passed, the function works to mix
+// the input dates into the current selection
+function processInputDates(datepicker, inputDates, clear = false) {
+ // const {config, dates: origDates, rangepicker} = datepicker;
+ const {config, dates: origDates, rangeSideIndex} = datepicker;
+ if (inputDates.length === 0) {
+ // empty input is considered valid unless origiDates is passed
+ return clear ? [] : undefined;
+ }
+
+ // const rangeEnd = rangepicker && datepicker === rangepicker.datepickers[1];
+ let newDates = inputDates.reduce((dates, dt) => {
+ let date = parseDate(dt, config.format, config.locale);
+ if (date === undefined) {
+ return dates;
+ }
+ // adjust to 1st of the month/Jan 1st of the year
+ // or to the last day of the monh/Dec 31st of the year if the datepicker
+ // is the range-end picker of a rangepicker
+ date = regularizeDate(date, config.pickLevel, rangeSideIndex);
+ if (
+ isInRange(date, config.minDate, config.maxDate)
+ && !dates.includes(date)
+ && !config.datesDisabled.includes(date)
+ && (config.pickLevel > 0 || !config.daysOfWeekDisabled.includes(new Date(date).getDay()))
+ ) {
+ dates.push(date);
+ }
+ return dates;
+ }, []);
+ if (newDates.length === 0) {
+ return;
+ }
+ if (config.multidate && !clear) {
+ // get the synmetric difference between origDates and newDates
+ newDates = newDates.reduce((dates, date) => {
+ if (!origDates.includes(date)) {
+ dates.push(date);
+ }
+ return dates;
+ }, origDates.filter(date => !newDates.includes(date)));
+ }
+ // do length check always because user can input multiple dates regardless of the mode
+ return config.maxNumberOfDates && newDates.length > config.maxNumberOfDates
+ ? newDates.slice(config.maxNumberOfDates * -1)
+ : newDates;
+}
+
+// refresh the UI elements
+// modes: 1: input only, 2, picker only, 3 both
+function refreshUI(datepicker, mode = 3, quickRender = true) {
+ const {config, picker, inputField} = datepicker;
+ if (mode & 2) {
+ const newView = picker.active ? config.pickLevel : config.startView;
+ picker.update().changeView(newView).render(quickRender);
+ }
+ if (mode & 1 && inputField) {
+ inputField.value = stringifyDates(datepicker.dates, config);
+ }
+}
+
+function setDate(datepicker, inputDates, options) {
+ let {clear, render, autohide, revert} = options;
+ if (render === undefined) {
+ render = true;
+ }
+ if (!render) {
+ autohide = false;
+ } else if (autohide === undefined) {
+ autohide = datepicker.config.autohide;
+ }
+
+ const newDates = processInputDates(datepicker, inputDates, clear);
+ if (!newDates && !revert) {
+ return;
+ }
+ if (newDates && newDates.toString() !== datepicker.dates.toString()) {
+ datepicker.dates = newDates;
+ refreshUI(datepicker, render ? 3 : 1);
+ triggerDatepickerEvent(datepicker, 'changeDate');
+ } else {
+ refreshUI(datepicker, 1);
+ }
+
+ if (autohide) {
+ datepicker.hide();
+ }
+}
+
+/**
+ * Class representing a date picker
+ */
+export default class Datepicker {
+ /**
+ * Create a date picker
+ * @param {Element} element - element to bind a date picker
+ * @param {Object} [options] - config options
+ * @param {DateRangePicker} [rangepicker] - DateRangePicker instance the
+ * date picker belongs to. Use this only when creating date picker as a part
+ * of date range picker
+ */
+ constructor(element, options = {}, rangepicker = undefined) {
+ element.datepicker = this;
+ this.element = element;
+
+ const config = this.config = Object.assign({
+ buttonClass: (options.buttonClass && String(options.buttonClass)) || 'button',
+ container: null,
+ defaultViewDate: today(),
+ maxDate: undefined,
+ minDate: undefined,
+ }, processOptions(defaultOptions, this));
+ // configure by type
+ const inline = this.inline = element.tagName !== 'INPUT';
+ let inputField;
+ if (inline) {
+ config.container = element;
+ } else {
+ if (options.container) {
+ // omit string type check because it doesn't guarantee to avoid errors
+ // (invalid selector string causes abend with sytax error)
+ config.container = options.container instanceof HTMLElement
+ ? options.container
+ : document.querySelector(options.container);
+ }
+ inputField = this.inputField = element;
+ inputField.classList.add('datepicker-input');
+ }
+ if (rangepicker) {
+ // check validiry
+ const index = rangepicker.inputs.indexOf(inputField);
+ const datepickers = rangepicker.datepickers;
+ if (index < 0 || index > 1 || !Array.isArray(datepickers)) {
+ throw Error('Invalid rangepicker object.');
+ }
+ // attach itaelf to the rangepicker here so that processInputDates() can
+ // determine if this is the range-end picker of the rangepicker while
+ // setting inital values when pickLevel > 0
+ datepickers[index] = this;
+ // add getter for rangepicker
+ Object.defineProperty(this, 'rangepicker', {
+ get() {
+ return rangepicker;
+ },
+ });
+ Object.defineProperty(this, 'rangeSideIndex', {
+ get() {
+ return index;
+ },
+ });
+ }
+
+ // set up config
+ this._options = options;
+ Object.assign(config, processOptions(options, this));
+
+ // set initial dates
+ let initialDates;
+ if (inline) {
+ initialDates = stringToArray(element.dataset.date, config.dateDelimiter);
+ delete element.dataset.date;
+ } else {
+ initialDates = stringToArray(inputField.value, config.dateDelimiter);
+ }
+ this.dates = [];
+ // process initial value
+ const inputDateValues = processInputDates(this, initialDates);
+ if (inputDateValues && inputDateValues.length > 0) {
+ this.dates = inputDateValues;
+ }
+ if (inputField) {
+ inputField.value = stringifyDates(this.dates, config);
+ }
+
+ const picker = this.picker = new Picker(this);
+
+ if (inline) {
+ this.show();
+ } else {
+ // set up event listeners in other modes
+ const onMousedownDocument = onClickOutside.bind(null, this);
+ const listeners = [
+ [inputField, 'keydown', onKeydown.bind(null, this)],
+ [inputField, 'focus', onFocus.bind(null, this)],
+ [inputField, 'mousedown', onMousedown.bind(null, this)],
+ [inputField, 'click', onClickInput.bind(null, this)],
+ [inputField, 'paste', onPaste.bind(null, this)],
+ [document, 'mousedown', onMousedownDocument],
+ [document, 'touchstart', onMousedownDocument],
+ [window, 'resize', picker.place.bind(picker)]
+ ];
+ registerListeners(this, listeners);
+ }
+ }
+
+ /**
+ * Format Date object or time value in given format and language
+ * @param {Date|Number} date - date or time value to format
+ * @param {String|Object} format - format string or object that contains
+ * toDisplay() custom formatter, whose signature is
+ * - args:
+ * - date: {Date} - Date instance of the date passed to the method
+ * - format: {Object} - the format object passed to the method
+ * - locale: {Object} - locale for the language specified by `lang`
+ * - return:
+ * {String} formatted date
+ * @param {String} [lang=en] - language code for the locale to use
+ * @return {String} formatted date
+ */
+ static formatDate(date, format, lang) {
+ return formatDate(date, format, lang && locales[lang] || locales.en);
+ }
+
+ /**
+ * Parse date string
+ * @param {String|Date|Number} dateStr - date string, Date object or time
+ * value to parse
+ * @param {String|Object} format - format string or object that contains
+ * toValue() custom parser, whose signature is
+ * - args:
+ * - dateStr: {String|Date|Number} - the dateStr passed to the method
+ * - format: {Object} - the format object passed to the method
+ * - locale: {Object} - locale for the language specified by `lang`
+ * - return:
+ * {Date|Number} parsed date or its time value
+ * @param {String} [lang=en] - language code for the locale to use
+ * @return {Number} time value of parsed date
+ */
+ static parseDate(dateStr, format, lang) {
+ return parseDate(dateStr, format, lang && locales[lang] || locales.en);
+ }
+
+ /**
+ * @type {Object} - Installed locales in `[languageCode]: localeObject` format
+ * en`:_English (US)_ is pre-installed.
+ */
+ static get locales() {
+ return locales;
+ }
+
+ /**
+ * @type {Boolean} - Whether the picker element is shown. `true` whne shown
+ */
+ get active() {
+ return !!(this.picker && this.picker.active);
+ }
+
+ /**
+ * @type {HTMLDivElement} - DOM object of picker element
+ */
+ get pickerElement() {
+ return this.picker ? this.picker.element : undefined;
+ }
+
+ /**
+ * Set new values to the config options
+ * @param {Object} options - config options to update
+ */
+ setOptions(options) {
+ const picker = this.picker;
+ const newOptions = processOptions(options, this);
+ Object.assign(this._options, options);
+ Object.assign(this.config, newOptions);
+ picker.setOptions(newOptions);
+
+ refreshUI(this, 3);
+ }
+
+ /**
+ * Show the picker element
+ */
+ show() {
+ if (this.inputField) {
+ if (this.inputField.disabled) {
+ return;
+ }
+ if (!isActiveElement(this.inputField) && !this.config.disableTouchKeyboard) {
+ this._showing = true;
+ this.inputField.focus();
+ delete this._showing;
+ }
+ }
+ this.picker.show();
+ }
+
+ /**
+ * Hide the picker element
+ * Not available on inline picker
+ */
+ hide() {
+ if (this.inline) {
+ return;
+ }
+ this.picker.hide();
+ this.picker.update().changeView(this.config.startView).render();
+ }
+
+ /**
+ * Destroy the Datepicker instance
+ * @return {Detepicker} - the instance destroyed
+ */
+ destroy() {
+ this.hide();
+ unregisterListeners(this);
+ this.picker.detach();
+ if (!this.inline) {
+ this.inputField.classList.remove('datepicker-input');
+ }
+ delete this.element.datepicker;
+ return this;
+ }
+
+ /**
+ * Get the selected date(s)
+ *
+ * The method returns a Date object of selected date by default, and returns
+ * an array of selected dates in multidate mode. If format string is passed,
+ * it returns date string(s) formatted in given format.
+ *
+ * @param {String} [format] - Format string to stringify the date(s)
+ * @return {Date|String|Date[]|String[]} - selected date(s), or if none is
+ * selected, empty array in multidate mode and untitled in sigledate mode
+ */
+ getDate(format = undefined) {
+ const callback = format
+ ? date => formatDate(date, format, this.config.locale)
+ : date => new Date(date);
+
+ if (this.config.multidate) {
+ return this.dates.map(callback);
+ }
+ if (this.dates.length > 0) {
+ return callback(this.dates[0]);
+ }
+ }
+
+ /**
+ * Set selected date(s)
+ *
+ * In multidate mode, you can pass multiple dates as a series of arguments
+ * or an array. (Since each date is parsed individually, the type of the
+ * dates doesn't have to be the same.)
+ * The given dates are used to toggle the select status of each date. The
+ * number of selected dates is kept from exceeding the length set to
+ * maxNumberOfDates.
+ *
+ * With clear: true option, the method can be used to clear the selection
+ * and to replace the selection instead of toggling in multidate mode.
+ * If the option is passed with no date arguments or an empty dates array,
+ * it works as "clear" (clear the selection then set nothing), and if the
+ * option is passed with new dates to select, it works as "replace" (clear
+ * the selection then set the given dates)
+ *
+ * When render: false option is used, the method omits re-rendering the
+ * picker element. In this case, you need to call refresh() method later in
+ * order for the picker element to reflect the changes. The input field is
+ * refreshed always regardless of this option.
+ *
+ * When invalid (unparsable, repeated, disabled or out-of-range) dates are
+ * passed, the method ignores them and applies only valid ones. In the case
+ * that all the given dates are invalid, which is distinguished from passing
+ * no dates, the method considers it as an error and leaves the selection
+ * untouched. (The input field also remains untouched unless revert: true
+ * option is used.)
+ *
+ * @param {...(Date|Number|String)|Array} [dates] - Date strings, Date
+ * objects, time values or mix of those for new selection
+ * @param {Object} [options] - function options
+ * - clear: {boolean} - Whether to clear the existing selection
+ * defualt: false
+ * - render: {boolean} - Whether to re-render the picker element
+ * default: true
+ * - autohide: {boolean} - Whether to hide the picker element after re-render
+ * Ignored when used with render: false
+ * default: config.autohide
+ * - revert: {boolean} - Whether to refresh the input field when all the
+ * passed dates are invalid
+ * default: false
+ */
+ setDate(...args) {
+ const dates = [...args];
+ const opts = {};
+ const lastArg = lastItemOf(args);
+ if (
+ typeof lastArg === 'object'
+ && !Array.isArray(lastArg)
+ && !(lastArg instanceof Date)
+ && lastArg
+ ) {
+ Object.assign(opts, dates.pop());
+ }
+
+ const inputDates = Array.isArray(dates[0]) ? dates[0] : dates;
+ setDate(this, inputDates, opts);
+ }
+
+ /**
+ * Update the selected date(s) with input field's value
+ * Not available on inline picker
+ *
+ * The input field will be refreshed with properly formatted date string.
+ *
+ * In the case that all the entered dates are invalid (unparsable, repeated,
+ * disabled or out-of-range), whixh is distinguished from empty input field,
+ * the method leaves the input field untouched as well as the selection by
+ * default. If revert: true option is used in this case, the input field is
+ * refreshed with the existing selection.
+ *
+ * @param {Object} [options] - function options
+ * - autohide: {boolean} - whether to hide the picker element after refresh
+ * default: false
+ * - revert: {boolean} - Whether to refresh the input field when all the
+ * passed dates are invalid
+ * default: false
+ */
+ update(options = undefined) {
+ if (this.inline) {
+ return;
+ }
+
+ const opts = Object.assign(options || {}, {clear: true, render: true});
+ const inputDates = stringToArray(this.inputField.value, this.config.dateDelimiter);
+ setDate(this, inputDates, opts);
+ }
+
+ /**
+ * Refresh the picker element and the associated input field
+ * @param {String} [target] - target item when refreshing one item only
+ * 'picker' or 'input'
+ * @param {Boolean} [forceRender] - whether to re-render the picker element
+ * regardless of its state instead of optimized refresh
+ */
+ refresh(target = undefined, forceRender = false) {
+ if (target && typeof target !== 'string') {
+ forceRender = target;
+ target = undefined;
+ }
+
+ let mode;
+ if (target === 'picker') {
+ mode = 2;
+ } else if (target === 'input') {
+ mode = 1;
+ } else {
+ mode = 3;
+ }
+ refreshUI(this, mode, !forceRender);
+ }
+
+ /**
+ * Enter edit mode
+ * Not available on inline picker or when the picker element is hidden
+ */
+ enterEditMode() {
+ if (this.inline || !this.picker.active || this.editMode) {
+ return;
+ }
+ this.editMode = true;
+ this.inputField.classList.add('in-edit');
+ }
+
+ /**
+ * Exit from edit mode
+ * Not available on inline picker
+ * @param {Object} [options] - function options
+ * - update: {boolean} - whether to call update() after exiting
+ * If false, input field is revert to the existing selection
+ * default: false
+ */
+ exitEditMode(options = undefined) {
+ if (this.inline || !this.editMode) {
+ return;
+ }
+ const opts = Object.assign({update: false}, options);
+ delete this.editMode;
+ this.inputField.classList.remove('in-edit');
+ if (opts.update) {
+ this.update(opts);
+ }
+ }
+}
diff --git a/src/ui/static/js/datepicker/datepicker-full.js b/src/ui/static/js/datepicker/datepicker-full.js
new file mode 100644
index 000000000..b054896ac
--- /dev/null
+++ b/src/ui/static/js/datepicker/datepicker-full.js
@@ -0,0 +1,5 @@
+import Datepicker from './Datepicker.js';
+import DateRangePicker from './DateRangePicker.js';
+
+window.Datepicker = Datepicker;
+window.DateRangePicker = DateRangePicker;
diff --git a/src/ui/static/js/datepicker/datepicker-full.min.js b/src/ui/static/js/datepicker/datepicker-full.min.js
new file mode 100644
index 000000000..dab79456b
--- /dev/null
+++ b/src/ui/static/js/datepicker/datepicker-full.min.js
@@ -0,0 +1 @@
+!function(){"use strict";function e(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function t(e){return e[e.length-1]}function i(e,...t){return t.forEach((t=>{e.includes(t)||e.push(t)})),e}function s(e,t){return e?e.split(t):[]}function n(e,t,i){return(void 0===t||e>=t)&&(void 0===i||e<=i)}function a(e,t,i){return ei?i:e}function r(e,t,i={},s=0,n=""){n+=`<${Object.keys(i).reduce(((e,t)=>{let n=i[t];return"function"==typeof n&&(n=n(s)),`${e} ${t}="${n}"`}),e)}>${e}>`;const a=s+1;return a\s+/g,">").replace(/\s+,"<")}function o(e){return new Date(e).setHours(0,0,0,0)}function c(){return(new Date).setHours(0,0,0,0)}function l(...e){switch(e.length){case 0:return c();case 1:return o(e[0])}const t=new Date(0);return t.setFullYear(...e),t.setHours(0,0,0,0)}function h(e,t){const i=new Date(e);return i.setDate(i.getDate()+t)}function u(e,t){const i=new Date(e),s=i.getMonth()+t;let n=s%12;n<0&&(n+=12);const a=i.setMonth(s);return i.getMonth()!==n?i.setDate(0):a}function f(e,t){const i=new Date(e),s=i.getMonth(),n=i.setFullYear(i.getFullYear()+t);return 1===s&&2===i.getMonth()?i.setDate(0):n}function p(e,t){return(e-t+7)%7}function g(e,t,i=0){const s=new Date(e).getDay();return h(e,p(t,i)-p(s,i))}function m(e,t){const i=new Date(e).getFullYear();return Math.floor(i/t)*t}function w(e,t,i){if(1!==t&&2!==t)return e;const s=new Date(e);return 1===t?i?s.setMonth(s.getMonth()+1,0):s.setDate(1):i?s.setFullYear(s.getFullYear()+1,0,0):s.setMonth(0,1),s.setHours(0,0,0,0)}const D=/dd?|DD?|mm?|MM?|yy?(?:yy)?/,y=/[\s!-/:-@[-`{-~年月日]+/;let v={};const b={y:(e,t)=>new Date(e).setFullYear(parseInt(t,10)),m(e,t,i){const s=new Date(e);let n=parseInt(t,10)-1;if(isNaN(n)){if(!t)return NaN;const e=t.toLowerCase(),s=t=>t.toLowerCase().startsWith(e);if(n=i.monthsShort.findIndex(s),n<0&&(n=i.months.findIndex(s)),n<0)return NaN}return s.setMonth(n),s.getMonth()!==x(n)?s.setDate(0):s.getTime()},d:(e,t)=>new Date(e).setDate(parseInt(t,10))},k={d:e=>e.getDate(),dd:e=>M(e.getDate(),2),D:(e,t)=>t.daysShort[e.getDay()],DD:(e,t)=>t.days[e.getDay()],m:e=>e.getMonth()+1,mm:e=>M(e.getMonth()+1,2),M:(e,t)=>t.monthsShort[e.getMonth()],MM:(e,t)=>t.months[e.getMonth()],y:e=>e.getFullYear(),yy:e=>M(e.getFullYear(),2).slice(-2),yyyy:e=>M(e.getFullYear(),4)};function x(e){return e>-1?e%12:x(e+12)}function M(e,t){return e.toString().padStart(t,"0")}function S(e){if("string"!=typeof e)throw new Error("Invalid date format.");if(e in v)return v[e];const i=e.split(D),s=e.match(new RegExp(D,"g"));if(0===i.length||!s)throw new Error("Invalid date format.");const n=s.map((e=>k[e])),a=Object.keys(b).reduce(((e,t)=>(s.find((e=>"D"!==e[0]&&e[0].toLowerCase()===t))&&e.push(t),e)),[]);return v[e]={parser(e,t){const i=e.split(y).reduce(((e,t,i)=>{if(t.length>0&&s[i]){const n=s[i][0];"M"===n?e.m=t:"D"!==n&&(e[n]=t)}return e}),{});return a.reduce(((e,s)=>{const n=b[s](e,i[s],t);return isNaN(n)?e:n}),c())},formatter:(e,s)=>n.reduce(((t,n,a)=>t+`${i[a]}${n(e,s)}`),"")+t(i)}}function O(e,t,i){if(e instanceof Date||"number"==typeof e){const t=o(e);return isNaN(t)?void 0:t}if(e){if("today"===e)return c();if(t&&t.toValue){const s=t.toValue(e,t,i);return isNaN(s)?void 0:o(s)}return S(t).parser(e,i)}}function C(e,t,i){if(isNaN(e)||!e&&0!==e)return"";const s="number"==typeof e?new Date(e):e;return t.toDisplay?t.toDisplay(s,t,i):S(t).formatter(s,i)}const E=document.createRange();function F(e){return E.createContextualFragment(e)}function V(e){return e.parentElement||(e.parentNode instanceof ShadowRoot?e.parentNode.host:void 0)}function N(e){return e.getRootNode().activeElement===e}function L(e){"none"!==e.style.display&&(e.style.display&&(e.dataset.styleDisplay=e.style.display),e.style.display="none")}function B(e){"none"===e.style.display&&(e.dataset.styleDisplay?(e.style.display=e.dataset.styleDisplay,delete e.dataset.styleDisplay):e.style.display="")}function A(e){e.firstChild&&(e.removeChild(e.firstChild),A(e))}const Y=new WeakMap,{addEventListener:W,removeEventListener:H}=EventTarget.prototype;function j(e,t){let i=Y.get(e);i||(i=[],Y.set(e,i)),t.forEach((e=>{W.call(...e),i.push(e)}))}function T(e){let t=Y.get(e);t&&(t.forEach((e=>{H.call(...e)})),Y.delete(e))}if(!Event.prototype.composedPath){const e=(t,i=[])=>{let s;return i.push(t),t.parentNode?s=t.parentNode:t.host?s=t.host:t.defaultView&&(s=t.defaultView),s?e(s,i):i};Event.prototype.composedPath=function(){return e(this.target)}}function _(e,t,i){const[s,...n]=e;return t(s)?s:s!==i&&"HTML"!==s.tagName&&0!==n.length?_(n,t,i):void 0}function K(e,t){const i="function"==typeof t?t:e=>e instanceof Element&&e.matches(t);return _(e.composedPath(),i,e.currentTarget)}const $={en:{days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",clear:"Clear",titleFormat:"MM y"}},R={autohide:!1,beforeShowDay:null,beforeShowDecade:null,beforeShowMonth:null,beforeShowYear:null,calendarWeeks:!1,clearBtn:!1,dateDelimiter:",",datesDisabled:[],daysOfWeekDisabled:[],daysOfWeekHighlighted:[],defaultViewDate:void 0,disableTouchKeyboard:!1,format:"mm/dd/yyyy",language:"en",maxDate:null,maxNumberOfDates:1,maxView:3,minDate:null,nextArrow:"»",orientation:"auto",pickLevel:0,prevArrow:"«",showDaysOfWeek:!0,showOnClick:!0,showOnFocus:!0,startView:0,title:"",todayBtn:!1,todayBtnMode:0,todayHighlight:!1,updateOnBlur:!0,weekStart:0},{language:I,format:P,weekStart:q}=R;function J(e,t){return e.length<6&&t>=0&&t<7?i(e,t):e}function U(e){return(e+6)%7}function z(e,t,i,s){const n=O(e,t,i);return void 0!==n?n:s}function X(e,t,i=3){const s=parseInt(e,10);return s>=0&&s<=i?s:t}function G(t,s){const n=Object.assign({},t),a={},r=s.constructor.locales,d=s.rangeSideIndex;let{format:o,language:c,locale:h,maxDate:u,maxView:f,minDate:p,pickLevel:g,startView:m,weekStart:y}=s.config||{};if(n.language){let e;if(n.language!==c&&(r[n.language]?e=n.language:(e=n.language.split("-")[0],void 0===r[e]&&(e=!1))),delete n.language,e){c=a.language=e;const t=h||r[I];h=Object.assign({format:P,weekStart:q},r[I]),c!==I&&Object.assign(h,r[c]),a.locale=h,o===t.format&&(o=a.format=h.format),y===t.weekStart&&(y=a.weekStart=h.weekStart,a.weekEnd=U(h.weekStart))}}if(n.format){const e="function"==typeof n.format.toDisplay,t="function"==typeof n.format.toValue,i=D.test(n.format);(e&&t||i)&&(o=a.format=n.format),delete n.format}let v=g;void 0!==n.pickLevel&&(v=X(n.pickLevel,2),delete n.pickLevel),v!==g&&(v>g&&(void 0===n.minDate&&(n.minDate=p),void 0===n.maxDate&&(n.maxDate=u)),n.datesDisabled||(n.datesDisabled=[]),g=a.pickLevel=v);let b=p,k=u;if(void 0!==n.minDate){const e=l(0,0,1);b=null===n.minDate?e:z(n.minDate,o,h,b),b!==e&&(b=w(b,g,!1)),delete n.minDate}if(void 0!==n.maxDate&&(k=null===n.maxDate?void 0:z(n.maxDate,o,h,k),void 0!==k&&(k=w(k,g,!0)),delete n.maxDate),k{const s=O(t,o,h);return void 0!==s?i(e,w(s,g,d)):e}),[]),delete n.datesDisabled),void 0!==n.defaultViewDate){const e=O(n.defaultViewDate,o,h);void 0!==e&&(a.defaultViewDate=e),delete n.defaultViewDate}if(void 0!==n.weekStart){const e=Number(n.weekStart)%7;isNaN(e)||(y=a.weekStart=e,a.weekEnd=U(e)),delete n.weekStart}if(n.daysOfWeekDisabled&&(a.daysOfWeekDisabled=n.daysOfWeekDisabled.reduce(J,[]),delete n.daysOfWeekDisabled),n.daysOfWeekHighlighted&&(a.daysOfWeekHighlighted=n.daysOfWeekHighlighted.reduce(J,[]),delete n.daysOfWeekHighlighted),void 0!==n.maxNumberOfDates){const e=parseInt(n.maxNumberOfDates,10);e>=0&&(a.maxNumberOfDates=e,a.multidate=1!==e),delete n.maxNumberOfDates}n.dateDelimiter&&(a.dateDelimiter=String(n.dateDelimiter),delete n.dateDelimiter);let x=f;void 0!==n.maxView&&(x=X(n.maxView,f),delete n.maxView),x=g>x?g:x,x!==f&&(f=a.maxView=x);let M=m;if(void 0!==n.startView&&(M=X(n.startView,M),delete n.startView),Mf&&(M=f),M!==m&&(a.startView=M),n.prevArrow){const e=F(n.prevArrow);e.childNodes.length>0&&(a.prevArrow=e.childNodes),delete n.prevArrow}if(n.nextArrow){const e=F(n.nextArrow);e.childNodes.length>0&&(a.nextArrow=e.childNodes),delete n.nextArrow}if(void 0!==n.disableTouchKeyboard&&(a.disableTouchKeyboard="ontouchstart"in document&&!!n.disableTouchKeyboard,delete n.disableTouchKeyboard),n.orientation){const e=n.orientation.toLowerCase().split(/\s+/g);a.orientation={x:e.find((e=>"left"===e||"right"===e))||"auto",y:e.find((e=>"top"===e||"bottom"===e))||"auto"},delete n.orientation}if(void 0!==n.todayBtnMode){switch(n.todayBtnMode){case 0:case 1:a.todayBtnMode=n.todayBtnMode}delete n.todayBtnMode}return Object.keys(n).forEach((t=>{void 0!==n[t]&&e(R,t)&&(a[t]=n[t])})),a}const Q=d(''),Z=d(`\n
${r("span",7,{class:"dow"})}
\n
${r("span",42)}
\n
`),ee=d(`\n
\n
${r("span",6,{class:"week"})}
\n
`);class te{constructor(e,t){Object.assign(this,t,{picker:e,element:F('').firstChild,selected:[]}),this.init(this.picker.datepicker.config)}init(e){void 0!==e.pickLevel&&(this.isMinView=this.id===e.pickLevel),this.setOptions(e),this.updateFocus(),this.updateSelection()}performBeforeHook(e,t,s){let n=this.beforeShow(new Date(s));switch(typeof n){case"boolean":n={enabled:n};break;case"string":n={classes:n}}if(n){if(!1===n.enabled&&(e.classList.add("disabled"),i(this.disabled,t)),n.classes){const s=n.classes.split(/\s+/);e.classList.add(...s),s.includes("disabled")&&i(this.disabled,t)}n.content&&function(e,t){A(e),t instanceof DocumentFragment?e.appendChild(t):"string"==typeof t?e.appendChild(F(t)):"function"==typeof t.forEach&&t.forEach((t=>{e.appendChild(t)}))}(e,n.content)}}}class ie extends te{constructor(e){super(e,{id:0,name:"days",cellClass:"day"})}init(e,t=!0){if(t){const e=F(Z).firstChild;this.dow=e.firstChild,this.grid=e.lastChild,this.element.appendChild(e)}super.init(e)}setOptions(t){let i;if(e(t,"minDate")&&(this.minDate=t.minDate),e(t,"maxDate")&&(this.maxDate=t.maxDate),t.datesDisabled&&(this.datesDisabled=t.datesDisabled),t.daysOfWeekDisabled&&(this.daysOfWeekDisabled=t.daysOfWeekDisabled,i=!0),t.daysOfWeekHighlighted&&(this.daysOfWeekHighlighted=t.daysOfWeekHighlighted),void 0!==t.todayHighlight&&(this.todayHighlight=t.todayHighlight),void 0!==t.weekStart&&(this.weekStart=t.weekStart,this.weekEnd=t.weekEnd,i=!0),t.locale){const e=this.locale=t.locale;this.dayNames=e.daysMin,this.switchLabelFormat=e.titleFormat,i=!0}if(void 0!==t.beforeShowDay&&(this.beforeShow="function"==typeof t.beforeShowDay?t.beforeShowDay:void 0),void 0!==t.calendarWeeks)if(t.calendarWeeks&&!this.calendarWeeks){const e=F(ee).firstChild;this.calendarWeeks={element:e,dow:e.firstChild,weeks:e.lastChild},this.element.insertBefore(e,this.element.firstChild)}else this.calendarWeeks&&!t.calendarWeeks&&(this.element.removeChild(this.calendarWeeks.element),this.calendarWeeks=null);void 0!==t.showDaysOfWeek&&(t.showDaysOfWeek?(B(this.dow),this.calendarWeeks&&B(this.calendarWeeks.dow)):(L(this.dow),this.calendarWeeks&&L(this.calendarWeeks.dow))),i&&Array.from(this.dow.children).forEach(((e,t)=>{const i=(this.weekStart+t)%7;e.textContent=this.dayNames[i],e.className=this.daysOfWeekDisabled.includes(i)?"dow disabled":"dow"}))}updateFocus(){const e=new Date(this.picker.viewDate),t=e.getFullYear(),i=e.getMonth(),s=l(t,i,1),n=g(s,this.weekStart,this.weekStart);this.first=s,this.last=l(t,i+1,0),this.start=n,this.focused=this.picker.viewDate}updateSelection(){const{dates:e,rangepicker:t}=this.picker.datepicker;this.selected=e,t&&(this.range=t.dates)}render(){this.today=this.todayHighlight?c():void 0,this.disabled=[...this.datesDisabled];const e=C(this.focused,this.switchLabelFormat,this.locale);if(this.picker.setViewSwitchLabel(e),this.picker.setPrevBtnDisabled(this.first<=this.minDate),this.picker.setNextBtnDisabled(this.last>=this.maxDate),this.calendarWeeks){const e=g(this.first,1,1);Array.from(this.calendarWeeks.weeks.children).forEach(((t,i)=>{t.textContent=function(e){const t=g(e,4,1),i=g(new Date(t).setMonth(0,4),4,1);return Math.round((t-i)/6048e5)+1}(h(e,7*i))}))}Array.from(this.grid.children).forEach(((e,t)=>{const s=e.classList,n=h(this.start,t),a=new Date(n),r=a.getDay();if(e.className=`datepicker-cell ${this.cellClass}`,e.dataset.date=n,e.textContent=a.getDate(),nthis.last&&s.add("next"),this.today===n&&s.add("today"),(nthis.maxDate||this.disabled.includes(n))&&s.add("disabled"),this.daysOfWeekDisabled.includes(r)&&(s.add("disabled"),i(this.disabled,n)),this.daysOfWeekHighlighted.includes(r)&&s.add("highlighted"),this.range){const[e,t]=this.range;n>e&&n{e.classList.remove("range","range-start","range-end","selected","focused")})),Array.from(this.grid.children).forEach((i=>{const s=Number(i.dataset.date),n=i.classList;s>e&&s{e.classList.remove("focused")})),this.grid.children[e].classList.add("focused")}}function se(e,t){if(!e||!e[0]||!e[1])return;const[[i,s],[n,a]]=e;return i>t||ne})))),super.init(e)}setOptions(t){if(t.locale&&(this.monthNames=t.locale.monthsShort),e(t,"minDate"))if(void 0===t.minDate)this.minYear=this.minMonth=this.minDate=void 0;else{const e=new Date(t.minDate);this.minYear=e.getFullYear(),this.minMonth=e.getMonth(),this.minDate=e.setDate(1)}if(e(t,"maxDate"))if(void 0===t.maxDate)this.maxYear=this.maxMonth=this.maxDate=void 0;else{const e=new Date(t.maxDate);this.maxYear=e.getFullYear(),this.maxMonth=e.getMonth(),this.maxDate=l(this.maxYear,this.maxMonth+1,0)}this.isMinView?t.datesDisabled&&(this.datesDisabled=t.datesDisabled):this.datesDisabled=[],void 0!==t.beforeShowMonth&&(this.beforeShow="function"==typeof t.beforeShowMonth?t.beforeShowMonth:void 0)}updateFocus(){const e=new Date(this.picker.viewDate);this.year=e.getFullYear(),this.focused=e.getMonth()}updateSelection(){const{dates:e,rangepicker:t}=this.picker.datepicker;this.selected=e.reduce(((e,t)=>{const s=new Date(t),n=s.getFullYear(),a=s.getMonth();return void 0===e[n]?e[n]=[a]:i(e[n],a),e}),{}),t&&t.dates&&(this.range=t.dates.map((e=>{const t=new Date(e);return isNaN(t)?void 0:[t.getFullYear(),t.getMonth()]})))}render(){this.disabled=this.datesDisabled.reduce(((e,t)=>{const i=new Date(t);return this.year===i.getFullYear()&&e.push(i.getMonth()),e}),[]),this.picker.setViewSwitchLabel(this.year),this.picker.setPrevBtnDisabled(this.year<=this.minYear),this.picker.setNextBtnDisabled(this.year>=this.maxYear);const e=this.selected[this.year]||[],t=this.yearthis.maxYear,i=this.year===this.minYear,s=this.year===this.maxYear,n=se(this.range,this.year);Array.from(this.grid.children).forEach(((a,r)=>{const d=a.classList,o=l(this.year,r,1);if(a.className=`datepicker-cell ${this.cellClass}`,this.isMinView&&(a.dataset.date=o),a.textContent=this.monthNames[r],(t||i&&rthis.maxMonth||this.disabled.includes(r))&&d.add("disabled"),n){const[e,t]=n;r>e&&r{e.classList.remove("range","range-start","range-end","selected","focused")})),Array.from(this.grid.children).forEach(((s,n)=>{const a=s.classList;n>t&&n{e.classList.remove("focused")})),this.grid.children[this.focused].classList.add("focused")}}class ae extends te{constructor(e,t){super(e,t)}init(e,t=!0){var i;t&&(this.navStep=10*this.step,this.beforeShowOption=`beforeShow${i=this.cellClass,[...i].reduce(((e,t,i)=>e+(i?t:t.toUpperCase())),"")}`,this.grid=this.element,this.element.classList.add(this.name,"datepicker-grid"),this.grid.appendChild(F(r("span",12)))),super.init(e)}setOptions(t){if(e(t,"minDate")&&(void 0===t.minDate?this.minYear=this.minDate=void 0:(this.minYear=m(t.minDate,this.step),this.minDate=l(this.minYear,0,1))),e(t,"maxDate")&&(void 0===t.maxDate?this.maxYear=this.maxDate=void 0:(this.maxYear=m(t.maxDate,this.step),this.maxDate=l(this.maxYear,11,31))),this.isMinView?t.datesDisabled&&(this.datesDisabled=t.datesDisabled):this.datesDisabled=[],void 0!==t[this.beforeShowOption]){const e=t[this.beforeShowOption];this.beforeShow="function"==typeof e?e:void 0}}updateFocus(){const e=new Date(this.picker.viewDate),t=m(e,this.navStep),i=t+9*this.step;this.first=t,this.last=i,this.start=t-this.step,this.focused=m(e,this.step)}updateSelection(){const{dates:e,rangepicker:t}=this.picker.datepicker;this.selected=e.reduce(((e,t)=>i(e,m(t,this.step))),[]),t&&t.dates&&(this.range=t.dates.map((e=>{if(void 0!==e)return m(e,this.step)})))}render(){this.disabled=this.datesDisabled.map((e=>new Date(e).getFullYear())),this.picker.setViewSwitchLabel(`${this.first}-${this.last}`),this.picker.setPrevBtnDisabled(this.first<=this.minYear),this.picker.setNextBtnDisabled(this.last>=this.maxYear),Array.from(this.grid.children).forEach(((e,t)=>{const i=e.classList,s=this.start+t*this.step,n=l(s,0,1);if(e.className=`datepicker-cell ${this.cellClass}`,this.isMinView&&(e.dataset.date=n),e.textContent=e.dataset.year=s,0===t?i.add("prev"):11===t&&i.add("next"),(sthis.maxYear||this.disabled.includes(s))&&i.add("disabled"),this.range){const[e,t]=this.range;s>e&&s{e.classList.remove("range","range-start","range-end","selected","focused")})),Array.from(this.grid.children).forEach((i=>{const s=Number(i.textContent),n=i.classList;s>e&&s{e.classList.remove("focused")})),this.grid.children[e].classList.add("focused")}}function re(e,t){const i={date:e.getDate(),viewDate:new Date(e.picker.viewDate),viewId:e.picker.currentView.id,datepicker:e};e.element.dispatchEvent(new CustomEvent(t,{detail:i}))}function de(e,t){const{minDate:i,maxDate:s}=e.config,{currentView:n,viewDate:r}=e.picker;let d;switch(n.id){case 0:d=u(r,t);break;case 1:d=f(r,t);break;default:d=f(r,t*n.navStep)}d=a(d,i,s),e.picker.changeFocus(d).render()}function oe(e){const t=e.picker.currentView.id;t!==e.config.maxView&&e.picker.changeView(t+1).render()}function ce(e){e.config.updateOnBlur?e.update({revert:!0}):e.refresh("input"),e.hide()}function le(e,t){const i=e.picker,s=new Date(i.viewDate),n=i.currentView.id,a=1===n?u(s,t-s.getMonth()):f(s,t-s.getFullYear());i.changeFocus(a).changeView(n-1).render()}function he(e){const t=e.picker,i=c();if(1===e.config.todayBtnMode){if(e.config.autohide)return void e.setDate(i);e.setDate(i,{render:!1}),t.update()}t.viewDate!==i&&t.changeFocus(i),t.changeView(0).render()}function ue(e){e.setDate({clear:!0})}function fe(e){oe(e)}function pe(e){de(e,-1)}function ge(e){de(e,1)}function me(e,t){const i=K(t,".datepicker-cell");if(!i||i.classList.contains("disabled"))return;const{id:s,isMinView:n}=e.picker.currentView;n?e.setDate(Number(i.dataset.date)):le(e,Number(1===s?i.dataset.month:i.dataset.year))}function we(e){e.preventDefault()}const De=["left","top","right","bottom"].reduce(((e,t)=>(e[t]=`datepicker-orient-${t}`,e)),{}),ye=e=>e?`${e}px`:e;function ve(t,i){if(void 0!==i.title&&(i.title?(t.controls.title.textContent=i.title,B(t.controls.title)):(t.controls.title.textContent="",L(t.controls.title))),i.prevArrow){const e=t.controls.prevBtn;A(e),i.prevArrow.forEach((t=>{e.appendChild(t.cloneNode(!0))}))}if(i.nextArrow){const e=t.controls.nextBtn;A(e),i.nextArrow.forEach((t=>{e.appendChild(t.cloneNode(!0))}))}if(i.locale&&(t.controls.todayBtn.textContent=i.locale.today,t.controls.clearBtn.textContent=i.locale.clear),void 0!==i.todayBtn&&(i.todayBtn?B(t.controls.todayBtn):L(t.controls.todayBtn)),e(i,"minDate")||e(i,"maxDate")){const{minDate:e,maxDate:i}=t.datepicker.config;t.controls.todayBtn.disabled=!n(c(),e,i)}void 0!==i.clearBtn&&(i.clearBtn?B(t.controls.clearBtn):L(t.controls.clearBtn))}function be(e){const{dates:i,config:s}=e;return a(i.length>0?t(i):s.defaultViewDate,s.minDate,s.maxDate)}function ke(e,t){const i=new Date(e.viewDate),s=new Date(t),{id:n,year:a,first:r,last:d}=e.currentView,o=s.getFullYear();switch(e.viewDate=t,o!==i.getFullYear()&&re(e.datepicker,"changeYear"),s.getMonth()!==i.getMonth()&&re(e.datepicker,"changeMonth"),n){case 0:return td;case 1:return o!==a;default:return od}}function xe(e){return window.getComputedStyle(e).direction}function Me(e){const t=V(e);if(t!==document.body&&t)return"visible"!==window.getComputedStyle(t).overflow?t:Me(t)}class Se{constructor(e){const{config:t}=this.datepicker=e,i=Q.replace(/%buttonClass%/g,t.buttonClass),s=this.element=F(i).firstChild,[n,a,r]=s.firstChild.children,d=n.firstElementChild,[o,c,l]=n.lastElementChild.children,[h,u]=r.firstChild.children,f={title:d,prevBtn:o,viewSwitch:c,nextBtn:l,todayBtn:h,clearBtn:u};this.main=a,this.controls=f;const p=e.inline?"inline":"dropdown";s.classList.add(`datepicker-${p}`),ve(this,t),this.viewDate=be(e),j(e,[[s,"mousedown",we],[a,"click",me.bind(null,e)],[f.viewSwitch,"click",fe.bind(null,e)],[f.prevBtn,"click",pe.bind(null,e)],[f.nextBtn,"click",ge.bind(null,e)],[f.todayBtn,"click",he.bind(null,e)],[f.clearBtn,"click",ue.bind(null,e)]]),this.views=[new ie(this),new ne(this),new ae(this,{id:2,name:"years",cellClass:"year",step:1}),new ae(this,{id:3,name:"decades",cellClass:"decade",step:10})],this.currentView=this.views[t.startView],this.currentView.render(),this.main.appendChild(this.currentView.element),t.container?t.container.appendChild(this.element):e.inputField.after(this.element)}setOptions(e){ve(this,e),this.views.forEach((t=>{t.init(e,!1)})),this.currentView.render()}detach(){this.element.remove()}show(){if(this.active)return;const{datepicker:e,element:t}=this;if(e.inline)t.classList.add("active");else{const i=xe(e.inputField);i!==xe(V(t))?t.dir=i:t.dir&&t.removeAttribute("dir"),t.style.visiblity="hidden",t.classList.add("active"),this.place(),t.style.visiblity="",e.config.disableTouchKeyboard&&e.inputField.blur()}this.active=!0,re(e,"show")}hide(){this.active&&(this.datepicker.exitEditMode(),this.element.classList.remove("active"),this.active=!1,re(this.datepicker,"hide"))}place(){const{classList:e,offsetParent:t,style:i}=this.element,{config:s,inputField:n}=this.datepicker,{width:a,height:r}=this.element.getBoundingClientRect(),{left:d,top:o,right:c,bottom:l,width:h,height:u}=n.getBoundingClientRect();let{x:f,y:p}=s.orientation,g=d,m=o;if(t!==document.body&&t){const e=t.getBoundingClientRect();g-=e.left-t.scrollLeft,m-=e.top-t.scrollTop}else g+=window.scrollX,m+=window.scrollY;const w=Me(n);let D=0,y=0,{clientWidth:v,clientHeight:b}=document.documentElement;if(w){const e=w.getBoundingClientRect();e.top>0&&(y=e.top),e.left>0&&(D=e.left),e.rightv?(f="right",vy&&l+r>b?"top":"bottom"),"top"===p?m-=r:m+=u,e.remove(...Object.values(De)),e.add(De[f],De[p]),i.left=ye(g),i.top=ye(m)}setViewSwitchLabel(e){this.controls.viewSwitch.textContent=e}setPrevBtnDisabled(e){this.controls.prevBtn.disabled=e}setNextBtnDisabled(e){this.controls.nextBtn.disabled=e}changeView(e){const t=this.currentView,i=this.views[e];return i.id!==t.id&&(this.currentView=i,this._renderMethod="render",re(this.datepicker,"changeView"),this.main.replaceChild(i.element,t.element)),this}changeFocus(e){return this._renderMethod=ke(this,e)?"render":"refreshFocus",this.views.forEach((e=>{e.updateFocus()})),this}update(){const e=be(this.datepicker);return this._renderMethod=ke(this,e)?"render":"refresh",this.views.forEach((e=>{e.updateFocus(),e.updateSelection()})),this}render(e=!0){const t=e&&this._renderMethod||"render";delete this._renderMethod,this.currentView[t]()}}function Oe(e,t,i,s,a,r){if(n(e,a,r)){if(s(e)){return Oe(t(e,i),t,i,s,a,r)}return e}}function Ce(e,t,i,s){const n=e.picker,a=n.currentView,r=a.step||1;let d,o,c=n.viewDate;switch(a.id){case 0:c=s?h(c,7*i):t.ctrlKey||t.metaKey?f(c,i):h(c,i),d=h,o=e=>a.disabled.includes(e);break;case 1:c=u(c,s?4*i:i),d=u,o=e=>{const t=new Date(e),{year:i,disabled:s}=a;return t.getFullYear()===i&&s.includes(t.getMonth())};break;default:c=f(c,i*(s?4:1)*r),d=f,o=e=>a.disabled.includes(m(e,r))}c=Oe(c,d,i<0?-r:r,o,a.minDate,a.maxDate),void 0!==c&&n.changeFocus(c).render()}function Ee(e,t){const i=t.key;if("Tab"===i)return void ce(e);const s=e.picker,{id:n,isMinView:a}=s.currentView;if(s.active){if(e.editMode)return void("Enter"===i?e.exitEditMode({update:!0,autohide:e.config.autohide}):"Escape"===i&&s.hide());if("ArrowLeft"===i)if(t.ctrlKey||t.metaKey)de(e,-1);else{if(t.shiftKey)return void e.enterEditMode();Ce(e,t,-1,!1)}else if("ArrowRight"===i)if(t.ctrlKey||t.metaKey)de(e,1);else{if(t.shiftKey)return void e.enterEditMode();Ce(e,t,1,!1)}else if("ArrowUp"===i)if(t.ctrlKey||t.metaKey)oe(e);else{if(t.shiftKey)return void e.enterEditMode();Ce(e,t,-1,!0)}else if("ArrowDown"===i){if(t.shiftKey&&!t.ctrlKey&&!t.metaKey)return void e.enterEditMode();Ce(e,t,1,!0)}else{if("Enter"!==i)return void("Escape"===i?s.hide():"Backspace"!==i&&"Delete"!==i&&(1!==i.length||t.ctrlKey||t.metaKey)||e.enterEditMode());if(a)return void e.setDate(s.viewDate);s.changeView(n-1).render()}}else{if("ArrowDown"!==i)return void("Enter"===i?e.update():"Escape"===i&&s.show());s.show()}t.preventDefault()}function Fe(e){e.config.showOnFocus&&!e._showing&&e.show()}function Ve(e,t){const i=t.target;(e.picker.active||e.config.showOnClick)&&(i._active=N(i),i._clicking=setTimeout((()=>{delete i._active,delete i._clicking}),2e3))}function Ne(e,t){const i=t.target;i._clicking&&(clearTimeout(i._clicking),delete i._clicking,i._active&&e.enterEditMode(),delete i._active,e.config.showOnClick&&e.show())}function Le(e,t){t.clipboardData.types.includes("text/plain")&&e.enterEditMode()}function Be(e,t){const{element:i,picker:s}=e;if(!s.active&&!N(i))return;const n=s.element;K(t,(e=>e===i||e===n))||ce(e)}function Ae(e,t){return e.map((e=>C(e,t.format,t.locale))).join(t.dateDelimiter)}function Ye(e,t,i=!1){const{config:s,dates:a,rangeSideIndex:r}=e;if(0===t.length)return i?[]:void 0;let d=t.reduce(((e,t)=>{let i=O(t,s.format,s.locale);return void 0===i||(i=w(i,s.pickLevel,r),!n(i,s.minDate,s.maxDate)||e.includes(i)||s.datesDisabled.includes(i)||!(s.pickLevel>0)&&s.daysOfWeekDisabled.includes(new Date(i).getDay())||e.push(i)),e}),[]);return 0!==d.length?(s.multidate&&!i&&(d=d.reduce(((e,t)=>(a.includes(t)||e.push(t),e)),a.filter((e=>!d.includes(e))))),s.maxNumberOfDates&&d.length>s.maxNumberOfDates?d.slice(-1*s.maxNumberOfDates):d):void 0}function We(e,t=3,i=!0){const{config:s,picker:n,inputField:a}=e;if(2&t){const e=n.active?s.pickLevel:s.startView;n.update().changeView(e).render(i)}1&t&&a&&(a.value=Ae(e.dates,s))}function He(e,t,i){let{clear:s,render:n,autohide:a,revert:r}=i;void 0===n&&(n=!0),n?void 0===a&&(a=e.config.autohide):a=!1;const d=Ye(e,t,s);(d||r)&&(d&&d.toString()!==e.dates.toString()?(e.dates=d,We(e,n?3:1),re(e,"changeDate")):We(e,1),a&&e.hide())}class je{constructor(e,t={},i){e.datepicker=this,this.element=e;const n=this.config=Object.assign({buttonClass:t.buttonClass&&String(t.buttonClass)||"button",container:null,defaultViewDate:c(),maxDate:void 0,minDate:void 0},G(R,this)),a=this.inline="INPUT"!==e.tagName;let r,d;if(a?n.container=e:(t.container&&(n.container=t.container instanceof HTMLElement?t.container:document.querySelector(t.container)),r=this.inputField=e,r.classList.add("datepicker-input")),i){const e=i.inputs.indexOf(r),t=i.datepickers;if(e<0||e>1||!Array.isArray(t))throw Error("Invalid rangepicker object.");t[e]=this,Object.defineProperty(this,"rangepicker",{get:()=>i}),Object.defineProperty(this,"rangeSideIndex",{get:()=>e})}this._options=t,Object.assign(n,G(t,this)),a?(d=s(e.dataset.date,n.dateDelimiter),delete e.dataset.date):d=s(r.value,n.dateDelimiter),this.dates=[];const o=Ye(this,d);o&&o.length>0&&(this.dates=o),r&&(r.value=Ae(this.dates,n));const l=this.picker=new Se(this);if(a)this.show();else{const e=Be.bind(null,this);j(this,[[r,"keydown",Ee.bind(null,this)],[r,"focus",Fe.bind(null,this)],[r,"mousedown",Ve.bind(null,this)],[r,"click",Ne.bind(null,this)],[r,"paste",Le.bind(null,this)],[document,"mousedown",e],[document,"touchstart",e],[window,"resize",l.place.bind(l)]])}}static formatDate(e,t,i){return C(e,t,i&&$[i]||$.en)}static parseDate(e,t,i){return O(e,t,i&&$[i]||$.en)}static get locales(){return $}get active(){return!(!this.picker||!this.picker.active)}get pickerElement(){return this.picker?this.picker.element:void 0}setOptions(e){const t=this.picker,i=G(e,this);Object.assign(this._options,e),Object.assign(this.config,i),t.setOptions(i),We(this,3)}show(){if(this.inputField){if(this.inputField.disabled)return;N(this.inputField)||this.config.disableTouchKeyboard||(this._showing=!0,this.inputField.focus(),delete this._showing)}this.picker.show()}hide(){this.inline||(this.picker.hide(),this.picker.update().changeView(this.config.startView).render())}destroy(){return this.hide(),T(this),this.picker.detach(),this.inline||this.inputField.classList.remove("datepicker-input"),delete this.element.datepicker,this}getDate(e){const t=e?t=>C(t,e,this.config.locale):e=>new Date(e);return this.config.multidate?this.dates.map(t):this.dates.length>0?t(this.dates[0]):void 0}setDate(...e){const i=[...e],s={},n=t(e);"object"!=typeof n||Array.isArray(n)||n instanceof Date||!n||Object.assign(s,i.pop());He(this,Array.isArray(i[0])?i[0]:i,s)}update(e){if(this.inline)return;const t=Object.assign(e||{},{clear:!0,render:!0});He(this,s(this.inputField.value,this.config.dateDelimiter),t)}refresh(e,t=!1){let i;e&&"string"!=typeof e&&(t=e,e=void 0),i="picker"===e?2:"input"===e?1:3,We(this,i,!t)}enterEditMode(){this.inline||!this.picker.active||this.editMode||(this.editMode=!0,this.inputField.classList.add("in-edit"))}exitEditMode(e){if(this.inline||!this.editMode)return;const t=Object.assign({update:!1},e);delete this.editMode,this.inputField.classList.remove("in-edit"),t.update&&this.update(t)}}function Te(e){const t=Object.assign({},e);return delete t.inputs,delete t.allowOneSidedRange,delete t.maxNumberOfDates,t}function _e(e,t,i,s){j(e,[[i,"changeDate",t]]),new je(i,s,e)}function Ke(e,t){if(e._updating)return;e._updating=!0;const i=t.target;if(void 0===i.datepicker)return;const s=e.datepickers,n={render:!1},a=e.inputs.indexOf(i),r=0===a?1:0,d=s[a].dates[0],o=s[r].dates[0];void 0!==d&&void 0!==o?0===a&&d>o?(s[0].setDate(o,n),s[1].setDate(d,n)):1===a&&da}),_e(this,s,this.inputs[0],n),_e(this,s,this.inputs[1],n),Object.freeze(a),a[0].dates.length>0?Ke(this,{target:this.inputs[0]}):a[1].dates.length>0&&Ke(this,{target:this.inputs[1]})}get dates(){return 2===this.datepickers.length?[this.datepickers[0].dates[0],this.datepickers[1].dates[0]]:void 0}setOptions(e){this.allowOneSidedRange=!!e.allowOneSidedRange;const t=Te(e);this.datepickers[0].setOptions(t),this.datepickers[1].setOptions(t)}destroy(){this.datepickers[0].destroy(),this.datepickers[1].destroy(),T(this),delete this.element.rangepicker}getDates(e){const t=e?t=>C(t,e,this.datepickers[0].config.locale):e=>new Date(e);return this.dates.map((e=>void 0===e?e:t(e)))}setDates(e,t){const[i,s]=this.datepickers,n=this.dates;this._updating=!0,i.setDate(e),s.setDate(t),delete this._updating,s.dates[0]!==n[1]?Ke(this,{target:this.inputs[1]}):i.dates[0]!==n[0]&&Ke(this,{target:this.inputs[0]})}}}();
\ No newline at end of file
diff --git a/src/ui/static/js/datepicker/datepicker.min.js b/src/ui/static/js/datepicker/datepicker.min.js
new file mode 100644
index 000000000..7590ab30b
--- /dev/null
+++ b/src/ui/static/js/datepicker/datepicker.min.js
@@ -0,0 +1 @@
+var Datepicker=function(){"use strict";function e(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function t(e){return e[e.length-1]}function i(e,...t){return t.forEach((t=>{e.includes(t)||e.push(t)})),e}function s(e,t){return e?e.split(t):[]}function n(e,t,i){return(void 0===t||e>=t)&&(void 0===i||e<=i)}function a(e,t,i){return ei?i:e}function r(e,t,i={},s=0,n=""){n+=`<${Object.keys(i).reduce(((e,t)=>{let n=i[t];return"function"==typeof n&&(n=n(s)),`${e} ${t}="${n}"`}),e)}>${e}>`;const a=s+1;return a\s+/g,">").replace(/\s+,"<")}function o(e){return new Date(e).setHours(0,0,0,0)}function c(){return(new Date).setHours(0,0,0,0)}function l(...e){switch(e.length){case 0:return c();case 1:return o(e[0])}const t=new Date(0);return t.setFullYear(...e),t.setHours(0,0,0,0)}function h(e,t){const i=new Date(e);return i.setDate(i.getDate()+t)}function u(e,t){const i=new Date(e),s=i.getMonth()+t;let n=s%12;n<0&&(n+=12);const a=i.setMonth(s);return i.getMonth()!==n?i.setDate(0):a}function f(e,t){const i=new Date(e),s=i.getMonth(),n=i.setFullYear(i.getFullYear()+t);return 1===s&&2===i.getMonth()?i.setDate(0):n}function p(e,t){return(e-t+7)%7}function g(e,t,i=0){const s=new Date(e).getDay();return h(e,p(t,i)-p(s,i))}function m(e,t){const i=new Date(e).getFullYear();return Math.floor(i/t)*t}function w(e,t,i){if(1!==t&&2!==t)return e;const s=new Date(e);return 1===t?i?s.setMonth(s.getMonth()+1,0):s.setDate(1):i?s.setFullYear(s.getFullYear()+1,0,0):s.setMonth(0,1),s.setHours(0,0,0,0)}const y=/dd?|DD?|mm?|MM?|yy?(?:yy)?/,D=/[\s!-/:-@[-`{-~年月日]+/;let v={};const b={y:(e,t)=>new Date(e).setFullYear(parseInt(t,10)),m(e,t,i){const s=new Date(e);let n=parseInt(t,10)-1;if(isNaN(n)){if(!t)return NaN;const e=t.toLowerCase(),s=t=>t.toLowerCase().startsWith(e);if(n=i.monthsShort.findIndex(s),n<0&&(n=i.months.findIndex(s)),n<0)return NaN}return s.setMonth(n),s.getMonth()!==x(n)?s.setDate(0):s.getTime()},d:(e,t)=>new Date(e).setDate(parseInt(t,10))},k={d:e=>e.getDate(),dd:e=>M(e.getDate(),2),D:(e,t)=>t.daysShort[e.getDay()],DD:(e,t)=>t.days[e.getDay()],m:e=>e.getMonth()+1,mm:e=>M(e.getMonth()+1,2),M:(e,t)=>t.monthsShort[e.getMonth()],MM:(e,t)=>t.months[e.getMonth()],y:e=>e.getFullYear(),yy:e=>M(e.getFullYear(),2).slice(-2),yyyy:e=>M(e.getFullYear(),4)};function x(e){return e>-1?e%12:x(e+12)}function M(e,t){return e.toString().padStart(t,"0")}function S(e){if("string"!=typeof e)throw new Error("Invalid date format.");if(e in v)return v[e];const i=e.split(y),s=e.match(new RegExp(y,"g"));if(0===i.length||!s)throw new Error("Invalid date format.");const n=s.map((e=>k[e])),a=Object.keys(b).reduce(((e,t)=>(s.find((e=>"D"!==e[0]&&e[0].toLowerCase()===t))&&e.push(t),e)),[]);return v[e]={parser(e,t){const i=e.split(D).reduce(((e,t,i)=>{if(t.length>0&&s[i]){const n=s[i][0];"M"===n?e.m=t:"D"!==n&&(e[n]=t)}return e}),{});return a.reduce(((e,s)=>{const n=b[s](e,i[s],t);return isNaN(n)?e:n}),c())},formatter:(e,s)=>n.reduce(((t,n,a)=>t+`${i[a]}${n(e,s)}`),"")+t(i)}}function C(e,t,i){if(e instanceof Date||"number"==typeof e){const t=o(e);return isNaN(t)?void 0:t}if(e){if("today"===e)return c();if(t&&t.toValue){const s=t.toValue(e,t,i);return isNaN(s)?void 0:o(s)}return S(t).parser(e,i)}}function O(e,t,i){if(isNaN(e)||!e&&0!==e)return"";const s="number"==typeof e?new Date(e):e;return t.toDisplay?t.toDisplay(s,t,i):S(t).formatter(s,i)}const E=document.createRange();function F(e){return E.createContextualFragment(e)}function V(e){return e.parentElement||(e.parentNode instanceof ShadowRoot?e.parentNode.host:void 0)}function N(e){return e.getRootNode().activeElement===e}function L(e){"none"!==e.style.display&&(e.style.display&&(e.dataset.styleDisplay=e.style.display),e.style.display="none")}function B(e){"none"===e.style.display&&(e.dataset.styleDisplay?(e.style.display=e.dataset.styleDisplay,delete e.dataset.styleDisplay):e.style.display="")}function Y(e){e.firstChild&&(e.removeChild(e.firstChild),Y(e))}const A=new WeakMap,{addEventListener:W,removeEventListener:H}=EventTarget.prototype;function T(e,t){let i=A.get(e);i||(i=[],A.set(e,i)),t.forEach((e=>{W.call(...e),i.push(e)}))}if(!Event.prototype.composedPath){const e=(t,i=[])=>{let s;return i.push(t),t.parentNode?s=t.parentNode:t.host?s=t.host:t.defaultView&&(s=t.defaultView),s?e(s,i):i};Event.prototype.composedPath=function(){return e(this.target)}}function K(e,t,i){const[s,...n]=e;return t(s)?s:s!==i&&"HTML"!==s.tagName&&0!==n.length?K(n,t,i):void 0}function j(e,t){const i="function"==typeof t?t:e=>e instanceof Element&&e.matches(t);return K(e.composedPath(),i,e.currentTarget)}const $={en:{days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",clear:"Clear",titleFormat:"MM y"}},_={autohide:!1,beforeShowDay:null,beforeShowDecade:null,beforeShowMonth:null,beforeShowYear:null,calendarWeeks:!1,clearBtn:!1,dateDelimiter:",",datesDisabled:[],daysOfWeekDisabled:[],daysOfWeekHighlighted:[],defaultViewDate:void 0,disableTouchKeyboard:!1,format:"mm/dd/yyyy",language:"en",maxDate:null,maxNumberOfDates:1,maxView:3,minDate:null,nextArrow:"»",orientation:"auto",pickLevel:0,prevArrow:"«",showDaysOfWeek:!0,showOnClick:!0,showOnFocus:!0,startView:0,title:"",todayBtn:!1,todayBtnMode:0,todayHighlight:!1,updateOnBlur:!0,weekStart:0},{language:I,format:P,weekStart:R}=_;function q(e,t){return e.length<6&&t>=0&&t<7?i(e,t):e}function J(e){return(e+6)%7}function U(e,t,i,s){const n=C(e,t,i);return void 0!==n?n:s}function z(e,t,i=3){const s=parseInt(e,10);return s>=0&&s<=i?s:t}function X(t,s){const n=Object.assign({},t),a={},r=s.constructor.locales,d=s.rangeSideIndex;let{format:o,language:c,locale:h,maxDate:u,maxView:f,minDate:p,pickLevel:g,startView:m,weekStart:D}=s.config||{};if(n.language){let e;if(n.language!==c&&(r[n.language]?e=n.language:(e=n.language.split("-")[0],void 0===r[e]&&(e=!1))),delete n.language,e){c=a.language=e;const t=h||r[I];h=Object.assign({format:P,weekStart:R},r[I]),c!==I&&Object.assign(h,r[c]),a.locale=h,o===t.format&&(o=a.format=h.format),D===t.weekStart&&(D=a.weekStart=h.weekStart,a.weekEnd=J(h.weekStart))}}if(n.format){const e="function"==typeof n.format.toDisplay,t="function"==typeof n.format.toValue,i=y.test(n.format);(e&&t||i)&&(o=a.format=n.format),delete n.format}let v=g;void 0!==n.pickLevel&&(v=z(n.pickLevel,2),delete n.pickLevel),v!==g&&(v>g&&(void 0===n.minDate&&(n.minDate=p),void 0===n.maxDate&&(n.maxDate=u)),n.datesDisabled||(n.datesDisabled=[]),g=a.pickLevel=v);let b=p,k=u;if(void 0!==n.minDate){const e=l(0,0,1);b=null===n.minDate?e:U(n.minDate,o,h,b),b!==e&&(b=w(b,g,!1)),delete n.minDate}if(void 0!==n.maxDate&&(k=null===n.maxDate?void 0:U(n.maxDate,o,h,k),void 0!==k&&(k=w(k,g,!0)),delete n.maxDate),k{const s=C(t,o,h);return void 0!==s?i(e,w(s,g,d)):e}),[]),delete n.datesDisabled),void 0!==n.defaultViewDate){const e=C(n.defaultViewDate,o,h);void 0!==e&&(a.defaultViewDate=e),delete n.defaultViewDate}if(void 0!==n.weekStart){const e=Number(n.weekStart)%7;isNaN(e)||(D=a.weekStart=e,a.weekEnd=J(e)),delete n.weekStart}if(n.daysOfWeekDisabled&&(a.daysOfWeekDisabled=n.daysOfWeekDisabled.reduce(q,[]),delete n.daysOfWeekDisabled),n.daysOfWeekHighlighted&&(a.daysOfWeekHighlighted=n.daysOfWeekHighlighted.reduce(q,[]),delete n.daysOfWeekHighlighted),void 0!==n.maxNumberOfDates){const e=parseInt(n.maxNumberOfDates,10);e>=0&&(a.maxNumberOfDates=e,a.multidate=1!==e),delete n.maxNumberOfDates}n.dateDelimiter&&(a.dateDelimiter=String(n.dateDelimiter),delete n.dateDelimiter);let x=f;void 0!==n.maxView&&(x=z(n.maxView,f),delete n.maxView),x=g>x?g:x,x!==f&&(f=a.maxView=x);let M=m;if(void 0!==n.startView&&(M=z(n.startView,M),delete n.startView),Mf&&(M=f),M!==m&&(a.startView=M),n.prevArrow){const e=F(n.prevArrow);e.childNodes.length>0&&(a.prevArrow=e.childNodes),delete n.prevArrow}if(n.nextArrow){const e=F(n.nextArrow);e.childNodes.length>0&&(a.nextArrow=e.childNodes),delete n.nextArrow}if(void 0!==n.disableTouchKeyboard&&(a.disableTouchKeyboard="ontouchstart"in document&&!!n.disableTouchKeyboard,delete n.disableTouchKeyboard),n.orientation){const e=n.orientation.toLowerCase().split(/\s+/g);a.orientation={x:e.find((e=>"left"===e||"right"===e))||"auto",y:e.find((e=>"top"===e||"bottom"===e))||"auto"},delete n.orientation}if(void 0!==n.todayBtnMode){switch(n.todayBtnMode){case 0:case 1:a.todayBtnMode=n.todayBtnMode}delete n.todayBtnMode}return Object.keys(n).forEach((t=>{void 0!==n[t]&&e(_,t)&&(a[t]=n[t])})),a}const G=d(''),Q=d(`\n
${r("span",7,{class:"dow"})}
\n
${r("span",42)}
\n
`),Z=d(`\n
\n
${r("span",6,{class:"week"})}
\n
`);class ee{constructor(e,t){Object.assign(this,t,{picker:e,element:F('').firstChild,selected:[]}),this.init(this.picker.datepicker.config)}init(e){void 0!==e.pickLevel&&(this.isMinView=this.id===e.pickLevel),this.setOptions(e),this.updateFocus(),this.updateSelection()}performBeforeHook(e,t,s){let n=this.beforeShow(new Date(s));switch(typeof n){case"boolean":n={enabled:n};break;case"string":n={classes:n}}if(n){if(!1===n.enabled&&(e.classList.add("disabled"),i(this.disabled,t)),n.classes){const s=n.classes.split(/\s+/);e.classList.add(...s),s.includes("disabled")&&i(this.disabled,t)}n.content&&function(e,t){Y(e),t instanceof DocumentFragment?e.appendChild(t):"string"==typeof t?e.appendChild(F(t)):"function"==typeof t.forEach&&t.forEach((t=>{e.appendChild(t)}))}(e,n.content)}}}class te extends ee{constructor(e){super(e,{id:0,name:"days",cellClass:"day"})}init(e,t=!0){if(t){const e=F(Q).firstChild;this.dow=e.firstChild,this.grid=e.lastChild,this.element.appendChild(e)}super.init(e)}setOptions(t){let i;if(e(t,"minDate")&&(this.minDate=t.minDate),e(t,"maxDate")&&(this.maxDate=t.maxDate),t.datesDisabled&&(this.datesDisabled=t.datesDisabled),t.daysOfWeekDisabled&&(this.daysOfWeekDisabled=t.daysOfWeekDisabled,i=!0),t.daysOfWeekHighlighted&&(this.daysOfWeekHighlighted=t.daysOfWeekHighlighted),void 0!==t.todayHighlight&&(this.todayHighlight=t.todayHighlight),void 0!==t.weekStart&&(this.weekStart=t.weekStart,this.weekEnd=t.weekEnd,i=!0),t.locale){const e=this.locale=t.locale;this.dayNames=e.daysMin,this.switchLabelFormat=e.titleFormat,i=!0}if(void 0!==t.beforeShowDay&&(this.beforeShow="function"==typeof t.beforeShowDay?t.beforeShowDay:void 0),void 0!==t.calendarWeeks)if(t.calendarWeeks&&!this.calendarWeeks){const e=F(Z).firstChild;this.calendarWeeks={element:e,dow:e.firstChild,weeks:e.lastChild},this.element.insertBefore(e,this.element.firstChild)}else this.calendarWeeks&&!t.calendarWeeks&&(this.element.removeChild(this.calendarWeeks.element),this.calendarWeeks=null);void 0!==t.showDaysOfWeek&&(t.showDaysOfWeek?(B(this.dow),this.calendarWeeks&&B(this.calendarWeeks.dow)):(L(this.dow),this.calendarWeeks&&L(this.calendarWeeks.dow))),i&&Array.from(this.dow.children).forEach(((e,t)=>{const i=(this.weekStart+t)%7;e.textContent=this.dayNames[i],e.className=this.daysOfWeekDisabled.includes(i)?"dow disabled":"dow"}))}updateFocus(){const e=new Date(this.picker.viewDate),t=e.getFullYear(),i=e.getMonth(),s=l(t,i,1),n=g(s,this.weekStart,this.weekStart);this.first=s,this.last=l(t,i+1,0),this.start=n,this.focused=this.picker.viewDate}updateSelection(){const{dates:e,rangepicker:t}=this.picker.datepicker;this.selected=e,t&&(this.range=t.dates)}render(){this.today=this.todayHighlight?c():void 0,this.disabled=[...this.datesDisabled];const e=O(this.focused,this.switchLabelFormat,this.locale);if(this.picker.setViewSwitchLabel(e),this.picker.setPrevBtnDisabled(this.first<=this.minDate),this.picker.setNextBtnDisabled(this.last>=this.maxDate),this.calendarWeeks){const e=g(this.first,1,1);Array.from(this.calendarWeeks.weeks.children).forEach(((t,i)=>{t.textContent=function(e){const t=g(e,4,1),i=g(new Date(t).setMonth(0,4),4,1);return Math.round((t-i)/6048e5)+1}(h(e,7*i))}))}Array.from(this.grid.children).forEach(((e,t)=>{const s=e.classList,n=h(this.start,t),a=new Date(n),r=a.getDay();if(e.className=`datepicker-cell ${this.cellClass}`,e.dataset.date=n,e.textContent=a.getDate(),nthis.last&&s.add("next"),this.today===n&&s.add("today"),(nthis.maxDate||this.disabled.includes(n))&&s.add("disabled"),this.daysOfWeekDisabled.includes(r)&&(s.add("disabled"),i(this.disabled,n)),this.daysOfWeekHighlighted.includes(r)&&s.add("highlighted"),this.range){const[e,t]=this.range;n>e&&n{e.classList.remove("range","range-start","range-end","selected","focused")})),Array.from(this.grid.children).forEach((i=>{const s=Number(i.dataset.date),n=i.classList;s>e&&s{e.classList.remove("focused")})),this.grid.children[e].classList.add("focused")}}function ie(e,t){if(!e||!e[0]||!e[1])return;const[[i,s],[n,a]]=e;return i>t||ne})))),super.init(e)}setOptions(t){if(t.locale&&(this.monthNames=t.locale.monthsShort),e(t,"minDate"))if(void 0===t.minDate)this.minYear=this.minMonth=this.minDate=void 0;else{const e=new Date(t.minDate);this.minYear=e.getFullYear(),this.minMonth=e.getMonth(),this.minDate=e.setDate(1)}if(e(t,"maxDate"))if(void 0===t.maxDate)this.maxYear=this.maxMonth=this.maxDate=void 0;else{const e=new Date(t.maxDate);this.maxYear=e.getFullYear(),this.maxMonth=e.getMonth(),this.maxDate=l(this.maxYear,this.maxMonth+1,0)}this.isMinView?t.datesDisabled&&(this.datesDisabled=t.datesDisabled):this.datesDisabled=[],void 0!==t.beforeShowMonth&&(this.beforeShow="function"==typeof t.beforeShowMonth?t.beforeShowMonth:void 0)}updateFocus(){const e=new Date(this.picker.viewDate);this.year=e.getFullYear(),this.focused=e.getMonth()}updateSelection(){const{dates:e,rangepicker:t}=this.picker.datepicker;this.selected=e.reduce(((e,t)=>{const s=new Date(t),n=s.getFullYear(),a=s.getMonth();return void 0===e[n]?e[n]=[a]:i(e[n],a),e}),{}),t&&t.dates&&(this.range=t.dates.map((e=>{const t=new Date(e);return isNaN(t)?void 0:[t.getFullYear(),t.getMonth()]})))}render(){this.disabled=this.datesDisabled.reduce(((e,t)=>{const i=new Date(t);return this.year===i.getFullYear()&&e.push(i.getMonth()),e}),[]),this.picker.setViewSwitchLabel(this.year),this.picker.setPrevBtnDisabled(this.year<=this.minYear),this.picker.setNextBtnDisabled(this.year>=this.maxYear);const e=this.selected[this.year]||[],t=this.yearthis.maxYear,i=this.year===this.minYear,s=this.year===this.maxYear,n=ie(this.range,this.year);Array.from(this.grid.children).forEach(((a,r)=>{const d=a.classList,o=l(this.year,r,1);if(a.className=`datepicker-cell ${this.cellClass}`,this.isMinView&&(a.dataset.date=o),a.textContent=this.monthNames[r],(t||i&&rthis.maxMonth||this.disabled.includes(r))&&d.add("disabled"),n){const[e,t]=n;r>e&&r{e.classList.remove("range","range-start","range-end","selected","focused")})),Array.from(this.grid.children).forEach(((s,n)=>{const a=s.classList;n>t&&n{e.classList.remove("focused")})),this.grid.children[this.focused].classList.add("focused")}}class ne extends ee{constructor(e,t){super(e,t)}init(e,t=!0){var i;t&&(this.navStep=10*this.step,this.beforeShowOption=`beforeShow${i=this.cellClass,[...i].reduce(((e,t,i)=>e+(i?t:t.toUpperCase())),"")}`,this.grid=this.element,this.element.classList.add(this.name,"datepicker-grid"),this.grid.appendChild(F(r("span",12)))),super.init(e)}setOptions(t){if(e(t,"minDate")&&(void 0===t.minDate?this.minYear=this.minDate=void 0:(this.minYear=m(t.minDate,this.step),this.minDate=l(this.minYear,0,1))),e(t,"maxDate")&&(void 0===t.maxDate?this.maxYear=this.maxDate=void 0:(this.maxYear=m(t.maxDate,this.step),this.maxDate=l(this.maxYear,11,31))),this.isMinView?t.datesDisabled&&(this.datesDisabled=t.datesDisabled):this.datesDisabled=[],void 0!==t[this.beforeShowOption]){const e=t[this.beforeShowOption];this.beforeShow="function"==typeof e?e:void 0}}updateFocus(){const e=new Date(this.picker.viewDate),t=m(e,this.navStep),i=t+9*this.step;this.first=t,this.last=i,this.start=t-this.step,this.focused=m(e,this.step)}updateSelection(){const{dates:e,rangepicker:t}=this.picker.datepicker;this.selected=e.reduce(((e,t)=>i(e,m(t,this.step))),[]),t&&t.dates&&(this.range=t.dates.map((e=>{if(void 0!==e)return m(e,this.step)})))}render(){this.disabled=this.datesDisabled.map((e=>new Date(e).getFullYear())),this.picker.setViewSwitchLabel(`${this.first}-${this.last}`),this.picker.setPrevBtnDisabled(this.first<=this.minYear),this.picker.setNextBtnDisabled(this.last>=this.maxYear),Array.from(this.grid.children).forEach(((e,t)=>{const i=e.classList,s=this.start+t*this.step,n=l(s,0,1);if(e.className=`datepicker-cell ${this.cellClass}`,this.isMinView&&(e.dataset.date=n),e.textContent=e.dataset.year=s,0===t?i.add("prev"):11===t&&i.add("next"),(sthis.maxYear||this.disabled.includes(s))&&i.add("disabled"),this.range){const[e,t]=this.range;s>e&&s{e.classList.remove("range","range-start","range-end","selected","focused")})),Array.from(this.grid.children).forEach((i=>{const s=Number(i.textContent),n=i.classList;s>e&&s{e.classList.remove("focused")})),this.grid.children[e].classList.add("focused")}}function ae(e,t){const i={date:e.getDate(),viewDate:new Date(e.picker.viewDate),viewId:e.picker.currentView.id,datepicker:e};e.element.dispatchEvent(new CustomEvent(t,{detail:i}))}function re(e,t){const{minDate:i,maxDate:s}=e.config,{currentView:n,viewDate:r}=e.picker;let d;switch(n.id){case 0:d=u(r,t);break;case 1:d=f(r,t);break;default:d=f(r,t*n.navStep)}d=a(d,i,s),e.picker.changeFocus(d).render()}function de(e){const t=e.picker.currentView.id;t!==e.config.maxView&&e.picker.changeView(t+1).render()}function oe(e){e.config.updateOnBlur?e.update({revert:!0}):e.refresh("input"),e.hide()}function ce(e,t){const i=e.picker,s=new Date(i.viewDate),n=i.currentView.id,a=1===n?u(s,t-s.getMonth()):f(s,t-s.getFullYear());i.changeFocus(a).changeView(n-1).render()}function le(e){const t=e.picker,i=c();if(1===e.config.todayBtnMode){if(e.config.autohide)return void e.setDate(i);e.setDate(i,{render:!1}),t.update()}t.viewDate!==i&&t.changeFocus(i),t.changeView(0).render()}function he(e){e.setDate({clear:!0})}function ue(e){de(e)}function fe(e){re(e,-1)}function pe(e){re(e,1)}function ge(e,t){const i=j(t,".datepicker-cell");if(!i||i.classList.contains("disabled"))return;const{id:s,isMinView:n}=e.picker.currentView;n?e.setDate(Number(i.dataset.date)):ce(e,Number(1===s?i.dataset.month:i.dataset.year))}function me(e){e.preventDefault()}const we=["left","top","right","bottom"].reduce(((e,t)=>(e[t]=`datepicker-orient-${t}`,e)),{}),ye=e=>e?`${e}px`:e;function De(t,i){if(void 0!==i.title&&(i.title?(t.controls.title.textContent=i.title,B(t.controls.title)):(t.controls.title.textContent="",L(t.controls.title))),i.prevArrow){const e=t.controls.prevBtn;Y(e),i.prevArrow.forEach((t=>{e.appendChild(t.cloneNode(!0))}))}if(i.nextArrow){const e=t.controls.nextBtn;Y(e),i.nextArrow.forEach((t=>{e.appendChild(t.cloneNode(!0))}))}if(i.locale&&(t.controls.todayBtn.textContent=i.locale.today,t.controls.clearBtn.textContent=i.locale.clear),void 0!==i.todayBtn&&(i.todayBtn?B(t.controls.todayBtn):L(t.controls.todayBtn)),e(i,"minDate")||e(i,"maxDate")){const{minDate:e,maxDate:i}=t.datepicker.config;t.controls.todayBtn.disabled=!n(c(),e,i)}void 0!==i.clearBtn&&(i.clearBtn?B(t.controls.clearBtn):L(t.controls.clearBtn))}function ve(e){const{dates:i,config:s}=e;return a(i.length>0?t(i):s.defaultViewDate,s.minDate,s.maxDate)}function be(e,t){const i=new Date(e.viewDate),s=new Date(t),{id:n,year:a,first:r,last:d}=e.currentView,o=s.getFullYear();switch(e.viewDate=t,o!==i.getFullYear()&&ae(e.datepicker,"changeYear"),s.getMonth()!==i.getMonth()&&ae(e.datepicker,"changeMonth"),n){case 0:return td;case 1:return o!==a;default:return od}}function ke(e){return window.getComputedStyle(e).direction}function xe(e){const t=V(e);if(t!==document.body&&t)return"visible"!==window.getComputedStyle(t).overflow?t:xe(t)}class Me{constructor(e){const{config:t}=this.datepicker=e,i=G.replace(/%buttonClass%/g,t.buttonClass),s=this.element=F(i).firstChild,[n,a,r]=s.firstChild.children,d=n.firstElementChild,[o,c,l]=n.lastElementChild.children,[h,u]=r.firstChild.children,f={title:d,prevBtn:o,viewSwitch:c,nextBtn:l,todayBtn:h,clearBtn:u};this.main=a,this.controls=f;const p=e.inline?"inline":"dropdown";s.classList.add(`datepicker-${p}`),De(this,t),this.viewDate=ve(e),T(e,[[s,"mousedown",me],[a,"click",ge.bind(null,e)],[f.viewSwitch,"click",ue.bind(null,e)],[f.prevBtn,"click",fe.bind(null,e)],[f.nextBtn,"click",pe.bind(null,e)],[f.todayBtn,"click",le.bind(null,e)],[f.clearBtn,"click",he.bind(null,e)]]),this.views=[new te(this),new se(this),new ne(this,{id:2,name:"years",cellClass:"year",step:1}),new ne(this,{id:3,name:"decades",cellClass:"decade",step:10})],this.currentView=this.views[t.startView],this.currentView.render(),this.main.appendChild(this.currentView.element),t.container?t.container.appendChild(this.element):e.inputField.after(this.element)}setOptions(e){De(this,e),this.views.forEach((t=>{t.init(e,!1)})),this.currentView.render()}detach(){this.element.remove()}show(){if(this.active)return;const{datepicker:e,element:t}=this;if(e.inline)t.classList.add("active");else{const i=ke(e.inputField);i!==ke(V(t))?t.dir=i:t.dir&&t.removeAttribute("dir"),t.style.visiblity="hidden",t.classList.add("active"),this.place(),t.style.visiblity="",e.config.disableTouchKeyboard&&e.inputField.blur()}this.active=!0,ae(e,"show")}hide(){this.active&&(this.datepicker.exitEditMode(),this.element.classList.remove("active"),this.active=!1,ae(this.datepicker,"hide"))}place(){const{classList:e,offsetParent:t,style:i}=this.element,{config:s,inputField:n}=this.datepicker,{width:a,height:r}=this.element.getBoundingClientRect(),{left:d,top:o,right:c,bottom:l,width:h,height:u}=n.getBoundingClientRect();let{x:f,y:p}=s.orientation,g=d,m=o;if(t!==document.body&&t){const e=t.getBoundingClientRect();g-=e.left-t.scrollLeft,m-=e.top-t.scrollTop}else g+=window.scrollX,m+=window.scrollY;const w=xe(n);let y=0,D=0,{clientWidth:v,clientHeight:b}=document.documentElement;if(w){const e=w.getBoundingClientRect();e.top>0&&(D=e.top),e.left>0&&(y=e.left),e.rightv?(f="right",vD&&l+r>b?"top":"bottom"),"top"===p?m-=r:m+=u,e.remove(...Object.values(we)),e.add(we[f],we[p]),i.left=ye(g),i.top=ye(m)}setViewSwitchLabel(e){this.controls.viewSwitch.textContent=e}setPrevBtnDisabled(e){this.controls.prevBtn.disabled=e}setNextBtnDisabled(e){this.controls.nextBtn.disabled=e}changeView(e){const t=this.currentView,i=this.views[e];return i.id!==t.id&&(this.currentView=i,this._renderMethod="render",ae(this.datepicker,"changeView"),this.main.replaceChild(i.element,t.element)),this}changeFocus(e){return this._renderMethod=be(this,e)?"render":"refreshFocus",this.views.forEach((e=>{e.updateFocus()})),this}update(){const e=ve(this.datepicker);return this._renderMethod=be(this,e)?"render":"refresh",this.views.forEach((e=>{e.updateFocus(),e.updateSelection()})),this}render(e=!0){const t=e&&this._renderMethod||"render";delete this._renderMethod,this.currentView[t]()}}function Se(e,t,i,s,a,r){if(n(e,a,r)){if(s(e)){return Se(t(e,i),t,i,s,a,r)}return e}}function Ce(e,t,i,s){const n=e.picker,a=n.currentView,r=a.step||1;let d,o,c=n.viewDate;switch(a.id){case 0:c=s?h(c,7*i):t.ctrlKey||t.metaKey?f(c,i):h(c,i),d=h,o=e=>a.disabled.includes(e);break;case 1:c=u(c,s?4*i:i),d=u,o=e=>{const t=new Date(e),{year:i,disabled:s}=a;return t.getFullYear()===i&&s.includes(t.getMonth())};break;default:c=f(c,i*(s?4:1)*r),d=f,o=e=>a.disabled.includes(m(e,r))}c=Se(c,d,i<0?-r:r,o,a.minDate,a.maxDate),void 0!==c&&n.changeFocus(c).render()}function Oe(e,t){const i=t.key;if("Tab"===i)return void oe(e);const s=e.picker,{id:n,isMinView:a}=s.currentView;if(s.active){if(e.editMode)return void("Enter"===i?e.exitEditMode({update:!0,autohide:e.config.autohide}):"Escape"===i&&s.hide());if("ArrowLeft"===i)if(t.ctrlKey||t.metaKey)re(e,-1);else{if(t.shiftKey)return void e.enterEditMode();Ce(e,t,-1,!1)}else if("ArrowRight"===i)if(t.ctrlKey||t.metaKey)re(e,1);else{if(t.shiftKey)return void e.enterEditMode();Ce(e,t,1,!1)}else if("ArrowUp"===i)if(t.ctrlKey||t.metaKey)de(e);else{if(t.shiftKey)return void e.enterEditMode();Ce(e,t,-1,!0)}else if("ArrowDown"===i){if(t.shiftKey&&!t.ctrlKey&&!t.metaKey)return void e.enterEditMode();Ce(e,t,1,!0)}else{if("Enter"!==i)return void("Escape"===i?s.hide():"Backspace"!==i&&"Delete"!==i&&(1!==i.length||t.ctrlKey||t.metaKey)||e.enterEditMode());if(a)return void e.setDate(s.viewDate);s.changeView(n-1).render()}}else{if("ArrowDown"!==i)return void("Enter"===i?e.update():"Escape"===i&&s.show());s.show()}t.preventDefault()}function Ee(e){e.config.showOnFocus&&!e._showing&&e.show()}function Fe(e,t){const i=t.target;(e.picker.active||e.config.showOnClick)&&(i._active=N(i),i._clicking=setTimeout((()=>{delete i._active,delete i._clicking}),2e3))}function Ve(e,t){const i=t.target;i._clicking&&(clearTimeout(i._clicking),delete i._clicking,i._active&&e.enterEditMode(),delete i._active,e.config.showOnClick&&e.show())}function Ne(e,t){t.clipboardData.types.includes("text/plain")&&e.enterEditMode()}function Le(e,t){const{element:i,picker:s}=e;if(!s.active&&!N(i))return;const n=s.element;j(t,(e=>e===i||e===n))||oe(e)}function Be(e,t){return e.map((e=>O(e,t.format,t.locale))).join(t.dateDelimiter)}function Ye(e,t,i=!1){const{config:s,dates:a,rangeSideIndex:r}=e;if(0===t.length)return i?[]:void 0;let d=t.reduce(((e,t)=>{let i=C(t,s.format,s.locale);return void 0===i||(i=w(i,s.pickLevel,r),!n(i,s.minDate,s.maxDate)||e.includes(i)||s.datesDisabled.includes(i)||!(s.pickLevel>0)&&s.daysOfWeekDisabled.includes(new Date(i).getDay())||e.push(i)),e}),[]);return 0!==d.length?(s.multidate&&!i&&(d=d.reduce(((e,t)=>(a.includes(t)||e.push(t),e)),a.filter((e=>!d.includes(e))))),s.maxNumberOfDates&&d.length>s.maxNumberOfDates?d.slice(-1*s.maxNumberOfDates):d):void 0}function Ae(e,t=3,i=!0){const{config:s,picker:n,inputField:a}=e;if(2&t){const e=n.active?s.pickLevel:s.startView;n.update().changeView(e).render(i)}1&t&&a&&(a.value=Be(e.dates,s))}function We(e,t,i){let{clear:s,render:n,autohide:a,revert:r}=i;void 0===n&&(n=!0),n?void 0===a&&(a=e.config.autohide):a=!1;const d=Ye(e,t,s);(d||r)&&(d&&d.toString()!==e.dates.toString()?(e.dates=d,Ae(e,n?3:1),ae(e,"changeDate")):Ae(e,1),a&&e.hide())}return class{constructor(e,t={},i){e.datepicker=this,this.element=e;const n=this.config=Object.assign({buttonClass:t.buttonClass&&String(t.buttonClass)||"button",container:null,defaultViewDate:c(),maxDate:void 0,minDate:void 0},X(_,this)),a=this.inline="INPUT"!==e.tagName;let r,d;if(a?n.container=e:(t.container&&(n.container=t.container instanceof HTMLElement?t.container:document.querySelector(t.container)),r=this.inputField=e,r.classList.add("datepicker-input")),i){const e=i.inputs.indexOf(r),t=i.datepickers;if(e<0||e>1||!Array.isArray(t))throw Error("Invalid rangepicker object.");t[e]=this,Object.defineProperty(this,"rangepicker",{get:()=>i}),Object.defineProperty(this,"rangeSideIndex",{get:()=>e})}this._options=t,Object.assign(n,X(t,this)),a?(d=s(e.dataset.date,n.dateDelimiter),delete e.dataset.date):d=s(r.value,n.dateDelimiter),this.dates=[];const o=Ye(this,d);o&&o.length>0&&(this.dates=o),r&&(r.value=Be(this.dates,n));const l=this.picker=new Me(this);if(a)this.show();else{const e=Le.bind(null,this);T(this,[[r,"keydown",Oe.bind(null,this)],[r,"focus",Ee.bind(null,this)],[r,"mousedown",Fe.bind(null,this)],[r,"click",Ve.bind(null,this)],[r,"paste",Ne.bind(null,this)],[document,"mousedown",e],[document,"touchstart",e],[window,"resize",l.place.bind(l)]])}}static formatDate(e,t,i){return O(e,t,i&&$[i]||$.en)}static parseDate(e,t,i){return C(e,t,i&&$[i]||$.en)}static get locales(){return $}get active(){return!(!this.picker||!this.picker.active)}get pickerElement(){return this.picker?this.picker.element:void 0}setOptions(e){const t=this.picker,i=X(e,this);Object.assign(this._options,e),Object.assign(this.config,i),t.setOptions(i),Ae(this,3)}show(){if(this.inputField){if(this.inputField.disabled)return;N(this.inputField)||this.config.disableTouchKeyboard||(this._showing=!0,this.inputField.focus(),delete this._showing)}this.picker.show()}hide(){this.inline||(this.picker.hide(),this.picker.update().changeView(this.config.startView).render())}destroy(){return this.hide(),function(e){let t=A.get(e);t&&(t.forEach((e=>{H.call(...e)})),A.delete(e))}(this),this.picker.detach(),this.inline||this.inputField.classList.remove("datepicker-input"),delete this.element.datepicker,this}getDate(e){const t=e?t=>O(t,e,this.config.locale):e=>new Date(e);return this.config.multidate?this.dates.map(t):this.dates.length>0?t(this.dates[0]):void 0}setDate(...e){const i=[...e],s={},n=t(e);"object"!=typeof n||Array.isArray(n)||n instanceof Date||!n||Object.assign(s,i.pop());We(this,Array.isArray(i[0])?i[0]:i,s)}update(e){if(this.inline)return;const t=Object.assign(e||{},{clear:!0,render:!0});We(this,s(this.inputField.value,this.config.dateDelimiter),t)}refresh(e,t=!1){let i;e&&"string"!=typeof e&&(t=e,e=void 0),i="picker"===e?2:"input"===e?1:3,Ae(this,i,!t)}enterEditMode(){this.inline||!this.picker.active||this.editMode||(this.editMode=!0,this.inputField.classList.add("in-edit"))}exitEditMode(e){if(this.inline||!this.editMode)return;const t=Object.assign({update:!1},e);delete this.editMode,this.inputField.classList.remove("in-edit"),t.update&&this.update(t)}}}();
\ No newline at end of file
diff --git a/src/ui/static/js/datepicker/events/functions.js b/src/ui/static/js/datepicker/events/functions.js
new file mode 100644
index 000000000..c0853017a
--- /dev/null
+++ b/src/ui/static/js/datepicker/events/functions.js
@@ -0,0 +1,48 @@
+import {limitToRange} from '../lib/utils.js';
+import {addMonths, addYears} from '../lib/date.js';
+
+export function triggerDatepickerEvent(datepicker, type) {
+ const detail = {
+ date: datepicker.getDate(),
+ viewDate: new Date(datepicker.picker.viewDate),
+ viewId: datepicker.picker.currentView.id,
+ datepicker,
+ };
+ datepicker.element.dispatchEvent(new CustomEvent(type, {detail}));
+}
+
+// direction: -1 (to previous), 1 (to next)
+export function goToPrevOrNext(datepicker, direction) {
+ const {minDate, maxDate} = datepicker.config;
+ const {currentView, viewDate} = datepicker.picker;
+ let newViewDate;
+ switch (currentView.id) {
+ case 0:
+ newViewDate = addMonths(viewDate, direction);
+ break;
+ case 1:
+ newViewDate = addYears(viewDate, direction);
+ break;
+ default:
+ newViewDate = addYears(viewDate, direction * currentView.navStep);
+ }
+ newViewDate = limitToRange(newViewDate, minDate, maxDate);
+ datepicker.picker.changeFocus(newViewDate).render();
+}
+
+export function switchView(datepicker) {
+ const viewId = datepicker.picker.currentView.id;
+ if (viewId === datepicker.config.maxView) {
+ return;
+ }
+ datepicker.picker.changeView(viewId + 1).render();
+}
+
+export function unfocus(datepicker) {
+ if (datepicker.config.updateOnBlur) {
+ datepicker.update({revert: true});
+ } else {
+ datepicker.refresh('input');
+ }
+ datepicker.hide();
+}
diff --git a/src/ui/static/js/datepicker/events/inputFieldListeners.js b/src/ui/static/js/datepicker/events/inputFieldListeners.js
new file mode 100644
index 000000000..acc9cc2a9
--- /dev/null
+++ b/src/ui/static/js/datepicker/events/inputFieldListeners.js
@@ -0,0 +1,195 @@
+import {isInRange} from '../lib/utils.js';
+import {isActiveElement} from '../lib/dom.js';
+import {addDays, addMonths, addYears, startOfYearPeriod} from '../lib/date.js';
+import {goToPrevOrNext, switchView, unfocus} from './functions.js';
+
+// Find the closest date that doesn't meet the condition for unavailable date
+// Returns undefined if no available date is found
+// addFn: function to calculate the next date
+// - args: time value, amount
+// increase: amount to pass to addFn
+// testFn: function to test the unavailablity of the date
+// - args: time value; retun: true if unavailable
+function findNextAvailableOne(date, addFn, increase, testFn, min, max) {
+ if (!isInRange(date, min, max)) {
+ return;
+ }
+ if (testFn(date)) {
+ const newDate = addFn(date, increase);
+ return findNextAvailableOne(newDate, addFn, increase, testFn, min, max);
+ }
+ return date;
+}
+
+// direction: -1 (left/up), 1 (right/down)
+// vertical: true for up/down, false for left/right
+function moveByArrowKey(datepicker, ev, direction, vertical) {
+ const picker = datepicker.picker;
+ const currentView = picker.currentView;
+ const step = currentView.step || 1;
+ let viewDate = picker.viewDate;
+ let addFn;
+ let testFn;
+ switch (currentView.id) {
+ case 0:
+ if (vertical) {
+ viewDate = addDays(viewDate, direction * 7);
+ } else if (ev.ctrlKey || ev.metaKey) {
+ viewDate = addYears(viewDate, direction);
+ } else {
+ viewDate = addDays(viewDate, direction);
+ }
+ addFn = addDays;
+ testFn = (date) => currentView.disabled.includes(date);
+ break;
+ case 1:
+ viewDate = addMonths(viewDate, vertical ? direction * 4 : direction);
+ addFn = addMonths;
+ testFn = (date) => {
+ const dt = new Date(date);
+ const {year, disabled} = currentView;
+ return dt.getFullYear() === year && disabled.includes(dt.getMonth());
+ };
+ break;
+ default:
+ viewDate = addYears(viewDate, direction * (vertical ? 4 : 1) * step);
+ addFn = addYears;
+ testFn = date => currentView.disabled.includes(startOfYearPeriod(date, step));
+ }
+ viewDate = findNextAvailableOne(
+ viewDate,
+ addFn,
+ direction < 0 ? -step : step,
+ testFn,
+ currentView.minDate,
+ currentView.maxDate
+ );
+ if (viewDate !== undefined) {
+ picker.changeFocus(viewDate).render();
+ }
+}
+
+export function onKeydown(datepicker, ev) {
+ const key = ev.key;
+ if (key === 'Tab') {
+ unfocus(datepicker);
+ return;
+ }
+
+ const picker = datepicker.picker;
+ const {id, isMinView} = picker.currentView;
+ if (!picker.active) {
+ if (key === 'ArrowDown') {
+ picker.show();
+ } else {
+ if (key === 'Enter') {
+ datepicker.update();
+ } else if (key === 'Escape') {
+ picker.show();
+ }
+ return;
+ }
+ } else if (datepicker.editMode) {
+ if (key === 'Enter') {
+ datepicker.exitEditMode({update: true, autohide: datepicker.config.autohide});
+ } else if (key === 'Escape') {
+ picker.hide();
+ }
+ return;
+ } else {
+ if (key === 'ArrowLeft') {
+ if (ev.ctrlKey || ev.metaKey) {
+ goToPrevOrNext(datepicker, -1);
+ } else if (ev.shiftKey) {
+ datepicker.enterEditMode();
+ return;
+ } else {
+ moveByArrowKey(datepicker, ev, -1, false);
+ }
+ } else if (key === 'ArrowRight') {
+ if (ev.ctrlKey || ev.metaKey) {
+ goToPrevOrNext(datepicker, 1);
+ } else if (ev.shiftKey) {
+ datepicker.enterEditMode();
+ return;
+ } else {
+ moveByArrowKey(datepicker, ev, 1, false);
+ }
+ } else if (key === 'ArrowUp') {
+ if (ev.ctrlKey || ev.metaKey) {
+ switchView(datepicker);
+ } else if (ev.shiftKey) {
+ datepicker.enterEditMode();
+ return;
+ } else {
+ moveByArrowKey(datepicker, ev, -1, true);
+ }
+ } else if (key === 'ArrowDown') {
+ if (ev.shiftKey && !ev.ctrlKey && !ev.metaKey) {
+ datepicker.enterEditMode();
+ return;
+ }
+ moveByArrowKey(datepicker, ev, 1, true);
+ } else if (key === 'Enter') {
+ if (isMinView) {
+ datepicker.setDate(picker.viewDate);
+ return;
+ }
+ picker.changeView(id - 1).render();
+ } else {
+ if (key === 'Escape') {
+ picker.hide();
+ } else if (
+ key === 'Backspace'
+ || key === 'Delete'
+ || (key.length === 1 && !ev.ctrlKey && !ev.metaKey)
+ ) {
+ datepicker.enterEditMode();
+ }
+ return;
+ }
+ }
+ ev.preventDefault();
+}
+
+export function onFocus(datepicker) {
+ if (datepicker.config.showOnFocus && !datepicker._showing) {
+ datepicker.show();
+ }
+}
+
+// for the prevention for entering edit mode while getting focus on click
+export function onMousedown(datepicker, ev) {
+ const el = ev.target;
+ if (datepicker.picker.active || datepicker.config.showOnClick) {
+ el._active = isActiveElement(el);
+ el._clicking = setTimeout(() => {
+ delete el._active;
+ delete el._clicking;
+ }, 2000);
+ }
+}
+
+export function onClickInput(datepicker, ev) {
+ const el = ev.target;
+ if (!el._clicking) {
+ return;
+ }
+ clearTimeout(el._clicking);
+ delete el._clicking;
+
+ if (el._active) {
+ datepicker.enterEditMode();
+ }
+ delete el._active;
+
+ if (datepicker.config.showOnClick) {
+ datepicker.show();
+ }
+}
+
+export function onPaste(datepicker, ev) {
+ if (ev.clipboardData.types.includes('text/plain')) {
+ datepicker.enterEditMode();
+ }
+}
diff --git a/src/ui/static/js/datepicker/events/otherListeners.js b/src/ui/static/js/datepicker/events/otherListeners.js
new file mode 100644
index 000000000..d25b5daa3
--- /dev/null
+++ b/src/ui/static/js/datepicker/events/otherListeners.js
@@ -0,0 +1,20 @@
+import {isActiveElement} from '../lib/dom.js';
+import {findElementInEventPath} from '../lib/event.js';
+import {unfocus} from './functions.js';
+
+// for the `document` to delegate the events from outside the picker/input field
+export function onClickOutside(datepicker, ev) {
+ const {element, picker} = datepicker;
+ // check both picker's and input's activeness to make updateOnBlur work in
+ // the cases where...
+ // - picker is hidden by ESC key press → input stays focused
+ // - input is unfocused by closing mobile keyboard → piker is kept shown
+ if (!picker.active && !isActiveElement(element)) {
+ return;
+ }
+ const pickerElem = picker.element;
+ if (findElementInEventPath(ev, el => el === element || el === pickerElem)) {
+ return;
+ }
+ unfocus(datepicker);
+}
diff --git a/src/ui/static/js/datepicker/events/pickerListeners.js b/src/ui/static/js/datepicker/events/pickerListeners.js
new file mode 100644
index 000000000..3deff58c1
--- /dev/null
+++ b/src/ui/static/js/datepicker/events/pickerListeners.js
@@ -0,0 +1,68 @@
+import {today, addMonths, addYears} from '../lib/date.js';
+import {findElementInEventPath} from '../lib/event.js';
+import {goToPrevOrNext, switchView} from './functions.js';
+
+function goToSelectedMonthOrYear(datepicker, selection) {
+ const picker = datepicker.picker;
+ const viewDate = new Date(picker.viewDate);
+ const viewId = picker.currentView.id;
+ const newDate = viewId === 1
+ ? addMonths(viewDate, selection - viewDate.getMonth())
+ : addYears(viewDate, selection - viewDate.getFullYear());
+
+ picker.changeFocus(newDate).changeView(viewId - 1).render();
+}
+
+export function onClickTodayBtn(datepicker) {
+ const picker = datepicker.picker;
+ const currentDate = today();
+ if (datepicker.config.todayBtnMode === 1) {
+ if (datepicker.config.autohide) {
+ datepicker.setDate(currentDate);
+ return;
+ }
+ datepicker.setDate(currentDate, {render: false});
+ picker.update();
+ }
+ if (picker.viewDate !== currentDate) {
+ picker.changeFocus(currentDate);
+ }
+ picker.changeView(0).render();
+}
+
+export function onClickClearBtn(datepicker) {
+ datepicker.setDate({clear: true});
+}
+
+export function onClickViewSwitch(datepicker) {
+ switchView(datepicker);
+}
+
+export function onClickPrevBtn(datepicker) {
+ goToPrevOrNext(datepicker, -1);
+}
+
+export function onClickNextBtn(datepicker) {
+ goToPrevOrNext(datepicker, 1);
+}
+
+// For the picker's main block to delegete the events from `datepicker-cell`s
+export function onClickView(datepicker, ev) {
+ const target = findElementInEventPath(ev, '.datepicker-cell');
+ if (!target || target.classList.contains('disabled')) {
+ return;
+ }
+
+ const {id, isMinView} = datepicker.picker.currentView;
+ if (isMinView) {
+ datepicker.setDate(Number(target.dataset.date));
+ } else if (id === 1) {
+ goToSelectedMonthOrYear(datepicker, Number(target.dataset.month));
+ } else {
+ goToSelectedMonthOrYear(datepicker, Number(target.dataset.year));
+ }
+}
+
+export function onMousedownPicker(ev) {
+ ev.preventDefault();
+}
diff --git a/src/ui/static/js/datepicker/i18n/base-locales.js b/src/ui/static/js/datepicker/i18n/base-locales.js
new file mode 100644
index 000000000..96bd963d7
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/base-locales.js
@@ -0,0 +1,13 @@
+// default locales
+export const locales = {
+ en: {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today",
+ clear: "Clear",
+ titleFormat: "MM y"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/ar-DZ.js b/src/ui/static/js/datepicker/i18n/locales/ar-DZ.js
new file mode 100644
index 000000000..0a9269d91
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/ar-DZ.js
@@ -0,0 +1,19 @@
+/**
+ * Arabic-Algeria translation for bootstrap-datepicker
+ * Rabah Saadi
+ */
+export default {
+ 'ar-DZ': {
+ days: ["الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت", "الأحد"],
+ daysShort: ["أحد", "اثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت", "أحد"],
+ daysMin: ["ح", "ن", "ث", "ع", "خ", "ج", "س", "ح"],
+ months: ["جانفي","فيفري","مارس","أفريل","ماي","جوان","جويليه","أوت","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],
+ monthsShort: ["جانفي","فيفري","مارس","أفريل","ماي","جوان","جويليه","أوت","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],
+ today: "هذا اليوم",
+ rtl: true,
+ monthsTitle: "أشهر",
+ clear: "إزالة",
+ format: "yyyy/mm/dd",
+ weekStart: 0
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/ar-tn.js b/src/ui/static/js/datepicker/i18n/locales/ar-tn.js
new file mode 100644
index 000000000..5a3b6af8e
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/ar-tn.js
@@ -0,0 +1,15 @@
+/**
+ * Arabic-Tunisia translation for bootstrap-datepicker
+ * Souhaieb Besbes
+ */
+export default {
+ 'ar-tn': {
+ days: ["الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت", "الأحد"],
+ daysShort: ["أحد", "اثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت", "أحد"],
+ daysMin: ["ح", "ن", "ث", "ع", "خ", "ج", "س", "ح"],
+ months: ["جانفي","فيفري","مارس","أفريل","ماي","جوان","جويليه","أوت","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],
+ monthsShort: ["جانفي","فيفري","مارس","أفريل","ماي","جوان","جويليه","أوت","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],
+ today: "هذا اليوم",
+ rtl: true
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/ar.js b/src/ui/static/js/datepicker/i18n/locales/ar.js
new file mode 100644
index 000000000..3ee311fe2
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/ar.js
@@ -0,0 +1,15 @@
+/**
+ * Arabic translation for bootstrap-datepicker
+ * Mohammed Alshehri
+ */
+export default {
+ ar: {
+ days: ["الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت", "الأحد"],
+ daysShort: ["أحد", "اثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت", "أحد"],
+ daysMin: ["ح", "ن", "ث", "ع", "خ", "ج", "س", "ح"],
+ months: ["يناير", "فبراير", "مارس", "أبريل", "مايو", "يونيو", "يوليو", "أغسطس", "سبتمبر", "أكتوبر", "نوفمبر", "ديسمبر"],
+ monthsShort: ["يناير", "فبراير", "مارس", "أبريل", "مايو", "يونيو", "يوليو", "أغسطس", "سبتمبر", "أكتوبر", "نوفمبر", "ديسمبر"],
+ today: "هذا اليوم",
+ rtl: true
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/az.js b/src/ui/static/js/datepicker/i18n/locales/az.js
new file mode 100644
index 000000000..52c38bb8d
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/az.js
@@ -0,0 +1,14 @@
+// Azerbaijani
+export default {
+ az: {
+ days: ["Bazar", "Bazar ertəsi", "Çərşənbə axşamı", "Çərşənbə", "Cümə axşamı", "Cümə", "Şənbə"],
+ daysShort: ["B.", "B.e", "Ç.a", "Ç.", "C.a", "C.", "Ş."],
+ daysMin: ["B.", "B.e", "Ç.a", "Ç.", "C.a", "C.", "Ş."],
+ months: ["Yanvar", "Fevral", "Mart", "Aprel", "May", "İyun", "İyul", "Avqust", "Sentyabr", "Oktyabr", "Noyabr", "Dekabr"],
+ monthsShort: ["Yan", "Fev", "Mar", "Apr", "May", "İyun", "İyul", "Avq", "Sen", "Okt", "Noy", "Dek"],
+ today: "Bu gün",
+ weekStart: 1,
+ clear: "Təmizlə",
+ monthsTitle: 'Aylar'
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/bg.js b/src/ui/static/js/datepicker/i18n/locales/bg.js
new file mode 100644
index 000000000..a667db287
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/bg.js
@@ -0,0 +1,14 @@
+/**
+ * Bulgarian translation for bootstrap-datepicker
+ * Apostol Apostolov
+ */
+export default {
+ bg: {
+ days: ["Неделя", "Понеделник", "Вторник", "Сряда", "Четвъртък", "Петък", "Събота"],
+ daysShort: ["Нед", "Пон", "Вто", "Сря", "Чет", "Пет", "Съб"],
+ daysMin: ["Н", "П", "В", "С", "Ч", "П", "С"],
+ months: ["Януари", "Февруари", "Март", "Април", "Май", "Юни", "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември"],
+ monthsShort: ["Ян", "Фев", "Мар", "Апр", "Май", "Юни", "Юли", "Авг", "Сеп", "Окт", "Ное", "Дек"],
+ today: "днес"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/bm.js b/src/ui/static/js/datepicker/i18n/locales/bm.js
new file mode 100644
index 000000000..92bcdbbc5
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/bm.js
@@ -0,0 +1,18 @@
+/**
+ * Bamanankan (bm) translation for bootstrap-datepicker
+ * Fatou Fall
+ */
+export default {
+ bm: {
+ days: ["Kari","Ntɛnɛn","Tarata","Araba","Alamisa","Juma","Sibiri"],
+ daysShort: ["Kar","Ntɛ","Tar","Ara","Ala","Jum","Sib"],
+ daysMin: ["Ka","Nt","Ta","Ar","Al","Ju","Si"],
+ months: ["Zanwuyekalo","Fewuruyekalo","Marisikalo","Awirilikalo","Mɛkalo","Zuwɛnkalo","Zuluyekalo","Utikalo","Sɛtanburukalo","ɔkutɔburukalo","Nowanburukalo","Desanburukalo"],
+ monthsShort: ["Zan","Few","Mar","Awi","Mɛ","Zuw","Zul","Uti","Sɛt","ɔku","Now","Des"],
+ today: "Bi",
+ monthsTitle: "Kalo",
+ clear: "Ka jɔsi",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/bn.js b/src/ui/static/js/datepicker/i18n/locales/bn.js
new file mode 100644
index 000000000..94aca096d
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/bn.js
@@ -0,0 +1,19 @@
+/**
+ * Bengali (Bangla) translation for bootstrap-datepicker
+ * Karim Khan
+ * Orif N. Jr.
+ */
+export default {
+ bn: {
+ days: ["রবিবার","সোমবার","মঙ্গলবার","বুধবার","বৃহস্পতিবার","শুক্রবার","শনিবার"],
+ daysShort: ["রবিবার","সোমবার","মঙ্গলবার","বুধবার","বৃহস্পতিবার","শুক্রবার","শনিবার"],
+ daysMin: ["রবি","সোম","মঙ্গল","বুধ","বৃহস্পতি","শুক্র","শনি"],
+ months: ["জানুয়ারী","ফেব্রুয়ারি","মার্চ","এপ্রিল","মে","জুন","জুলাই","অগাস্ট","সেপ্টেম্বর","অক্টোবর","নভেম্বর","ডিসেম্বর"],
+ monthsShort: ["জানুয়ারী","ফেব্রুয়ারি","মার্চ","এপ্রিল","মে","জুন","জুলাই","অগাস্ট","সেপ্টেম্বর","অক্টোবর","নভেম্বর","ডিসেম্বর"],
+ today: "আজ",
+ monthsTitle: "মাস",
+ clear: "পরিষ্কার",
+ weekStart: 0,
+ format: "mm/dd/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/br.js b/src/ui/static/js/datepicker/i18n/locales/br.js
new file mode 100644
index 000000000..39ca7a7ea
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/br.js
@@ -0,0 +1,18 @@
+/**
+ * Breton translation for bootstrap-datepicker
+ * Gwenn Meynier
+ */
+export default {
+ br: {
+ days: ["Sul", "Lun", "Meurzh", "Merc'her", "Yaou", "Gwener", "Sadorn"],
+ daysShort: ["Sul", "Lun", "Meu.", "Mer.", "Yao.", "Gwe.", "Sad."],
+ daysMin: ["Su", "L", "Meu", "Mer", "Y", "G", "Sa"],
+ months: ["Genver", "C'hwevrer", "Meurzh", "Ebrel", "Mae", "Mezheven", "Gouere", "Eost", "Gwengolo", "Here", "Du", "Kerzu"],
+ monthsShort: ["Genv.", "C'hw.", "Meur.", "Ebre.", "Mae", "Mezh.", "Goue.", "Eost", "Gwen.", "Here", "Du", "Kerz."],
+ today: "Hiziv",
+ monthsTitle: "Miz",
+ clear: "Dilemel",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/bs.js b/src/ui/static/js/datepicker/i18n/locales/bs.js
new file mode 100644
index 000000000..47b7fa49c
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/bs.js
@@ -0,0 +1,15 @@
+/**
+ * Bosnian translation for bootstrap-datepicker
+ */
+export default {
+ bs: {
+ days: ["Nedjelja","Ponedjeljak", "Utorak", "Srijeda", "Četvrtak", "Petak", "Subota"],
+ daysShort: ["Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub"],
+ daysMin: ["N", "Po", "U", "Sr", "Č", "Pe", "Su"],
+ months: ["Januar", "Februar", "Mart", "April", "Maj", "Juni", "Juli", "August", "Septembar", "Oktobar", "Novembar", "Decembar"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"],
+ today: "Danas",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/ca.js b/src/ui/static/js/datepicker/i18n/locales/ca.js
new file mode 100644
index 000000000..9260b6071
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/ca.js
@@ -0,0 +1,18 @@
+/**
+ * Catalan translation for bootstrap-datepicker
+ * J. Garcia
+ */
+export default {
+ ca: {
+ days: ["diumenge", "dilluns", "dimarts", "dimecres", "dijous", "divendres", "dissabte"],
+ daysShort: ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+ daysMin: ["dg", "dl", "dt", "dc", "dj", "dv", "ds"],
+ months: ["gener", "febrer", "març", "abril", "maig", "juny", "juliol", "agost", "setembre", "octubre", "novembre", "desembre"],
+ monthsShort: ["gen.", "febr.", "març", "abr.", "maig", "juny", "jul.", "ag.", "set.", "oct.", "nov.", "des."],
+ today: "Avui",
+ monthsTitle: "Mesos",
+ clear: "Esborra",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/cs.js b/src/ui/static/js/datepicker/i18n/locales/cs.js
new file mode 100644
index 000000000..ac3e584af
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/cs.js
@@ -0,0 +1,19 @@
+/**
+ * Czech translation for bootstrap-datepicker
+ * Matěj Koubík
+ * Fixes by Michal Remiš
+ */
+export default {
+ cs: {
+ days: ["Neděle", "Pondělí", "Úterý", "Středa", "Čtvrtek", "Pátek", "Sobota"],
+ daysShort: ["Ned", "Pon", "Úte", "Stř", "Čtv", "Pát", "Sob"],
+ daysMin: ["Ne", "Po", "Út", "St", "Čt", "Pá", "So"],
+ months: ["Leden", "Únor", "Březen", "Duben", "Květen", "Červen", "Červenec", "Srpen", "Září", "Říjen", "Listopad", "Prosinec"],
+ monthsShort: ["Led", "Úno", "Bře", "Dub", "Kvě", "Čer", "Čnc", "Srp", "Zář", "Říj", "Lis", "Pro"],
+ today: "Dnes",
+ clear: "Vymazat",
+ monthsTitle: "Měsíc",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/cy.js b/src/ui/static/js/datepicker/i18n/locales/cy.js
new file mode 100644
index 000000000..0efd1a695
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/cy.js
@@ -0,0 +1,14 @@
+/**
+ * Welsh translation for bootstrap-datepicker
+ * S. Morris
+ */
+export default {
+ cy: {
+ days: ["Sul", "Llun", "Mawrth", "Mercher", "Iau", "Gwener", "Sadwrn"],
+ daysShort: ["Sul", "Llu", "Maw", "Mer", "Iau", "Gwe", "Sad"],
+ daysMin: ["Su", "Ll", "Ma", "Me", "Ia", "Gwe", "Sa"],
+ months: ["Ionawr", "Chewfror", "Mawrth", "Ebrill", "Mai", "Mehefin", "Gorfennaf", "Awst", "Medi", "Hydref", "Tachwedd", "Rhagfyr"],
+ monthsShort: ["Ion", "Chw", "Maw", "Ebr", "Mai", "Meh", "Gor", "Aws", "Med", "Hyd", "Tach", "Rha"],
+ today: "Heddiw"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/da.js b/src/ui/static/js/datepicker/i18n/locales/da.js
new file mode 100644
index 000000000..5bbdbec1e
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/da.js
@@ -0,0 +1,19 @@
+/**
+ * Danish translation for bootstrap-datepicker
+ * Christian Pedersen
+ * Ivan Mylyanyk
+ */
+export default {
+ da: {
+ days: ["Søndag", "Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "Lørdag"],
+ daysShort: ["Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør"],
+ daysMin: ["Sø", "Ma", "Ti", "On", "To", "Fr", "Lø"],
+ months: ["Januar", "Februar", "Marts", "April", "Maj", "Juni", "Juli", "August", "September", "Oktober", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"],
+ today: "I Dag",
+ weekStart: 1,
+ clear: "Nulstil",
+ format: "dd/mm/yyyy",
+ monthsTitle: "Måneder"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/de.js b/src/ui/static/js/datepicker/i18n/locales/de.js
new file mode 100644
index 000000000..bd0f5dd4a
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/de.js
@@ -0,0 +1,18 @@
+/**
+ * German translation for bootstrap-datepicker
+ * Sam Zurcher
+ */
+export default {
+ de: {
+ days: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"],
+ daysShort: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
+ daysMin: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
+ months: ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"],
+ monthsShort: ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"],
+ today: "Heute",
+ monthsTitle: "Monate",
+ clear: "Löschen",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/el.js b/src/ui/static/js/datepicker/i18n/locales/el.js
new file mode 100644
index 000000000..4a3f2df3b
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/el.js
@@ -0,0 +1,16 @@
+/**
+ * Greek translation for bootstrap-datepicker
+ */
+export default {
+ el: {
+ days: ["Κυριακή", "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο"],
+ daysShort: ["Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ"],
+ daysMin: ["Κυ", "Δε", "Τρ", "Τε", "Πε", "Πα", "Σα"],
+ months: ["Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος"],
+ monthsShort: ["Ιαν", "Φεβ", "Μαρ", "Απρ", "Μάι", "Ιουν", "Ιουλ", "Αυγ", "Σεπ", "Οκτ", "Νοε", "Δεκ"],
+ today: "Σήμερα",
+ clear: "Καθαρισμός",
+ weekStart: 1,
+ format: "d/m/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/en-AU.js b/src/ui/static/js/datepicker/i18n/locales/en-AU.js
new file mode 100644
index 000000000..cddae4086
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/en-AU.js
@@ -0,0 +1,18 @@
+/**
+ * Australian English translation for bootstrap-datepicker
+ * Steve Chapman
+ */
+export default {
+ 'en-AU': {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today",
+ monthsTitle: "Months",
+ clear: "Clear",
+ weekStart: 1,
+ format: "d/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/en-CA.js b/src/ui/static/js/datepicker/i18n/locales/en-CA.js
new file mode 100644
index 000000000..aeef9b690
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/en-CA.js
@@ -0,0 +1,18 @@
+/**
+ * Canadian English translation for bootstrap-datepicker
+ * Mike Nacey
+ */
+export default {
+ 'en-CA': {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today",
+ monthsTitle: "Months",
+ clear: "Clear",
+ weekStart: 0,
+ format: "yyyy-mm-dd"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/en-GB.js b/src/ui/static/js/datepicker/i18n/locales/en-GB.js
new file mode 100644
index 000000000..fa865ad71
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/en-GB.js
@@ -0,0 +1,18 @@
+/**
+ * British English translation for bootstrap-datepicker
+ * Xavier Dutreilh
+ */
+export default {
+ 'en-GB': {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today",
+ monthsTitle: "Months",
+ clear: "Clear",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/en-IE.js b/src/ui/static/js/datepicker/i18n/locales/en-IE.js
new file mode 100644
index 000000000..c9988bd48
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/en-IE.js
@@ -0,0 +1,17 @@
+/**
+ * Irish English translation for bootstrap-datepicker
+ */
+export default {
+ 'en-IE': {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today",
+ monthsTitle: "Months",
+ clear: "Clear",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/en-NZ.js b/src/ui/static/js/datepicker/i18n/locales/en-NZ.js
new file mode 100644
index 000000000..bad9a0766
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/en-NZ.js
@@ -0,0 +1,17 @@
+/**
+ * New Zealand English translation for bootstrap-datepicker
+ */
+export default {
+ 'en-NZ': {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today",
+ monthsTitle: "Months",
+ clear: "Clear",
+ weekStart: 1,
+ format: "d/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/en-ZA.js b/src/ui/static/js/datepicker/i18n/locales/en-ZA.js
new file mode 100644
index 000000000..e44e4693d
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/en-ZA.js
@@ -0,0 +1,17 @@
+/**
+ * South African English translation for bootstrap-datepicker
+ */
+export default {
+ 'en-ZA': {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today",
+ monthsTitle: "Months",
+ clear: "Clear",
+ weekStart: 1,
+ format: "yyyy/mm/d"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/eo.js b/src/ui/static/js/datepicker/i18n/locales/eo.js
new file mode 100644
index 000000000..a53430013
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/eo.js
@@ -0,0 +1,17 @@
+/**
+ * Esperanto translation for bootstrap-datepicker
+ * Emmanuel Debanne
+ */
+export default {
+ eo: {
+ days: ["dimanĉo", "lundo", "mardo", "merkredo", "ĵaŭdo", "vendredo", "sabato"],
+ daysShort: ["dim.", "lun.", "mar.", "mer.", "ĵaŭ.", "ven.", "sam."],
+ daysMin: ["d", "l", "ma", "me", "ĵ", "v", "s"],
+ months: ["januaro", "februaro", "marto", "aprilo", "majo", "junio", "julio", "aŭgusto", "septembro", "oktobro", "novembro", "decembro"],
+ monthsShort: ["jan.", "feb.", "mar.", "apr.", "majo", "jun.", "jul.", "aŭg.", "sep.", "okt.", "nov.", "dec."],
+ today: "Hodiaŭ",
+ clear: "Nuligi",
+ weekStart: 1,
+ format: "yyyy-mm-dd"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/es.js b/src/ui/static/js/datepicker/i18n/locales/es.js
new file mode 100644
index 000000000..201f52f65
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/es.js
@@ -0,0 +1,18 @@
+/**
+ * Spanish translation for bootstrap-datepicker
+ * Bruno Bonamin
+ */
+export default {
+ es: {
+ days: ["Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"],
+ daysShort: ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb"],
+ daysMin: ["Do", "Lu", "Ma", "Mi", "Ju", "Vi", "Sa"],
+ months: ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"],
+ monthsShort: ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"],
+ today: "Hoy",
+ monthsTitle: "Meses",
+ clear: "Borrar",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/et.js b/src/ui/static/js/datepicker/i18n/locales/et.js
new file mode 100644
index 000000000..183878b1c
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/et.js
@@ -0,0 +1,18 @@
+/**
+ * Estonian translation for bootstrap-datepicker
+ * Ando Roots
+ * Fixes by Illimar Tambek <
+ */
+export default {
+ et: {
+ days: ["Pühapäev", "Esmaspäev", "Teisipäev", "Kolmapäev", "Neljapäev", "Reede", "Laupäev"],
+ daysShort: ["Pühap", "Esmasp", "Teisip", "Kolmap", "Neljap", "Reede", "Laup"],
+ daysMin: ["P", "E", "T", "K", "N", "R", "L"],
+ months: ["Jaanuar", "Veebruar", "Märts", "Aprill", "Mai", "Juuni", "Juuli", "August", "September", "Oktoober", "November", "Detsember"],
+ monthsShort: ["Jaan", "Veebr", "Märts", "Apr", "Mai", "Juuni", "Juuli", "Aug", "Sept", "Okt", "Nov", "Dets"],
+ today: "Täna",
+ clear: "Tühjenda",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/eu.js b/src/ui/static/js/datepicker/i18n/locales/eu.js
new file mode 100644
index 000000000..1f3c2c30e
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/eu.js
@@ -0,0 +1,18 @@
+/**
+ * Basque translation for bootstrap-datepicker
+ * Arkaitz Etxeberria
+ */
+export default {
+ eu: {
+ days: ['Igandea', 'Astelehena', 'Asteartea', 'Asteazkena', 'Osteguna', 'Ostirala', 'Larunbata'],
+ daysShort: ['Ig', 'Al', 'Ar', 'Az', 'Og', 'Ol', 'Lr'],
+ daysMin: ['Ig', 'Al', 'Ar', 'Az', 'Og', 'Ol', 'Lr'],
+ months: ['Urtarrila', 'Otsaila', 'Martxoa', 'Apirila', 'Maiatza', 'Ekaina', 'Uztaila', 'Abuztua', 'Iraila', 'Urria', 'Azaroa', 'Abendua'],
+ monthsShort: ['Urt', 'Ots', 'Mar', 'Api', 'Mai', 'Eka', 'Uzt', 'Abu', 'Ira', 'Urr', 'Aza', 'Abe'],
+ today: "Gaur",
+ monthsTitle: "Hilabeteak",
+ clear: "Ezabatu",
+ weekStart: 1,
+ format: "yyyy/mm/dd"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/fa.js b/src/ui/static/js/datepicker/i18n/locales/fa.js
new file mode 100644
index 000000000..dece24ed0
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/fa.js
@@ -0,0 +1,17 @@
+/**
+ * Persian translation for bootstrap-datepicker
+ * Mostafa Rokooie
+ */
+export default {
+ fa: {
+ days: ["یکشنبه", "دوشنبه", "سهشنبه", "چهارشنبه", "پنجشنبه", "جمعه", "شنبه", "یکشنبه"],
+ daysShort: ["یک", "دو", "سه", "چهار", "پنج", "جمعه", "شنبه", "یک"],
+ daysMin: ["ی", "د", "س", "چ", "پ", "ج", "ش", "ی"],
+ months: ["ژانویه", "فوریه", "مارس", "آوریل", "مه", "ژوئن", "ژوئیه", "اوت", "سپتامبر", "اکتبر", "نوامبر", "دسامبر"],
+ monthsShort: ["ژان", "فور", "مار", "آور", "مه", "ژون", "ژوی", "اوت", "سپت", "اکت", "نوا", "دسا"],
+ today: "امروز",
+ clear: "پاک کن",
+ weekStart: 1,
+ format: "yyyy/mm/dd"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/fi.js b/src/ui/static/js/datepicker/i18n/locales/fi.js
new file mode 100644
index 000000000..d9fde77fb
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/fi.js
@@ -0,0 +1,17 @@
+/**
+ * Finnish translation for bootstrap-datepicker
+ * Jaakko Salonen
+ */
+export default {
+ fi: {
+ days: ["sunnuntai", "maanantai", "tiistai", "keskiviikko", "torstai", "perjantai", "lauantai"],
+ daysShort: ["sun", "maa", "tii", "kes", "tor", "per", "lau"],
+ daysMin: ["su", "ma", "ti", "ke", "to", "pe", "la"],
+ months: ["tammikuu", "helmikuu", "maaliskuu", "huhtikuu", "toukokuu", "kesäkuu", "heinäkuu", "elokuu", "syyskuu", "lokakuu", "marraskuu", "joulukuu"],
+ monthsShort: ["tammi", "helmi", "maalis", "huhti", "touko", "kesä", "heinä", "elo", "syys", "loka", "marras", "joulu"],
+ today: "tänään",
+ clear: "Tyhjennä",
+ weekStart: 1,
+ format: "d.m.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/fo.js b/src/ui/static/js/datepicker/i18n/locales/fo.js
new file mode 100644
index 000000000..908ccfcc8
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/fo.js
@@ -0,0 +1,15 @@
+/**
+ * Faroese translation for bootstrap-datepicker
+ * Theodor Johannesen
+ */
+export default {
+ fo: {
+ days: ["Sunnudagur", "Mánadagur", "Týsdagur", "Mikudagur", "Hósdagur", "Fríggjadagur", "Leygardagur"],
+ daysShort: ["Sun", "Mán", "Týs", "Mik", "Hós", "Frí", "Ley"],
+ daysMin: ["Su", "Má", "Tý", "Mi", "Hó", "Fr", "Le"],
+ months: ["Januar", "Februar", "Marts", "Apríl", "Mei", "Juni", "Juli", "August", "Septembur", "Oktobur", "Novembur", "Desembur"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Des"],
+ today: "Í Dag",
+ clear: "Reinsa"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/fr-CH.js b/src/ui/static/js/datepicker/i18n/locales/fr-CH.js
new file mode 100644
index 000000000..6d90d69bd
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/fr-CH.js
@@ -0,0 +1,21 @@
+/**
+ * French (Switzerland) translation for bootstrap-datepicker
+ * Christoph Jossi
+ * Based on
+ * French translation for bootstrap-datepicker
+ * Nico Mollet
+ */
+export default {
+ 'fr-CH': {
+ days: ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"],
+ daysShort: ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"],
+ daysMin: ["D", "L", "Ma", "Me", "J", "V", "S"],
+ months: ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"],
+ monthsShort: ["Jan", "Fév", "Mar", "Avr", "Mai", "Jui", "Jul", "Aou", "Sep", "Oct", "Nov", "Déc"],
+ today: "Aujourd'hui",
+ monthsTitle: "Mois",
+ clear: "Effacer",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/fr.js b/src/ui/static/js/datepicker/i18n/locales/fr.js
new file mode 100644
index 000000000..cac7cc355
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/fr.js
@@ -0,0 +1,18 @@
+/**
+ * French translation for bootstrap-datepicker
+ * Nico Mollet
+ */
+export default {
+ fr: {
+ days: ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"],
+ daysShort: ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+ daysMin: ["d", "l", "ma", "me", "j", "v", "s"],
+ months: ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"],
+ monthsShort: ["janv.", "févr.", "mars", "avril", "mai", "juin", "juil.", "août", "sept.", "oct.", "nov.", "déc."],
+ today: "Aujourd'hui",
+ monthsTitle: "Mois",
+ clear: "Effacer",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/gl.js b/src/ui/static/js/datepicker/i18n/locales/gl.js
new file mode 100644
index 000000000..86581e78d
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/gl.js
@@ -0,0 +1,16 @@
+/**
+ * Galician translation
+ */
+export default {
+ gl: {
+ days: ["Domingo", "Luns", "Martes", "Mércores", "Xoves", "Venres", "Sábado"],
+ daysShort: ["Dom", "Lun", "Mar", "Mér", "Xov", "Ven", "Sáb"],
+ daysMin: ["Do", "Lu", "Ma", "Me", "Xo", "Ve", "Sa"],
+ months: ["Xaneiro", "Febreiro", "Marzo", "Abril", "Maio", "Xuño", "Xullo", "Agosto", "Setembro", "Outubro", "Novembro", "Decembro"],
+ monthsShort: ["Xan", "Feb", "Mar", "Abr", "Mai", "Xun", "Xul", "Ago", "Sep", "Out", "Nov", "Dec"],
+ today: "Hoxe",
+ clear: "Limpar",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/he.js b/src/ui/static/js/datepicker/i18n/locales/he.js
new file mode 100644
index 000000000..c7f1d62e6
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/he.js
@@ -0,0 +1,15 @@
+/**
+ * Hebrew translation for bootstrap-datepicker
+ * Sagie Maoz
+ */
+export default {
+ he: {
+ days: ["ראשון", "שני", "שלישי", "רביעי", "חמישי", "שישי", "שבת", "ראשון"],
+ daysShort: ["א", "ב", "ג", "ד", "ה", "ו", "ש", "א"],
+ daysMin: ["א", "ב", "ג", "ד", "ה", "ו", "ש", "א"],
+ months: ["ינואר", "פברואר", "מרץ", "אפריל", "מאי", "יוני", "יולי", "אוגוסט", "ספטמבר", "אוקטובר", "נובמבר", "דצמבר"],
+ monthsShort: ["ינו", "פבר", "מרץ", "אפר", "מאי", "יונ", "יול", "אוג", "ספט", "אוק", "נוב", "דצמ"],
+ today: "היום",
+ rtl: true
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/hi.js b/src/ui/static/js/datepicker/i18n/locales/hi.js
new file mode 100644
index 000000000..25d68cdab
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/hi.js
@@ -0,0 +1,18 @@
+/**
+ * Hindi translation for bootstrap-datepicker
+ * Visar Uruqi
+ */
+export default {
+ hi: {
+ days: ["रविवार", "सोमवार", "मंगलवार", "बुधवार", "गुरुवार", "शुक्रवार", "शनिवार"],
+ daysShort: ["सूर्य", "सोम", "मंगल", "बुध", "गुरु", "शुक्र", "शनि"],
+ daysMin: ["र", "सो", "मं", "बु", "गु", "शु", "श"],
+ months: ["जनवरी", "फ़रवरी", "मार्च", "अप्रैल", "मई", "जून", "जुलाई", "अगस्त", "सितम्बर", "अक्टूबर", "नवंबर", "दिसम्बर"],
+ monthsShort: ["जन", "फ़रवरी", "मार्च", "अप्रैल", "मई", "जून", "जुलाई", "अगस्त", "सितं", "अक्टूबर", "नवं", "दिसम्बर"],
+ today: "आज",
+ monthsTitle: "महीने",
+ clear: "साफ",
+ weekStart: 1,
+ format: "dd / mm / yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/hr.js b/src/ui/static/js/datepicker/i18n/locales/hr.js
new file mode 100644
index 000000000..0360859c2
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/hr.js
@@ -0,0 +1,13 @@
+/**
+ * Croatian localisation
+ */
+export default {
+ hr: {
+ days: ["Nedjelja", "Ponedjeljak", "Utorak", "Srijeda", "Četvrtak", "Petak", "Subota"],
+ daysShort: ["Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub"],
+ daysMin: ["Ne", "Po", "Ut", "Sr", "Če", "Pe", "Su"],
+ months: ["Siječanj", "Veljača", "Ožujak", "Travanj", "Svibanj", "Lipanj", "Srpanj", "Kolovoz", "Rujan", "Listopad", "Studeni", "Prosinac"],
+ monthsShort: ["Sij", "Velj", "Ožu", "Tra", "Svi", "Lip", "Srp", "Kol", "Ruj", "Lis", "Stu", "Pro"],
+ today: "Danas"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/hu.js b/src/ui/static/js/datepicker/i18n/locales/hu.js
new file mode 100644
index 000000000..f5342671b
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/hu.js
@@ -0,0 +1,18 @@
+/**
+ * Hungarian translation for bootstrap-datepicker
+ * Sotus László
+ */
+export default {
+ hu: {
+ days: ["vasárnap", "hétfő", "kedd", "szerda", "csütörtök", "péntek", "szombat"],
+ daysShort: ["vas", "hét", "ked", "sze", "csü", "pén", "szo"],
+ daysMin: ["V", "H", "K", "Sze", "Cs", "P", "Szo"],
+ months: ["január", "február", "március", "április", "május", "június", "július", "augusztus", "szeptember", "október", "november", "december"],
+ monthsShort: ["jan", "feb", "már", "ápr", "máj", "jún", "júl", "aug", "sze", "okt", "nov", "dec"],
+ today: "ma",
+ weekStart: 1,
+ clear: "töröl",
+ titleFormat: "y. MM",
+ format: "yyyy.mm.dd"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/hy.js b/src/ui/static/js/datepicker/i18n/locales/hy.js
new file mode 100644
index 000000000..599dc800e
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/hy.js
@@ -0,0 +1,18 @@
+/**
+ * Armenian translation for bootstrap-datepicker
+ * Hayk Chamyan
+ */
+export default {
+ hy: {
+ days: ["Կիրակի", "Երկուշաբթի", "Երեքշաբթի", "Չորեքշաբթի", "Հինգշաբթի", "Ուրբաթ", "Շաբաթ"],
+ daysShort: ["Կիր", "Երկ", "Երե", "Չոր", "Հին", "Ուրբ", "Շաբ"],
+ daysMin: ["Կի", "Եկ", "Եք", "Չո", "Հի", "Ու", "Շա"],
+ months: ["Հունվար", "Փետրվար", "Մարտ", "Ապրիլ", "Մայիս", "Հունիս", "Հուլիս", "Օգոստոս", "Սեպտեմբեր", "Հոկտեմբեր", "Նոյեմբեր", "Դեկտեմբեր"],
+ monthsShort: ["Հնվ", "Փետ", "Մար", "Ապր", "Մայ", "Հուն", "Հուլ", "Օգս", "Սեպ", "Հոկ", "Նոյ", "Դեկ"],
+ today: "Այսօր",
+ clear: "Ջնջել",
+ format: "dd.mm.yyyy",
+ weekStart: 1,
+ monthsTitle: 'Ամիսնէր'
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/id.js b/src/ui/static/js/datepicker/i18n/locales/id.js
new file mode 100644
index 000000000..4dc532baa
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/id.js
@@ -0,0 +1,19 @@
+/**
+ * Bahasa translation for bootstrap-datepicker
+ * Azwar Akbar
+ * Ardeman
+ */
+export default {
+ id: {
+ days: ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"],
+ daysShort: ["Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"],
+ daysMin: ["Mg", "Sn", "Sl", "Rb", "Km", "Jm", "Sb"],
+ months: ["Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Agt", "Sep", "Okt", "Nov", "Des"],
+ today: "Hari Ini",
+ monthsTitle: "Bulan",
+ clear: "Kosongkan",
+ weekStart: 0,
+ format: "dd-mm-yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/is.js b/src/ui/static/js/datepicker/i18n/locales/is.js
new file mode 100644
index 000000000..efbd2a6d9
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/is.js
@@ -0,0 +1,14 @@
+/**
+ * Icelandic translation for bootstrap-datepicker
+ * Hinrik Örn Sigurðsson
+ */
+export default {
+ is: {
+ days: ["Sunnudagur", "Mánudagur", "Þriðjudagur", "Miðvikudagur", "Fimmtudagur", "Föstudagur", "Laugardagur"],
+ daysShort: ["Sun", "Mán", "Þri", "Mið", "Fim", "Fös", "Lau"],
+ daysMin: ["Su", "Má", "Þr", "Mi", "Fi", "Fö", "La"],
+ months: ["Janúar", "Febrúar", "Mars", "Apríl", "Maí", "Júní", "Júlí", "Ágúst", "September", "Október", "Nóvember", "Desember"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maí", "Jún", "Júl", "Ágú", "Sep", "Okt", "Nóv", "Des"],
+ today: "Í Dag"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/it-CH.js b/src/ui/static/js/datepicker/i18n/locales/it-CH.js
new file mode 100644
index 000000000..21e355bb9
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/it-CH.js
@@ -0,0 +1,20 @@
+/**
+ * Italian (Switzerland) translation for bootstrap-datepicker
+ * Christoph Jossi
+ * Based on
+ * Italian translation for bootstrap-datepicker
+ * Enrico Rubboli
+ */
+export default {
+ 'it-CH': {
+ days: ["Domenica", "Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato"],
+ daysShort: ["Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"],
+ daysMin: ["Do", "Lu", "Ma", "Me", "Gi", "Ve", "Sa"],
+ months: ["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"],
+ monthsShort: ["Gen", "Feb", "Mar", "Apr", "Mag", "Giu", "Lug", "Ago", "Set", "Ott", "Nov", "Dic"],
+ today: "Oggi",
+ clear: "Cancella",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/it.js b/src/ui/static/js/datepicker/i18n/locales/it.js
new file mode 100644
index 000000000..169500447
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/it.js
@@ -0,0 +1,18 @@
+/**
+ * Italian translation for bootstrap-datepicker
+ * Enrico Rubboli
+ */
+export default {
+ it: {
+ days: ["Domenica", "Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato"],
+ daysShort: ["Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"],
+ daysMin: ["Do", "Lu", "Ma", "Me", "Gi", "Ve", "Sa"],
+ months: ["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"],
+ monthsShort: ["Gen", "Feb", "Mar", "Apr", "Mag", "Giu", "Lug", "Ago", "Set", "Ott", "Nov", "Dic"],
+ today: "Oggi",
+ monthsTitle: "Mesi",
+ clear: "Cancella",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/ja.js b/src/ui/static/js/datepicker/i18n/locales/ja.js
new file mode 100644
index 000000000..bdcb0dd99
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/ja.js
@@ -0,0 +1,17 @@
+/**
+ * Japanese translation for bootstrap-datepicker
+ * Norio Suzuki
+ */
+export default {
+ ja: {
+ days: ["日曜", "月曜", "火曜", "水曜", "木曜", "金曜", "土曜"],
+ daysShort: ["日", "月", "火", "水", "木", "金", "土"],
+ daysMin: ["日", "月", "火", "水", "木", "金", "土"],
+ months: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
+ monthsShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
+ today: "今日",
+ format: "yyyy/mm/dd",
+ titleFormat: "y年mm月",
+ clear: "クリア"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/ka.js b/src/ui/static/js/datepicker/i18n/locales/ka.js
new file mode 100644
index 000000000..b2a7c0ddf
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/ka.js
@@ -0,0 +1,17 @@
+/**
+ * Georgian translation for bootstrap-datepicker
+ * Levan Melikishvili
+ */
+export default {
+ ka: {
+ days: ["კვირა", "ორშაბათი", "სამშაბათი", "ოთხშაბათი", "ხუთშაბათი", "პარასკევი", "შაბათი"],
+ daysShort: ["კვი", "ორშ", "სამ", "ოთხ", "ხუთ", "პარ", "შაბ"],
+ daysMin: ["კვ", "ორ", "სა", "ოთ", "ხუ", "პა", "შა"],
+ months: ["იანვარი", "თებერვალი", "მარტი", "აპრილი", "მაისი", "ივნისი", "ივლისი", "აგვისტო", "სექტემბერი", "ოქტომბერი", "ნოემბერი", "დეკემბერი"],
+ monthsShort: ["იან", "თებ", "მარ", "აპრ", "მაი", "ივნ", "ივლ", "აგვ", "სექ", "ოქტ", "ნოე", "დეკ"],
+ today: "დღეს",
+ clear: "გასუფთავება",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/kk.js b/src/ui/static/js/datepicker/i18n/locales/kk.js
new file mode 100644
index 000000000..46ad589e8
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/kk.js
@@ -0,0 +1,15 @@
+/**
+ * Kazakh translation for bootstrap-datepicker
+ * Yerzhan Tolekov
+ */
+export default {
+ kk: {
+ days: ["Жексенбі", "Дүйсенбі", "Сейсенбі", "Сәрсенбі", "Бейсенбі", "Жұма", "Сенбі"],
+ daysShort: ["Жек", "Дүй", "Сей", "Сәр", "Бей", "Жұм", "Сен"],
+ daysMin: ["Жк", "Дс", "Сс", "Ср", "Бс", "Жм", "Сн"],
+ months: ["Қаңтар", "Ақпан", "Наурыз", "Сәуір", "Мамыр", "Маусым", "Шілде", "Тамыз", "Қыркүйек", "Қазан", "Қараша", "Желтоқсан"],
+ monthsShort: ["Қаң", "Ақп", "Нау", "Сәу", "Мам", "Мау", "Шіл", "Там", "Қыр", "Қаз", "Қар", "Жел"],
+ today: "Бүгін",
+ weekStart: 1
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/km.js b/src/ui/static/js/datepicker/i18n/locales/km.js
new file mode 100644
index 000000000..1688262e0
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/km.js
@@ -0,0 +1,15 @@
+/**
+ * Khmer translation for bootstrap-datepicker
+ * This is the Updated Version of: https: //github.com/uxsolutions/bootstrap-datepicker/blob/71308d42cce9524284c50c6fac50422d1790ac0f/js/locales/bootstrap-datepicker.kh.js
+ */
+export default {
+ km: {
+ days: ["អាទិត្យ", "ចន្ទ", "អង្គារ", "ពុធ", "ព្រហស្បតិ៍", "សុក្រ", "សៅរ៍"],
+ daysShort: ["អា.ទិ", "ចន្ទ", "អង្គារ", "ពុធ", "ព្រ.ហ", "សុក្រ", "សៅរ៍"],
+ daysMin: ["អា.ទិ", "ចន្ទ", "អង្គារ", "ពុធ", "ព្រ.ហ", "សុក្រ", "សៅរ៍"],
+ months: ["មករា", "កុម្ភះ", "មិនា", "មេសា", "ឧសភា", "មិថុនា", "កក្កដា", "សីហា", "កញ្ញា", "តុលា", "វិច្ឆិកា", "ធ្នូ"],
+ monthsShort: ["មករា", "កុម្ភះ", "មិនា", "មេសា", "ឧសភា", "មិថុនា", "កក្កដា", "សីហា", "កញ្ញា", "តុលា", "វិច្ឆិកា", "ធ្នូ"],
+ today: "ថ្ងៃនេះ",
+ clear: "សំអាត"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/ko.js b/src/ui/static/js/datepicker/i18n/locales/ko.js
new file mode 100644
index 000000000..d5eb49e8b
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/ko.js
@@ -0,0 +1,18 @@
+/**
+ * Korean translation for bootstrap-datepicker
+ * This is a port from https: //github.com/moment/moment/blob/develop/src/locale/ko.js
+ */
+export default {
+ ko: {
+ days: ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일"],
+ daysShort: ["일", "월", "화", "수", "목", "금", "토"],
+ daysMin: ["일", "월", "화", "수", "목", "금", "토"],
+ months: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"],
+ monthsShort: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"],
+ today: "오늘",
+ clear: "삭제",
+ format: "yyyy-mm-dd",
+ titleFormat: "y년mm월",
+ weekStart: 0
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/lt.js b/src/ui/static/js/datepicker/i18n/locales/lt.js
new file mode 100644
index 000000000..960ef9fff
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/lt.js
@@ -0,0 +1,19 @@
+/**
+ * Lithuanian translation for bootstrap-datepicker
+ * Šarūnas Gliebus
+ */
+
+export default {
+ lt: {
+ days: ["Sekmadienis", "Pirmadienis", "Antradienis", "Trečiadienis", "Ketvirtadienis", "Penktadienis", "Šeštadienis"],
+ daysShort: ["S", "Pr", "A", "T", "K", "Pn", "Š"],
+ daysMin: ["Sk", "Pr", "An", "Tr", "Ke", "Pn", "Št"],
+ months: ["Sausis", "Vasaris", "Kovas", "Balandis", "Gegužė", "Birželis", "Liepa", "Rugpjūtis", "Rugsėjis", "Spalis", "Lapkritis", "Gruodis"],
+ monthsShort: ["Sau", "Vas", "Kov", "Bal", "Geg", "Bir", "Lie", "Rugp", "Rugs", "Spa", "Lap", "Gru"],
+ today: "Šiandien",
+ monthsTitle: "Mėnesiai",
+ clear: "Išvalyti",
+ weekStart: 1,
+ format: "yyyy-mm-dd"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/lv.js b/src/ui/static/js/datepicker/i18n/locales/lv.js
new file mode 100644
index 000000000..68a4542f8
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/lv.js
@@ -0,0 +1,18 @@
+/**
+ * Latvian translation for bootstrap-datepicker
+ * Artis Avotins
+ */
+
+export default {
+ lv: {
+ days: ["Svētdiena", "Pirmdiena", "Otrdiena", "Trešdiena", "Ceturtdiena", "Piektdiena", "Sestdiena"],
+ daysShort: ["Sv", "P", "O", "T", "C", "Pk", "S"],
+ daysMin: ["Sv", "Pr", "Ot", "Tr", "Ce", "Pk", "Se"],
+ months: ["Janvāris", "Februāris", "Marts", "Aprīlis", "Maijs", "Jūnijs", "Jūlijs", "Augusts", "Septembris", "Oktobris", "Novembris", "Decembris"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mai", "Jūn", "Jūl", "Aug", "Sep", "Okt", "Nov", "Dec"],
+ monthsTitle: "Mēneši",
+ today: "Šodien",
+ clear: "Nodzēst",
+ weekStart: 1
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/me.js b/src/ui/static/js/datepicker/i18n/locales/me.js
new file mode 100644
index 000000000..7fa5cb376
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/me.js
@@ -0,0 +1,17 @@
+/**
+ * Montenegrin translation for bootstrap-datepicker
+ * Miodrag Nikač
+ */
+export default {
+ me: {
+ days: ["Nedjelja","Ponedjeljak", "Utorak", "Srijeda", "Četvrtak", "Petak", "Subota"],
+ daysShort: ["Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub"],
+ daysMin: ["Ne", "Po", "Ut", "Sr", "Če", "Pe", "Su"],
+ months: ["Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Avg", "Sep", "Okt", "Nov", "Dec"],
+ today: "Danas",
+ weekStart: 1,
+ clear: "Izbriši",
+ format: "dd.mm.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/mk.js b/src/ui/static/js/datepicker/i18n/locales/mk.js
new file mode 100644
index 000000000..7debd94fd
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/mk.js
@@ -0,0 +1,15 @@
+/**
+ * Macedonian translation for bootstrap-datepicker
+ * Marko Aleksic
+ */
+export default {
+ mk: {
+ days: ["Недела", "Понеделник", "Вторник", "Среда", "Четврток", "Петок", "Сабота"],
+ daysShort: ["Нед", "Пон", "Вто", "Сре", "Чет", "Пет", "Саб"],
+ daysMin: ["Не", "По", "Вт", "Ср", "Че", "Пе", "Са"],
+ months: ["Јануари", "Февруари", "Март", "Април", "Мај", "Јуни", "Јули", "Август", "Септември", "Октомври", "Ноември", "Декември"],
+ monthsShort: ["Јан", "Фев", "Мар", "Апр", "Мај", "Јун", "Јул", "Авг", "Сеп", "Окт", "Ное", "Дек"],
+ today: "Денес",
+ format: "dd.mm.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/mn.js b/src/ui/static/js/datepicker/i18n/locales/mn.js
new file mode 100644
index 000000000..6bd14dbe7
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/mn.js
@@ -0,0 +1,17 @@
+/**
+ * Mongolian translation for bootstrap-datepicker
+ * Andrey Torsunov
+ */
+export default {
+ mn: {
+ days: ["Ням", "Даваа", "Мягмар", "Лхагва", "Пүрэв", "Баасан", "Бямба"],
+ daysShort: ["Ням", "Дав", "Мяг", "Лха", "Пүр", "Баа", "Бям"],
+ daysMin: ["Ня", "Да", "Мя", "Лх", "Пү", "Ба", "Бя"],
+ months: ["Хулгана", "Үхэр", "Бар", "Туулай", "Луу", "Могой", "Морь", "Хонь", "Бич", "Тахиа", "Нохой", "Гахай"],
+ monthsShort: ["Хул", "Үхэ", "Бар", "Туу", "Луу", "Мог", "Мор", "Хон", "Бич", "Тах", "Нох", "Гах"],
+ today: "Өнөөдөр",
+ clear: "Тодорхой",
+ format: "yyyy.mm.dd",
+ weekStart: 1
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/mr.js b/src/ui/static/js/datepicker/i18n/locales/mr.js
new file mode 100644
index 000000000..3f29a0266
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/mr.js
@@ -0,0 +1,18 @@
+/**
+ * Marathi translation for bootstrap-datepicker
+ * Sushant Pimple
+ */
+export default {
+ mr: {
+ days: ["रविवार", "सोमवार", "मंगळवार", "बुधवार", "गुरुवार", "शुक्रवार", "शनिवार"],
+ daysShort: ["रवि", "सोम", "मंगळ", "बुध", "गुरु", "शुक्र", "शनि"],
+ daysMin: ["र", "सो", "मं", "बु", "गु", "शु", "श"],
+ months: ["जानेवारी", "फेब्रुवारी", "मार्च", "एप्रिल", "मे", "जून", "जुलै", "ऑगस्ट", "सप्टेंबर", "ऑक्टोबर", "नोव्हेंबर", "डिसेंबर"],
+ monthsShort: ["जाने.", "फेब्रु.", "मार्च", "एप्रिल", "मे", "जून", "जुलै", "ऑगस्ट", "सप्टें.", "ऑक्टो.", "नोव्हें.", "डिसें."],
+ today: "आज",
+ monthsTitle: "महीने",
+ clear: "हटवा",
+ weekStart: 1,
+ format: "dd / mm / yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/ms.js b/src/ui/static/js/datepicker/i18n/locales/ms.js
new file mode 100644
index 000000000..66a947ffd
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/ms.js
@@ -0,0 +1,15 @@
+/**
+ * Malay translation for bootstrap-datepicker
+ * Ateman Faiz
+ */
+export default {
+ ms: {
+ days: ["Ahad", "Isnin", "Selasa", "Rabu", "Khamis", "Jumaat", "Sabtu"],
+ daysShort: ["Aha", "Isn", "Sel", "Rab", "Kha", "Jum", "Sab"],
+ daysMin: ["Ah", "Is", "Se", "Ra", "Kh", "Ju", "Sa"],
+ months: ["Januari", "Februari", "Mac", "April", "Mei", "Jun", "Julai", "Ogos", "September", "Oktober", "November", "Disember"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Ogo", "Sep", "Okt", "Nov", "Dis"],
+ today: "Hari Ini",
+ clear: "Bersihkan"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/nl-BE.js b/src/ui/static/js/datepicker/i18n/locales/nl-BE.js
new file mode 100644
index 000000000..b4a5c878b
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/nl-BE.js
@@ -0,0 +1,18 @@
+/**
+ * Belgium-Dutch translation for bootstrap-datepicker
+ * Julien Poulin
+ */
+export default {
+ 'nl-BE': {
+ days: ["zondag", "maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag"],
+ daysShort: ["zo", "ma", "di", "wo", "do", "vr", "za"],
+ daysMin: ["zo", "ma", "di", "wo", "do", "vr", "za"],
+ months: ["januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december"],
+ monthsShort: ["jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec"],
+ today: "Vandaag",
+ monthsTitle: "Maanden",
+ clear: "Leegmaken",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/nl.js b/src/ui/static/js/datepicker/i18n/locales/nl.js
new file mode 100644
index 000000000..365dc21b3
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/nl.js
@@ -0,0 +1,18 @@
+/**
+ * Dutch translation for bootstrap-datepicker
+ * Reinier Goltstein
+ */
+export default {
+ nl: {
+ days: ["zondag", "maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag"],
+ daysShort: ["zo", "ma", "di", "wo", "do", "vr", "za"],
+ daysMin: ["zo", "ma", "di", "wo", "do", "vr", "za"],
+ months: ["januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december"],
+ monthsShort: ["jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec"],
+ today: "Vandaag",
+ monthsTitle: "Maanden",
+ clear: "Wissen",
+ weekStart: 1,
+ format: "dd-mm-yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/no.js b/src/ui/static/js/datepicker/i18n/locales/no.js
new file mode 100644
index 000000000..aa54b82c4
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/no.js
@@ -0,0 +1,18 @@
+/**
+ * Norwegian translation for bootstrap-datepicker
+ * George Gooding
+ */
+export default {
+ no: {
+ days: ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag'],
+ daysShort: ['søn', 'man', 'tir', 'ons', 'tor', 'fre', 'lør'],
+ daysMin: ['sø', 'ma', 'ti', 'on', 'to', 'fr', 'lø'],
+ months: ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember'],
+ monthsShort: ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des'],
+ today: 'i dag',
+ monthsTitle: 'Måneder',
+ clear: 'Nullstill',
+ weekStart: 1,
+ format: 'dd.mm.yyyy'
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/oc.js b/src/ui/static/js/datepicker/i18n/locales/oc.js
new file mode 100644
index 000000000..b04e9f00c
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/oc.js
@@ -0,0 +1,17 @@
+/**
+ * Occitan translation for bootstrap-datepicker
+ */
+export default {
+ oc: {
+ days: ["Dimenge", "Diluns", "Dimars", "Dimècres", "Dijòus", "Divendres", "Dissabte"],
+ daysShort: ["Dim", "Dil", "Dmr", "Dmc", "Dij", "Div", "Dis"],
+ daysMin: ["dg", "dl", "dr", "dc", "dj", "dv", "ds"],
+ months: ["Genièr", "Febrièr", "Març", "Abrial", "Mai", "Junh", "Julhet", "Agost", "Setembre", "Octobre", "Novembre", "Decembre"],
+ monthsShort: ["Gen", "Feb", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Oct", "Nov", "Dec"],
+ today: "Uèi",
+ monthsTitle: "Meses",
+ clear: "Escafar",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/pl.js b/src/ui/static/js/datepicker/i18n/locales/pl.js
new file mode 100644
index 000000000..62b8ede6c
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/pl.js
@@ -0,0 +1,17 @@
+/**
+ * Polish translation for bootstrap-datepicker
+ * Robert
+ */
+export default {
+ pl: {
+ days: ["Niedziela", "Poniedziałek", "Wtorek", "Środa", "Czwartek", "Piątek", "Sobota"],
+ daysShort: ["Niedz.", "Pon.", "Wt.", "Śr.", "Czw.", "Piąt.", "Sob."],
+ daysMin: ["Ndz.", "Pn.", "Wt.", "Śr.", "Czw.", "Pt.", "Sob."],
+ months: ["Styczeń", "Luty", "Marzec", "Kwiecień", "Maj", "Czerwiec", "Lipiec", "Sierpień", "Wrzesień", "Październik", "Listopad", "Grudzień"],
+ monthsShort: ["Sty.", "Lut.", "Mar.", "Kwi.", "Maj", "Cze.", "Lip.", "Sie.", "Wrz.", "Paź.", "Lis.", "Gru."],
+ today: "Dzisiaj",
+ weekStart: 1,
+ clear: "Wyczyść",
+ format: "dd.mm.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/pt-BR.js b/src/ui/static/js/datepicker/i18n/locales/pt-BR.js
new file mode 100644
index 000000000..051c03119
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/pt-BR.js
@@ -0,0 +1,17 @@
+/**
+ * Brazilian translation for bootstrap-datepicker
+ * Cauan Cabral
+ */
+export default {
+ 'pt-BR': {
+ days: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"],
+ daysShort: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"],
+ daysMin: ["Do", "Se", "Te", "Qu", "Qu", "Se", "Sa"],
+ months: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
+ monthsShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"],
+ today: "Hoje",
+ monthsTitle: "Meses",
+ clear: "Limpar",
+ format: "dd/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/pt.js b/src/ui/static/js/datepicker/i18n/locales/pt.js
new file mode 100644
index 000000000..baaa6ea7c
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/pt.js
@@ -0,0 +1,18 @@
+/**
+ * Portuguese translation for bootstrap-datepicker
+ * Original code: Cauan Cabral
+ * Tiago Melo
+ */
+export default {
+ pt: {
+ days: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"],
+ daysShort: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"],
+ daysMin: ["Do", "Se", "Te", "Qu", "Qu", "Se", "Sa"],
+ months: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
+ monthsShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"],
+ today: "Hoje",
+ monthsTitle: "Meses",
+ clear: "Limpar",
+ format: "dd/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/ro.js b/src/ui/static/js/datepicker/i18n/locales/ro.js
new file mode 100644
index 000000000..1039334c8
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/ro.js
@@ -0,0 +1,17 @@
+/**
+ * Romanian translation for bootstrap-datepicker
+ * Cristian Vasile
+ */
+export default {
+ ro: {
+ days: ["Duminică", "Luni", "Marţi", "Miercuri", "Joi", "Vineri", "Sâmbătă"],
+ daysShort: ["Dum", "Lun", "Mar", "Mie", "Joi", "Vin", "Sâm"],
+ daysMin: ["Du", "Lu", "Ma", "Mi", "Jo", "Vi", "Sâ"],
+ months: ["Ianuarie", "Februarie", "Martie", "Aprilie", "Mai", "Iunie", "Iulie", "August", "Septembrie", "Octombrie", "Noiembrie", "Decembrie"],
+ monthsShort: ["Ian", "Feb", "Mar", "Apr", "Mai", "Iun", "Iul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Astăzi",
+ clear: "Șterge",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/ru.js b/src/ui/static/js/datepicker/i18n/locales/ru.js
new file mode 100644
index 000000000..b1e866ede
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/ru.js
@@ -0,0 +1,18 @@
+/**
+ * Russian translation for bootstrap-datepicker
+ * Victor Taranenko
+ */
+export default {
+ ru: {
+ days: ["Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"],
+ daysShort: ["Вск", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Суб"],
+ daysMin: ["Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"],
+ months: ["Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"],
+ monthsShort: ["Янв", "Фев", "Мар", "Апр", "Май", "Июн", "Июл", "Авг", "Сен", "Окт", "Ноя", "Дек"],
+ today: "Сегодня",
+ clear: "Очистить",
+ format: "dd.mm.yyyy",
+ weekStart: 1,
+ monthsTitle: 'Месяцы'
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/si.js b/src/ui/static/js/datepicker/i18n/locales/si.js
new file mode 100644
index 000000000..a09d0dfaa
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/si.js
@@ -0,0 +1,18 @@
+/**
+ * Sinhala translation for bootstrap-datepicker
+ * Chanaka Fernando
+ */
+export default {
+ si: {
+ days: ["ඉරිදා", "සඳුදා", "අඟහරුවාදා", "බදාදා", "බ්රහස්පතින්දා", "සිකුරාදා", "සෙනසුරාදා"],
+ daysShort: ["ඉරි", "සඳු", "අඟ", "බදා", "බ්රහ", "සිකු", "සෙන"],
+ daysMin: ["ඉ", "ස", "අ", "බ", "බ්ර", "සි", "සෙ"],
+ months: ["ජනවාරි", "පෙබරවාරි", "මාර්තු", "අප්රේල්", "මැයි", "ජුනි", "ජූලි", "අගෝස්තු", "සැප්තැම්බර්", "ඔක්තෝබර්", "නොවැම්බර්", "දෙසැම්බර්"],
+ monthsShort: ["ජන", "පෙබ", "මාර්", "අප්රේ", "මැයි", "ජුනි", "ජූලි", "අගෝ", "සැප්", "ඔක්", "නොවැ", "දෙසැ"],
+ today: "අද",
+ monthsTitle: "මාස",
+ clear: "මකන්න",
+ weekStart: 0,
+ format: "yyyy-mm-dd"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/sk.js b/src/ui/static/js/datepicker/i18n/locales/sk.js
new file mode 100644
index 000000000..9c4a70d4c
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/sk.js
@@ -0,0 +1,18 @@
+/**
+ * Slovak translation for bootstrap-datepicker
+ * Marek Lichtner
+ * Fixes by Michal Remiš
+ */
+export default {
+ sk: {
+ days: ["Nedeľa", "Pondelok", "Utorok", "Streda", "Štvrtok", "Piatok", "Sobota"],
+ daysShort: ["Ned", "Pon", "Uto", "Str", "Štv", "Pia", "Sob"],
+ daysMin: ["Ne", "Po", "Ut", "St", "Št", "Pia", "So"],
+ months: ["Január", "Február", "Marec", "Apríl", "Máj", "Jún", "Júl", "August", "September", "Október", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Máj", "Jún", "Júl", "Aug", "Sep", "Okt", "Nov", "Dec"],
+ today: "Dnes",
+ clear: "Vymazať",
+ weekStart: 1,
+ format: "d.m.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/sl.js b/src/ui/static/js/datepicker/i18n/locales/sl.js
new file mode 100644
index 000000000..87acf5a62
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/sl.js
@@ -0,0 +1,15 @@
+/**
+ * Slovene translation for bootstrap-datepicker
+ * Gregor Rudolf
+ */
+export default {
+ sl: {
+ days: ["Nedelja", "Ponedeljek", "Torek", "Sreda", "Četrtek", "Petek", "Sobota"],
+ daysShort: ["Ned", "Pon", "Tor", "Sre", "Čet", "Pet", "Sob"],
+ daysMin: ["Ne", "Po", "To", "Sr", "Če", "Pe", "So"],
+ months: ["Januar", "Februar", "Marec", "April", "Maj", "Junij", "Julij", "Avgust", "September", "Oktober", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Avg", "Sep", "Okt", "Nov", "Dec"],
+ today: "Danes",
+ weekStart: 1
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/sq.js b/src/ui/static/js/datepicker/i18n/locales/sq.js
new file mode 100644
index 000000000..be94a7643
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/sq.js
@@ -0,0 +1,18 @@
+/**
+ * Albanian translation for bootstrap-datepicker
+ * Tomor Pupovci
+ */
+export default {
+ sq: {
+ days: ["E Diel", "E Hënë", "E Martē", "E Mërkurë", "E Enjte", "E Premte", "E Shtunë"],
+ daysShort: ["Die", "Hën", "Mar", "Mër", "Enj", "Pre", "Shtu"],
+ daysMin: ["Di", "Hë", "Ma", "Më", "En", "Pr", "Sht"],
+ months: ["Janar", "Shkurt", "Mars", "Prill", "Maj", "Qershor", "Korrik", "Gusht", "Shtator", "Tetor", "Nëntor", "Dhjetor"],
+ monthsShort: ["Jan", "Shk", "Mar", "Pri", "Maj", "Qer", "Korr", "Gu", "Sht", "Tet", "Nën", "Dhjet"],
+ monthsTitle: "Muaj",
+ today: "Sot",
+ weekStart: 1,
+ format: "dd/mm/yyyy",
+ clear: "Pastro"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/sr-latn.js b/src/ui/static/js/datepicker/i18n/locales/sr-latn.js
new file mode 100644
index 000000000..a84b45196
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/sr-latn.js
@@ -0,0 +1,16 @@
+/**
+ * Serbian latin translation for bootstrap-datepicker
+ * Bojan Milosavlević
+ */
+export default {
+ 'sr-latn': {
+ days: ["Nedelja","Ponedeljak", "Utorak", "Sreda", "Četvrtak", "Petak", "Subota"],
+ daysShort: ["Ned", "Pon", "Uto", "Sre", "Čet", "Pet", "Sub"],
+ daysMin: ["N", "Po", "U", "Sr", "Č", "Pe", "Su"],
+ months: ["Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Avg", "Sep", "Okt", "Nov", "Dec"],
+ today: "Danas",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/sr.js b/src/ui/static/js/datepicker/i18n/locales/sr.js
new file mode 100644
index 000000000..2a2ae0a05
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/sr.js
@@ -0,0 +1,16 @@
+/**
+ * Serbian cyrillic translation for bootstrap-datepicker
+ * Bojan Milosavlević
+ */
+export default {
+ sr: {
+ days: ["Недеља","Понедељак", "Уторак", "Среда", "Четвртак", "Петак", "Субота"],
+ daysShort: ["Нед", "Пон", "Уто", "Сре", "Чет", "Пет", "Суб"],
+ daysMin: ["Н", "По", "У", "Ср", "Ч", "Пе", "Су"],
+ months: ["Јануар", "Фебруар", "Март", "Април", "Мај", "Јун", "Јул", "Август", "Септембар", "Октобар", "Новембар", "Децембар"],
+ monthsShort: ["Јан", "Феб", "Мар", "Апр", "Мај", "Јун", "Јул", "Авг", "Сеп", "Окт", "Нов", "Дец"],
+ today: "Данас",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/sv.js b/src/ui/static/js/datepicker/i18n/locales/sv.js
new file mode 100644
index 000000000..2323d4363
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/sv.js
@@ -0,0 +1,17 @@
+/**
+ * Swedish translation for bootstrap-datepicker
+ * Patrik Ragnarsson
+ */
+export default {
+ sv: {
+ days: ["söndag", "måndag", "tisdag", "onsdag", "torsdag", "fredag", "lördag"],
+ daysShort: ["sön", "mån", "tis", "ons", "tor", "fre", "lör"],
+ daysMin: ["sö", "må", "ti", "on", "to", "fr", "lö"],
+ months: ["januari", "februari", "mars", "april", "maj", "juni", "juli", "augusti", "september", "oktober", "november", "december"],
+ monthsShort: ["jan", "feb", "mar", "apr", "maj", "jun", "jul", "aug", "sep", "okt", "nov", "dec"],
+ today: "Idag",
+ format: "yyyy-mm-dd",
+ weekStart: 1,
+ clear: "Rensa"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/sw.js b/src/ui/static/js/datepicker/i18n/locales/sw.js
new file mode 100644
index 000000000..61ce0add2
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/sw.js
@@ -0,0 +1,15 @@
+/**
+ * Swahili translation for bootstrap-datepicker
+ * Edwin Mugendi
+ * Source: http: //scriptsource.org/cms/scripts/page.php?item_id=entry_detail&uid=xnfaqyzcku
+ */
+export default {
+ sw: {
+ days: ["Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi"],
+ daysShort: ["J2", "J3", "J4", "J5", "Alh", "Ij", "J1"],
+ daysMin: ["2", "3", "4", "5", "A", "I", "1"],
+ months: ["Januari", "Februari", "Machi", "Aprili", "Mei", "Juni", "Julai", "Agosti", "Septemba", "Oktoba", "Novemba", "Desemba"],
+ monthsShort: ["Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nov", "Des"],
+ today: "Leo"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/ta.js b/src/ui/static/js/datepicker/i18n/locales/ta.js
new file mode 100644
index 000000000..3a7ed92b1
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/ta.js
@@ -0,0 +1,18 @@
+/**
+ * Tamil translation for bootstrap-datepicker
+ * Abubacker Siddik A
+ */
+export default {
+ ta: {
+ days: ["ஞாயிறு", "திங்கள்", "செவ்வாய்", "புதன்", "வியாழன்", "வெள்ளி", "சனி"],
+ daysShort: ["ஞாயி", "திங்", "செவ்", "புத", "வியா", "வெள்", "சனி"],
+ daysMin: ["ஞா", "தி", "செ", "பு", "வி", "வெ", "ச"],
+ months: ["ஜனவரி", "பிப்ரவரி", "மார்ச்", "ஏப்ரல்", "மே", "ஜூன்", "ஜூலை", "ஆகஸ்டு", "செப்டம்பர்", "அக்டோபர்", "நவம்பர்", "டிசம்பர்"],
+ monthsShort: ["ஜன", "பிப்", "மார்", "ஏப்", "மே", "ஜூன்", "ஜூலை", "ஆக", "செப்", "அக்", "நவ", "டிச"],
+ today: "இன்று",
+ monthsTitle: "மாதங்கள்",
+ clear: "நீக்கு",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/tg.js b/src/ui/static/js/datepicker/i18n/locales/tg.js
new file mode 100644
index 000000000..b389e6b3f
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/tg.js
@@ -0,0 +1,19 @@
+/**
+ * Tajik (cyrillic) translation for bootstrap-datepicker
+ * Bakhtiyor Bahritidinov
+ * Orif N. Jr.
+ */
+export default {
+ tg: {
+ days: ["Якшанбе", "Душанбе", "Сешанбе", "Чоршанбе", "Панҷшанбе", "Ҷумъа", "Шанбе"],
+ daysShort: ["Яшб", "Дшб", "Сшб", "Чшб", "Пшб", "Ҷум", "Шнб"],
+ daysMin: ["Яш", "Дш", "Сш", "Чш", "Пш", "Ҷм", "Шб"],
+ months: ["Январ", "Феврал", "Март", "Апрел", "Май", "Июн", "Июл", "Август", "Сентябр", "Октябр", "Ноябр", "Декабр"],
+ monthsShort: ["Янв", "Фев", "Мар", "Апр", "Май", "Июн", "Июл", "Авг", "Сен", "Окт", "Ноя", "Дек"],
+ today: "Имрӯз",
+ monthsTitle: "Моҳҳо",
+ clear: "Тоза намудан",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/th.js b/src/ui/static/js/datepicker/i18n/locales/th.js
new file mode 100644
index 000000000..cbfa391e7
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/th.js
@@ -0,0 +1,14 @@
+/**
+ * Thai translation for bootstrap-datepicker
+ * Suchau Jiraprapot
+ */
+export default {
+ th: {
+ days: ["อาทิตย์", "จันทร์", "อังคาร", "พุธ", "พฤหัส", "ศุกร์", "เสาร์", "อาทิตย์"],
+ daysShort: ["อา", "จ", "อ", "พ", "พฤ", "ศ", "ส", "อา"],
+ daysMin: ["อา", "จ", "อ", "พ", "พฤ", "ศ", "ส", "อา"],
+ months: ["มกราคม", "กุมภาพันธ์", "มีนาคม", "เมษายน", "พฤษภาคม", "มิถุนายน", "กรกฎาคม", "สิงหาคม", "กันยายน", "ตุลาคม", "พฤศจิกายน", "ธันวาคม"],
+ monthsShort: ["ม.ค.", "ก.พ.", "มี.ค.", "เม.ย.", "พ.ค.", "มิ.ย.", "ก.ค.", "ส.ค.", "ก.ย.", "ต.ค.", "พ.ย.", "ธ.ค."],
+ today: "วันนี้"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/tk.js b/src/ui/static/js/datepicker/i18n/locales/tk.js
new file mode 100644
index 000000000..0705b1ccd
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/tk.js
@@ -0,0 +1,18 @@
+/**
+ * Turkmen translation for bootstrap-datepicker
+ * N'Bayramberdiyev
+ */
+export default {
+ tk: {
+ days: ["Ýekşenbe", "Duşenbe", "Sişenbe", "Çarşenbe", "Penşenbe", "Anna", "Şenbe"],
+ daysShort: ["Ýek", "Duş", "Siş", "Çar", "Pen", "Ann", "Şen"],
+ daysMin: ["Ýe", "Du", "Si", "Ça", "Pe", "An", "Şe"],
+ months: ["Ýanwar", "Fewral", "Mart", "Aprel", "Maý", "Iýun", "Iýul", "Awgust", "Sentýabr", "Oktýabr", "Noýabr", "Dekabr"],
+ monthsShort: ["Ýan", "Few", "Mar", "Apr", "Maý", "Iýn", "Iýl", "Awg", "Sen", "Okt", "Noý", "Dek"],
+ today: "Bu gün",
+ monthsTitle: "Aýlar",
+ clear: "Aýyr",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/tr.js b/src/ui/static/js/datepicker/i18n/locales/tr.js
new file mode 100644
index 000000000..0f8390e10
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/tr.js
@@ -0,0 +1,17 @@
+/**
+ * Turkish translation for bootstrap-datepicker
+ * Serkan Algur
+ */
+export default {
+ tr: {
+ days: ["Pazar", "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi"],
+ daysShort: ["Pz", "Pzt", "Sal", "Çrş", "Prş", "Cu", "Cts"],
+ daysMin: ["Pz", "Pzt", "Sa", "Çr", "Pr", "Cu", "Ct"],
+ months: ["Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"],
+ monthsShort: ["Oca", "Şub", "Mar", "Nis", "May", "Haz", "Tem", "Ağu", "Eyl", "Eki", "Kas", "Ara"],
+ today: "Bugün",
+ clear: "Temizle",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/uk.js b/src/ui/static/js/datepicker/i18n/locales/uk.js
new file mode 100644
index 000000000..fe5ae79d1
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/uk.js
@@ -0,0 +1,17 @@
+/**
+ * Ukrainian translation for bootstrap-datepicker
+ * Igor Polynets
+ */
+export default {
+ uk: {
+ days: ["Неділя", "Понеділок", "Вівторок", "Середа", "Четвер", "П'ятниця", "Субота"],
+ daysShort: ["Нед", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Суб"],
+ daysMin: ["Нд", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"],
+ months: ["Cічень", "Лютий", "Березень", "Квітень", "Травень", "Червень", "Липень", "Серпень", "Вересень", "Жовтень", "Листопад", "Грудень"],
+ monthsShort: ["Січ", "Лют", "Бер", "Кві", "Тра", "Чер", "Лип", "Сер", "Вер", "Жов", "Лис", "Гру"],
+ today: "Сьогодні",
+ clear: "Очистити",
+ format: "dd.mm.yyyy",
+ weekStart: 1
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/uz-cyrl.js b/src/ui/static/js/datepicker/i18n/locales/uz-cyrl.js
new file mode 100644
index 000000000..b5a7cac58
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/uz-cyrl.js
@@ -0,0 +1,18 @@
+/**
+ * Uzbek cyrillic translation for bootstrap-datepicker
+ * Kakhramonov Javlonbek
+ */
+export default {
+ 'uz-cyrl': {
+ days: ["Якшанба", "Душанба", "Сешанба", "Чоршанба", "Пайшанба", "Жума", "Шанба"],
+ daysShort: ["Якш", "Ду", "Се", "Чор", "Пай", "Жу", "Ша"],
+ daysMin: ["Як", "Ду", "Се", "Чо", "Па", "Жу", "Ша"],
+ months: ["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],
+ monthsShort: ["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],
+ today: "Бугун",
+ clear: "Ўчириш",
+ format: "dd.mm.yyyy",
+ weekStart: 1,
+ monthsTitle: 'Ойлар'
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/uz-latn.js b/src/ui/static/js/datepicker/i18n/locales/uz-latn.js
new file mode 100644
index 000000000..9b689dbe2
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/uz-latn.js
@@ -0,0 +1,18 @@
+/**
+ * Uzbek latin translation for bootstrap-datepicker
+ * Kakhramonov Javlonbek
+ */
+export default {
+ 'uz-latn': {
+ days: ["Yakshanba", "Dushanba", "Seshanba", "Chorshanba", "Payshanba", "Juma", "Shanba"],
+ daysShort: ["Yak", "Du", "Se", "Chor", "Pay", "Ju", "Sha"],
+ daysMin: ["Ya", "Du", "Se", "Cho", "Pa", "Ju", "Sha"],
+ months: ["Yanvar", "Fevral", "Mart", "Aprel", "May", "Iyun", "Iyul", "Avgust", "Sentabr", "Oktabr", "Noyabr", "Dekabr"],
+ monthsShort: ["Yan", "Fev", "Mar", "Apr", "May", "Iyn", "Iyl", "Avg", "Sen", "Okt", "Noy", "Dek"],
+ today: "Bugun",
+ clear: "O'chirish",
+ format: "dd.mm.yyyy",
+ weekStart: 1,
+ monthsTitle: 'Oylar'
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/vi.js b/src/ui/static/js/datepicker/i18n/locales/vi.js
new file mode 100644
index 000000000..ffb8da93d
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/vi.js
@@ -0,0 +1,16 @@
+/**
+ * Vietnamese translation for bootstrap-datepicker
+ * An Vo
+ */
+export default {
+ vi: {
+ days: ["Chủ nhật", "Thứ hai", "Thứ ba", "Thứ tư", "Thứ năm", "Thứ sáu", "Thứ bảy"],
+ daysShort: ["CN", "Thứ 2", "Thứ 3", "Thứ 4", "Thứ 5", "Thứ 6", "Thứ 7"],
+ daysMin: ["CN", "T2", "T3", "T4", "T5", "T6", "T7"],
+ months: ["Tháng 1", "Tháng 2", "Tháng 3", "Tháng 4", "Tháng 5", "Tháng 6", "Tháng 7", "Tháng 8", "Tháng 9", "Tháng 10", "Tháng 11", "Tháng 12"],
+ monthsShort: ["Th1", "Th2", "Th3", "Th4", "Th5", "Th6", "Th7", "Th8", "Th9", "Th10", "Th11", "Th12"],
+ today: "Hôm nay",
+ clear: "Xóa",
+ format: "dd/mm/yyyy"
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/zh-CN.js b/src/ui/static/js/datepicker/i18n/locales/zh-CN.js
new file mode 100644
index 000000000..4aadcd14b
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/zh-CN.js
@@ -0,0 +1,19 @@
+/**
+ * Simplified Chinese translation for bootstrap-datepicker
+ * Yuan Cheung
+ */
+export default {
+ 'zh-CN': {
+ days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
+ daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
+ daysMin: ["日", "一", "二", "三", "四", "五", "六"],
+ months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
+ monthsShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
+ today: "今天",
+ monthsTitle: "选择月份",
+ clear: "清除",
+ format: "yyyy-mm-dd",
+ titleFormat: "y年mm月",
+ weekStart: 1
+ }
+};
diff --git a/src/ui/static/js/datepicker/i18n/locales/zh-TW.js b/src/ui/static/js/datepicker/i18n/locales/zh-TW.js
new file mode 100644
index 000000000..762334b16
--- /dev/null
+++ b/src/ui/static/js/datepicker/i18n/locales/zh-TW.js
@@ -0,0 +1,20 @@
+/**
+ * Traditional Chinese translation for bootstrap-datepicker
+ * Rung-Sheng Jang
+ * FrankWu Fix more appropriate use of Traditional Chinese habit
+ */
+export default {
+ 'zh-TW': {
+ days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
+ daysShort: ["週日", "週一", "週二", "週三", "週四", "週五", "週六"],
+ daysMin: ["日", "一", "二", "三", "四", "五", "六"],
+ months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
+ monthsShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
+ today: "今天",
+ monthsTitle: "月份",
+ format: "yyyy/mm/dd",
+ weekStart: 0,
+ titleFormat: "y年mm月",
+ clear: "清除"
+ }
+};
diff --git a/src/ui/static/js/datepicker/lib/date-format.js b/src/ui/static/js/datepicker/lib/date-format.js
new file mode 100644
index 000000000..09ef250be
--- /dev/null
+++ b/src/ui/static/js/datepicker/lib/date-format.js
@@ -0,0 +1,185 @@
+import {stripTime, today} from './date.js';
+import {lastItemOf} from './utils.js';
+
+// pattern for format parts
+export const reFormatTokens = /dd?|DD?|mm?|MM?|yy?(?:yy)?/;
+// pattern for non date parts
+export const reNonDateParts = /[\s!-/:-@[-`{-~年月日]+/;
+// cache for persed formats
+let knownFormats = {};
+// parse funtions for date parts
+const parseFns = {
+ y(date, year) {
+ return new Date(date).setFullYear(parseInt(year, 10));
+ },
+ m(date, month, locale) {
+ const newDate = new Date(date);
+ let monthIndex = parseInt(month, 10) - 1;
+
+ if (isNaN(monthIndex)) {
+ if (!month) {
+ return NaN;
+ }
+
+ const monthName = month.toLowerCase();
+ const compareNames = name => name.toLowerCase().startsWith(monthName);
+ // compare with both short and full names because some locales have periods
+ // in the short names (not equal to the first X letters of the full names)
+ monthIndex = locale.monthsShort.findIndex(compareNames);
+ if (monthIndex < 0) {
+ monthIndex = locale.months.findIndex(compareNames);
+ }
+ if (monthIndex < 0) {
+ return NaN;
+ }
+ }
+
+ newDate.setMonth(monthIndex);
+ return newDate.getMonth() !== normalizeMonth(monthIndex)
+ ? newDate.setDate(0)
+ : newDate.getTime();
+ },
+ d(date, day) {
+ return new Date(date).setDate(parseInt(day, 10));
+ },
+};
+// format functions for date parts
+const formatFns = {
+ d(date) {
+ return date.getDate();
+ },
+ dd(date) {
+ return padZero(date.getDate(), 2);
+ },
+ D(date, locale) {
+ return locale.daysShort[date.getDay()];
+ },
+ DD(date, locale) {
+ return locale.days[date.getDay()];
+ },
+ m(date) {
+ return date.getMonth() + 1;
+ },
+ mm(date) {
+ return padZero(date.getMonth() + 1, 2);
+ },
+ M(date, locale) {
+ return locale.monthsShort[date.getMonth()];
+ },
+ MM(date, locale) {
+ return locale.months[date.getMonth()];
+ },
+ y(date) {
+ return date.getFullYear();
+ },
+ yy(date) {
+ return padZero(date.getFullYear(), 2).slice(-2);
+ },
+ yyyy(date) {
+ return padZero(date.getFullYear(), 4);
+ },
+};
+
+// get month index in normal range (0 - 11) from any number
+function normalizeMonth(monthIndex) {
+ return monthIndex > -1 ? monthIndex % 12 : normalizeMonth(monthIndex + 12);
+}
+
+function padZero(num, length) {
+ return num.toString().padStart(length, '0');
+}
+
+function parseFormatString(format) {
+ if (typeof format !== 'string') {
+ throw new Error("Invalid date format.");
+ }
+ if (format in knownFormats) {
+ return knownFormats[format];
+ }
+
+ // sprit the format string into parts and seprators
+ const separators = format.split(reFormatTokens);
+ const parts = format.match(new RegExp(reFormatTokens, 'g'));
+ if (separators.length === 0 || !parts) {
+ throw new Error("Invalid date format.");
+ }
+
+ // collect format functions used in the format
+ const partFormatters = parts.map(token => formatFns[token]);
+
+ // collect parse function keys used in the format
+ // iterate over parseFns' keys in order to keep the order of the keys.
+ const partParserKeys = Object.keys(parseFns).reduce((keys, key) => {
+ const token = parts.find(part => part[0] !== 'D' && part[0].toLowerCase() === key);
+ if (token) {
+ keys.push(key);
+ }
+ return keys;
+ }, []);
+
+ return knownFormats[format] = {
+ parser(dateStr, locale) {
+ const dateParts = dateStr.split(reNonDateParts).reduce((dtParts, part, index) => {
+ if (part.length > 0 && parts[index]) {
+ const token = parts[index][0];
+ if (token === 'M') {
+ dtParts.m = part;
+ } else if (token !== 'D') {
+ dtParts[token] = part;
+ }
+ }
+ return dtParts;
+ }, {});
+
+ // iterate over partParserkeys so that the parsing is made in the oder
+ // of year, month and day to prevent the day parser from correcting last
+ // day of month wrongly
+ return partParserKeys.reduce((origDate, key) => {
+ const newDate = parseFns[key](origDate, dateParts[key], locale);
+ // ingnore the part failed to parse
+ return isNaN(newDate) ? origDate : newDate;
+ }, today());
+ },
+ formatter(date, locale) {
+ let dateStr = partFormatters.reduce((str, fn, index) => {
+ return str += `${separators[index]}${fn(date, locale)}`;
+ }, '');
+ // separators' length is always parts' length + 1,
+ return dateStr += lastItemOf(separators);
+ },
+ };
+}
+
+export function parseDate(dateStr, format, locale) {
+ if (dateStr instanceof Date || typeof dateStr === 'number') {
+ const date = stripTime(dateStr);
+ return isNaN(date) ? undefined : date;
+ }
+ if (!dateStr) {
+ return undefined;
+ }
+ if (dateStr === 'today') {
+ return today();
+ }
+
+ if (format && format.toValue) {
+ const date = format.toValue(dateStr, format, locale);
+ return isNaN(date) ? undefined : stripTime(date);
+ }
+
+ return parseFormatString(format).parser(dateStr, locale);
+}
+
+export function formatDate(date, format, locale) {
+ if (isNaN(date) || (!date && date !== 0)) {
+ return '';
+ }
+
+ const dateObj = typeof date === 'number' ? new Date(date) : date;
+
+ if (format.toDisplay) {
+ return format.toDisplay(dateObj, format, locale);
+ }
+
+ return parseFormatString(format).formatter(dateObj, locale);
+}
diff --git a/src/ui/static/js/datepicker/lib/date.js b/src/ui/static/js/datepicker/lib/date.js
new file mode 100644
index 000000000..4282901c2
--- /dev/null
+++ b/src/ui/static/js/datepicker/lib/date.js
@@ -0,0 +1,100 @@
+export function stripTime(timeValue) {
+ return new Date(timeValue).setHours(0, 0, 0, 0);
+}
+
+export function today() {
+ return new Date().setHours(0, 0, 0, 0);
+}
+
+// Get the time value of the start of given date or year, month and day
+export function dateValue(...args) {
+ switch (args.length) {
+ case 0:
+ return today();
+ case 1:
+ return stripTime(args[0]);
+ }
+
+ // use setFullYear() to keep 2-digit year from being mapped to 1900-1999
+ const newDate = new Date(0);
+ newDate.setFullYear(...args);
+ return newDate.setHours(0, 0, 0, 0);
+}
+
+export function addDays(date, amount) {
+ const newDate = new Date(date);
+ return newDate.setDate(newDate.getDate() + amount);
+}
+
+export function addWeeks(date, amount) {
+ return addDays(date, amount * 7);
+}
+
+export function addMonths(date, amount) {
+ // If the day of the date is not in the new month, the last day of the new
+ // month will be returned. e.g. Jan 31 + 1 month → Feb 28 (not Mar 03)
+ const newDate = new Date(date);
+ const monthsToSet = newDate.getMonth() + amount;
+ let expectedMonth = monthsToSet % 12;
+ if (expectedMonth < 0) {
+ expectedMonth += 12;
+ }
+
+ const time = newDate.setMonth(monthsToSet);
+ return newDate.getMonth() !== expectedMonth ? newDate.setDate(0) : time;
+}
+
+export function addYears(date, amount) {
+ // If the date is Feb 29 and the new year is not a leap year, Feb 28 of the
+ // new year will be returned.
+ const newDate = new Date(date);
+ const expectedMonth = newDate.getMonth();
+ const time = newDate.setFullYear(newDate.getFullYear() + amount);
+ return expectedMonth === 1 && newDate.getMonth() === 2 ? newDate.setDate(0) : time;
+}
+
+// Calculate the distance bettwen 2 days of the week
+function dayDiff(day, from) {
+ return (day - from + 7) % 7;
+}
+
+// Get the date of the specified day of the week of given base date
+export function dayOfTheWeekOf(baseDate, dayOfWeek, weekStart = 0) {
+ const baseDay = new Date(baseDate).getDay();
+ return addDays(baseDate, dayDiff(dayOfWeek, weekStart) - dayDiff(baseDay, weekStart));
+}
+
+// Get the ISO week of a date
+export function getWeek(date) {
+ // start of ISO week is Monday
+ const thuOfTheWeek = dayOfTheWeekOf(date, 4, 1);
+ // 1st week == the week where the 4th of January is in
+ const firstThu = dayOfTheWeekOf(new Date(thuOfTheWeek).setMonth(0, 4), 4, 1);
+ return Math.round((thuOfTheWeek - firstThu) / 604800000) + 1;
+}
+
+// Get the start year of the period of years that includes given date
+// years: length of the year period
+export function startOfYearPeriod(date, years) {
+ /* @see https://en.wikipedia.org/wiki/Year_zero#ISO_8601 */
+ const year = new Date(date).getFullYear();
+ return Math.floor(year / years) * years;
+}
+
+// Convert date to the first/last date of the month/year of the date
+export function regularizeDate(date, timeSpan, useLastDate) {
+ if (timeSpan !== 1 && timeSpan !== 2) {
+ return date;
+ }
+ const newDate = new Date(date);
+ if (timeSpan === 1) {
+ useLastDate
+ ? newDate.setMonth(newDate.getMonth() + 1, 0)
+ : newDate.setDate(1);
+ } else {
+ useLastDate
+ ? newDate.setFullYear(newDate.getFullYear() + 1, 0, 0)
+ : newDate.setMonth(0, 1);
+ }
+ return newDate.setHours(0, 0, 0, 0);
+}
diff --git a/src/ui/static/js/datepicker/lib/dom.js b/src/ui/static/js/datepicker/lib/dom.js
new file mode 100644
index 000000000..c32fe7515
--- /dev/null
+++ b/src/ui/static/js/datepicker/lib/dom.js
@@ -0,0 +1,63 @@
+const range = document.createRange();
+
+export function parseHTML(html) {
+ return range.createContextualFragment(html);
+}
+
+export function getParent(el) {
+ return el.parentElement
+ || (el.parentNode instanceof ShadowRoot ? el.parentNode.host : undefined);
+}
+
+export function isActiveElement(el) {
+ return el.getRootNode().activeElement === el;
+}
+
+// equivalent to jQuery's :visble
+export function isVisible(el) {
+ return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
+}
+
+export function hideElement(el) {
+ if (el.style.display === 'none') {
+ return;
+ }
+ // back up the existing display setting in data-style-display
+ if (el.style.display) {
+ el.dataset.styleDisplay = el.style.display;
+ }
+ el.style.display = 'none';
+}
+
+export function showElement(el) {
+ if (el.style.display !== 'none') {
+ return;
+ }
+ if (el.dataset.styleDisplay) {
+ // restore backed-up dispay property
+ el.style.display = el.dataset.styleDisplay;
+ delete el.dataset.styleDisplay;
+ } else {
+ el.style.display = '';
+ }
+}
+
+export function emptyChildNodes(el) {
+ if (el.firstChild) {
+ el.removeChild(el.firstChild);
+ emptyChildNodes(el);
+ }
+}
+
+export function replaceChildNodes(el, newChildNodes) {
+ emptyChildNodes(el);
+ if (newChildNodes instanceof DocumentFragment) {
+ el.appendChild(newChildNodes);
+ } else if (typeof newChildNodes === 'string') {
+ el.appendChild(parseHTML(newChildNodes));
+ } else if (typeof newChildNodes.forEach === 'function') {
+ newChildNodes.forEach((node) => {
+ el.appendChild(node);
+ });
+ }
+}
diff --git a/src/ui/static/js/datepicker/lib/event.js b/src/ui/static/js/datepicker/lib/event.js
new file mode 100644
index 000000000..e2922644a
--- /dev/null
+++ b/src/ui/static/js/datepicker/lib/event.js
@@ -0,0 +1,71 @@
+const listenerRegistry = new WeakMap();
+const {addEventListener, removeEventListener} = EventTarget.prototype;
+
+// Register event listeners to a key object
+// listeners: array of listener definitions;
+// - each definition must be a flat array of event target and the arguments
+// used to call addEventListener() on the target
+export function registerListeners(keyObj, listeners) {
+ let registered = listenerRegistry.get(keyObj);
+ if (!registered) {
+ registered = [];
+ listenerRegistry.set(keyObj, registered);
+ }
+ listeners.forEach((listener) => {
+ addEventListener.call(...listener);
+ registered.push(listener);
+ });
+}
+
+export function unregisterListeners(keyObj) {
+ let listeners = listenerRegistry.get(keyObj);
+ if (!listeners) {
+ return;
+ }
+ listeners.forEach((listener) => {
+ removeEventListener.call(...listener);
+ });
+ listenerRegistry.delete(keyObj);
+}
+
+// Event.composedPath() polyfill for Edge
+// based on https://gist.github.com/kleinfreund/e9787d73776c0e3750dcfcdc89f100ec
+if (!Event.prototype.composedPath) {
+ const getComposedPath = (node, path = []) => {
+ path.push(node);
+
+ let parent;
+ if (node.parentNode) {
+ parent = node.parentNode;
+ } else if (node.host) { // ShadowRoot
+ parent = node.host;
+ } else if (node.defaultView) { // Document
+ parent = node.defaultView;
+ }
+ return parent ? getComposedPath(parent, path) : path;
+ };
+
+ Event.prototype.composedPath = function () {
+ return getComposedPath(this.target);
+ };
+}
+
+function findFromPath(path, criteria, currentTarget) {
+ const [node, ...rest] = path;
+ if (criteria(node)) {
+ return node;
+ }
+ if (node === currentTarget || node.tagName === 'HTML' || rest.length === 0) {
+ // stop when reaching currentTarget or
+ return;
+ }
+ return findFromPath(rest, criteria, currentTarget);
+}
+
+// Search for the actual target of a delegated event
+export function findElementInEventPath(ev, selector) {
+ const criteria = typeof selector === 'function'
+ ? selector
+ : el => el instanceof Element && el.matches(selector);
+ return findFromPath(ev.composedPath(), criteria, ev.currentTarget);
+}
diff --git a/src/ui/static/js/datepicker/lib/utils.js b/src/ui/static/js/datepicker/lib/utils.js
new file mode 100644
index 000000000..5ad8a796c
--- /dev/null
+++ b/src/ui/static/js/datepicker/lib/utils.js
@@ -0,0 +1,61 @@
+export function hasProperty(obj, prop) {
+ return Object.prototype.hasOwnProperty.call(obj, prop);
+}
+
+export function lastItemOf(arr) {
+ return arr[arr.length - 1];
+}
+
+// push only the items not included in the array
+export function pushUnique(arr, ...items) {
+ items.forEach((item) => {
+ if (arr.includes(item)) {
+ return;
+ }
+ arr.push(item);
+ });
+ return arr;
+}
+
+export function stringToArray(str, separator) {
+ // convert empty string to an empty array
+ return str ? str.split(separator) : [];
+}
+
+export function isInRange(testVal, min, max) {
+ const minOK = min === undefined || testVal >= min;
+ const maxOK = max === undefined || testVal <= max;
+ return minOK && maxOK;
+}
+
+export function limitToRange(val, min, max) {
+ if (val < min) {
+ return min;
+ }
+ if (val > max) {
+ return max;
+ }
+ return val;
+}
+
+export function createTagRepeat(tagName, repeat, attributes = {}, index = 0, html = '') {
+ const openTagSrc = Object.keys(attributes).reduce((src, attr) => {
+ let val = attributes[attr];
+ if (typeof val === 'function') {
+ val = val(index);
+ }
+ return `${src} ${attr}="${val}"`;
+ }, tagName);
+ html += `<${openTagSrc}>${tagName}>`;
+
+ const next = index + 1;
+ return next < repeat
+ ? createTagRepeat(tagName, repeat, attributes, next, html)
+ : html;
+}
+
+// Remove the spacing surrounding tags for HTML parser not to create text nodes
+// before/after elements
+export function optimizeTemplateHTML(html) {
+ return html.replace(/>\s+/g, '>').replace(/\s+, '<');
+}
diff --git a/src/ui/static/js/datepicker/locales/ar-DZ.js b/src/ui/static/js/datepicker/locales/ar-DZ.js
new file mode 100644
index 000000000..dd7c8ddcb
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/ar-DZ.js
@@ -0,0 +1,19 @@
+/**
+ * Arabic-Algeria translation for bootstrap-datepicker
+ * Rabah Saadi
+ */
+(function () {
+ Datepicker.locales['ar-DZ'] = {
+ days: ["الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت", "الأحد"],
+ daysShort: ["أحد", "اثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت", "أحد"],
+ daysMin: ["ح", "ن", "ث", "ع", "خ", "ج", "س", "ح"],
+ months: ["جانفي","فيفري","مارس","أفريل","ماي","جوان","جويليه","أوت","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],
+ monthsShort: ["جانفي","فيفري","مارس","أفريل","ماي","جوان","جويليه","أوت","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],
+ today: "هذا اليوم",
+ rtl: true,
+ monthsTitle: "أشهر",
+ clear: "إزالة",
+ format: "yyyy/mm/dd",
+ weekStart: 0
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/ar-tn.js b/src/ui/static/js/datepicker/locales/ar-tn.js
new file mode 100644
index 000000000..d94df17a9
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/ar-tn.js
@@ -0,0 +1,15 @@
+/**
+ * Arabic-Tunisia translation for bootstrap-datepicker
+ * Souhaieb Besbes
+ */
+(function () {
+ Datepicker.locales['ar-tn'] = {
+ days: ["الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت", "الأحد"],
+ daysShort: ["أحد", "اثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت", "أحد"],
+ daysMin: ["ح", "ن", "ث", "ع", "خ", "ج", "س", "ح"],
+ months: ["جانفي","فيفري","مارس","أفريل","ماي","جوان","جويليه","أوت","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],
+ monthsShort: ["جانفي","فيفري","مارس","أفريل","ماي","جوان","جويليه","أوت","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],
+ today: "هذا اليوم",
+ rtl: true
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/ar.js b/src/ui/static/js/datepicker/locales/ar.js
new file mode 100644
index 000000000..608fe0d3e
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/ar.js
@@ -0,0 +1,15 @@
+/**
+ * Arabic translation for bootstrap-datepicker
+ * Mohammed Alshehri
+ */
+(function () {
+ Datepicker.locales.ar = {
+ days: ["الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت", "الأحد"],
+ daysShort: ["أحد", "اثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت", "أحد"],
+ daysMin: ["ح", "ن", "ث", "ع", "خ", "ج", "س", "ح"],
+ months: ["يناير", "فبراير", "مارس", "أبريل", "مايو", "يونيو", "يوليو", "أغسطس", "سبتمبر", "أكتوبر", "نوفمبر", "ديسمبر"],
+ monthsShort: ["يناير", "فبراير", "مارس", "أبريل", "مايو", "يونيو", "يوليو", "أغسطس", "سبتمبر", "أكتوبر", "نوفمبر", "ديسمبر"],
+ today: "هذا اليوم",
+ rtl: true
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/az.js b/src/ui/static/js/datepicker/locales/az.js
new file mode 100644
index 000000000..ae3d4def8
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/az.js
@@ -0,0 +1,14 @@
+// Azerbaijani
+(function () {
+ Datepicker.locales.az = {
+ days: ["Bazar", "Bazar ertəsi", "Çərşənbə axşamı", "Çərşənbə", "Cümə axşamı", "Cümə", "Şənbə"],
+ daysShort: ["B.", "B.e", "Ç.a", "Ç.", "C.a", "C.", "Ş."],
+ daysMin: ["B.", "B.e", "Ç.a", "Ç.", "C.a", "C.", "Ş."],
+ months: ["Yanvar", "Fevral", "Mart", "Aprel", "May", "İyun", "İyul", "Avqust", "Sentyabr", "Oktyabr", "Noyabr", "Dekabr"],
+ monthsShort: ["Yan", "Fev", "Mar", "Apr", "May", "İyun", "İyul", "Avq", "Sen", "Okt", "Noy", "Dek"],
+ today: "Bu gün",
+ weekStart: 1,
+ clear: "Təmizlə",
+ monthsTitle: 'Aylar'
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/bg.js b/src/ui/static/js/datepicker/locales/bg.js
new file mode 100644
index 000000000..81d649efd
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/bg.js
@@ -0,0 +1,14 @@
+/**
+ * Bulgarian translation for bootstrap-datepicker
+ * Apostol Apostolov
+ */
+(function () {
+ Datepicker.locales.bg = {
+ days: ["Неделя", "Понеделник", "Вторник", "Сряда", "Четвъртък", "Петък", "Събота"],
+ daysShort: ["Нед", "Пон", "Вто", "Сря", "Чет", "Пет", "Съб"],
+ daysMin: ["Н", "П", "В", "С", "Ч", "П", "С"],
+ months: ["Януари", "Февруари", "Март", "Април", "Май", "Юни", "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември"],
+ monthsShort: ["Ян", "Фев", "Мар", "Апр", "Май", "Юни", "Юли", "Авг", "Сеп", "Окт", "Ное", "Дек"],
+ today: "днес"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/bm.js b/src/ui/static/js/datepicker/locales/bm.js
new file mode 100644
index 000000000..57550b8fb
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/bm.js
@@ -0,0 +1,18 @@
+/**
+ * Bamanankan (bm) translation for bootstrap-datepicker
+ * Fatou Fall
+ */
+(function () {
+ Datepicker.locales.bm = {
+ days: ["Kari","Ntɛnɛn","Tarata","Araba","Alamisa","Juma","Sibiri"],
+ daysShort: ["Kar","Ntɛ","Tar","Ara","Ala","Jum","Sib"],
+ daysMin: ["Ka","Nt","Ta","Ar","Al","Ju","Si"],
+ months: ["Zanwuyekalo","Fewuruyekalo","Marisikalo","Awirilikalo","Mɛkalo","Zuwɛnkalo","Zuluyekalo","Utikalo","Sɛtanburukalo","ɔkutɔburukalo","Nowanburukalo","Desanburukalo"],
+ monthsShort: ["Zan","Few","Mar","Awi","Mɛ","Zuw","Zul","Uti","Sɛt","ɔku","Now","Des"],
+ today: "Bi",
+ monthsTitle: "Kalo",
+ clear: "Ka jɔsi",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/bn.js b/src/ui/static/js/datepicker/locales/bn.js
new file mode 100644
index 000000000..6143362af
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/bn.js
@@ -0,0 +1,19 @@
+/**
+ * Bengali (Bangla) translation for bootstrap-datepicker
+ * Karim Khan
+ * Orif N. Jr.
+ */
+(function () {
+ Datepicker.locales.bn = {
+ days: ["রবিবার","সোমবার","মঙ্গলবার","বুধবার","বৃহস্পতিবার","শুক্রবার","শনিবার"],
+ daysShort: ["রবিবার","সোমবার","মঙ্গলবার","বুধবার","বৃহস্পতিবার","শুক্রবার","শনিবার"],
+ daysMin: ["রবি","সোম","মঙ্গল","বুধ","বৃহস্পতি","শুক্র","শনি"],
+ months: ["জানুয়ারী","ফেব্রুয়ারি","মার্চ","এপ্রিল","মে","জুন","জুলাই","অগাস্ট","সেপ্টেম্বর","অক্টোবর","নভেম্বর","ডিসেম্বর"],
+ monthsShort: ["জানুয়ারী","ফেব্রুয়ারি","মার্চ","এপ্রিল","মে","জুন","জুলাই","অগাস্ট","সেপ্টেম্বর","অক্টোবর","নভেম্বর","ডিসেম্বর"],
+ today: "আজ",
+ monthsTitle: "মাস",
+ clear: "পরিষ্কার",
+ weekStart: 0,
+ format: "mm/dd/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/br.js b/src/ui/static/js/datepicker/locales/br.js
new file mode 100644
index 000000000..f6419a589
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/br.js
@@ -0,0 +1,18 @@
+/**
+ * Breton translation for bootstrap-datepicker
+ * Gwenn Meynier
+ */
+(function () {
+ Datepicker.locales.br = {
+ days: ["Sul", "Lun", "Meurzh", "Merc'her", "Yaou", "Gwener", "Sadorn"],
+ daysShort: ["Sul", "Lun", "Meu.", "Mer.", "Yao.", "Gwe.", "Sad."],
+ daysMin: ["Su", "L", "Meu", "Mer", "Y", "G", "Sa"],
+ months: ["Genver", "C'hwevrer", "Meurzh", "Ebrel", "Mae", "Mezheven", "Gouere", "Eost", "Gwengolo", "Here", "Du", "Kerzu"],
+ monthsShort: ["Genv.", "C'hw.", "Meur.", "Ebre.", "Mae", "Mezh.", "Goue.", "Eost", "Gwen.", "Here", "Du", "Kerz."],
+ today: "Hiziv",
+ monthsTitle: "Miz",
+ clear: "Dilemel",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/bs.js b/src/ui/static/js/datepicker/locales/bs.js
new file mode 100644
index 000000000..306f163d3
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/bs.js
@@ -0,0 +1,15 @@
+/**
+ * Bosnian translation for bootstrap-datepicker
+ */
+(function () {
+ Datepicker.locales.bs = {
+ days: ["Nedjelja","Ponedjeljak", "Utorak", "Srijeda", "Četvrtak", "Petak", "Subota"],
+ daysShort: ["Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub"],
+ daysMin: ["N", "Po", "U", "Sr", "Č", "Pe", "Su"],
+ months: ["Januar", "Februar", "Mart", "April", "Maj", "Juni", "Juli", "August", "Septembar", "Oktobar", "Novembar", "Decembar"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"],
+ today: "Danas",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/ca.js b/src/ui/static/js/datepicker/locales/ca.js
new file mode 100644
index 000000000..20c30beac
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/ca.js
@@ -0,0 +1,18 @@
+/**
+ * Catalan translation for bootstrap-datepicker
+ * J. Garcia
+ */
+(function () {
+ Datepicker.locales.ca = {
+ days: ["diumenge", "dilluns", "dimarts", "dimecres", "dijous", "divendres", "dissabte"],
+ daysShort: ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+ daysMin: ["dg", "dl", "dt", "dc", "dj", "dv", "ds"],
+ months: ["gener", "febrer", "març", "abril", "maig", "juny", "juliol", "agost", "setembre", "octubre", "novembre", "desembre"],
+ monthsShort: ["gen.", "febr.", "març", "abr.", "maig", "juny", "jul.", "ag.", "set.", "oct.", "nov.", "des."],
+ today: "Avui",
+ monthsTitle: "Mesos",
+ clear: "Esborra",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/cs.js b/src/ui/static/js/datepicker/locales/cs.js
new file mode 100644
index 000000000..7531dda83
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/cs.js
@@ -0,0 +1,19 @@
+/**
+ * Czech translation for bootstrap-datepicker
+ * Matěj Koubík
+ * Fixes by Michal Remiš
+ */
+(function () {
+ Datepicker.locales.cs = {
+ days: ["Neděle", "Pondělí", "Úterý", "Středa", "Čtvrtek", "Pátek", "Sobota"],
+ daysShort: ["Ned", "Pon", "Úte", "Stř", "Čtv", "Pát", "Sob"],
+ daysMin: ["Ne", "Po", "Út", "St", "Čt", "Pá", "So"],
+ months: ["Leden", "Únor", "Březen", "Duben", "Květen", "Červen", "Červenec", "Srpen", "Září", "Říjen", "Listopad", "Prosinec"],
+ monthsShort: ["Led", "Úno", "Bře", "Dub", "Kvě", "Čer", "Čnc", "Srp", "Zář", "Říj", "Lis", "Pro"],
+ today: "Dnes",
+ clear: "Vymazat",
+ monthsTitle: "Měsíc",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/cy.js b/src/ui/static/js/datepicker/locales/cy.js
new file mode 100644
index 000000000..7c609992f
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/cy.js
@@ -0,0 +1,14 @@
+/**
+ * Welsh translation for bootstrap-datepicker
+ * S. Morris
+ */
+(function () {
+ Datepicker.locales.cy = {
+ days: ["Sul", "Llun", "Mawrth", "Mercher", "Iau", "Gwener", "Sadwrn"],
+ daysShort: ["Sul", "Llu", "Maw", "Mer", "Iau", "Gwe", "Sad"],
+ daysMin: ["Su", "Ll", "Ma", "Me", "Ia", "Gwe", "Sa"],
+ months: ["Ionawr", "Chewfror", "Mawrth", "Ebrill", "Mai", "Mehefin", "Gorfennaf", "Awst", "Medi", "Hydref", "Tachwedd", "Rhagfyr"],
+ monthsShort: ["Ion", "Chw", "Maw", "Ebr", "Mai", "Meh", "Gor", "Aws", "Med", "Hyd", "Tach", "Rha"],
+ today: "Heddiw"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/da.js b/src/ui/static/js/datepicker/locales/da.js
new file mode 100644
index 000000000..fb7a18961
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/da.js
@@ -0,0 +1,19 @@
+/**
+ * Danish translation for bootstrap-datepicker
+ * Christian Pedersen
+ * Ivan Mylyanyk
+ */
+(function () {
+ Datepicker.locales.da = {
+ days: ["Søndag", "Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "Lørdag"],
+ daysShort: ["Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør"],
+ daysMin: ["Sø", "Ma", "Ti", "On", "To", "Fr", "Lø"],
+ months: ["Januar", "Februar", "Marts", "April", "Maj", "Juni", "Juli", "August", "September", "Oktober", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"],
+ today: "I Dag",
+ weekStart: 1,
+ clear: "Nulstil",
+ format: "dd/mm/yyyy",
+ monthsTitle: "Måneder"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/de.js b/src/ui/static/js/datepicker/locales/de.js
new file mode 100644
index 000000000..60c87be51
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/de.js
@@ -0,0 +1,18 @@
+/**
+ * German translation for bootstrap-datepicker
+ * Sam Zurcher
+ */
+(function () {
+ Datepicker.locales.de = {
+ days: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"],
+ daysShort: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
+ daysMin: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
+ months: ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"],
+ monthsShort: ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"],
+ today: "Heute",
+ monthsTitle: "Monate",
+ clear: "Löschen",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/el.js b/src/ui/static/js/datepicker/locales/el.js
new file mode 100644
index 000000000..a65a21dc6
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/el.js
@@ -0,0 +1,16 @@
+/**
+ * Greek translation for bootstrap-datepicker
+ */
+(function () {
+ Datepicker.locales.el = {
+ days: ["Κυριακή", "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο"],
+ daysShort: ["Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ"],
+ daysMin: ["Κυ", "Δε", "Τρ", "Τε", "Πε", "Πα", "Σα"],
+ months: ["Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος"],
+ monthsShort: ["Ιαν", "Φεβ", "Μαρ", "Απρ", "Μάι", "Ιουν", "Ιουλ", "Αυγ", "Σεπ", "Οκτ", "Νοε", "Δεκ"],
+ today: "Σήμερα",
+ clear: "Καθαρισμός",
+ weekStart: 1,
+ format: "d/m/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/en-AU.js b/src/ui/static/js/datepicker/locales/en-AU.js
new file mode 100644
index 000000000..6d8455619
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/en-AU.js
@@ -0,0 +1,18 @@
+/**
+ * Australian English translation for bootstrap-datepicker
+ * Steve Chapman
+ */
+(function () {
+ Datepicker.locales['en-AU'] = {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today",
+ monthsTitle: "Months",
+ clear: "Clear",
+ weekStart: 1,
+ format: "d/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/en-CA.js b/src/ui/static/js/datepicker/locales/en-CA.js
new file mode 100644
index 000000000..3a35c32d2
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/en-CA.js
@@ -0,0 +1,18 @@
+/**
+ * Canadian English translation for bootstrap-datepicker
+ * Mike Nacey
+ */
+(function () {
+ Datepicker.locales['en-CA'] = {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today",
+ monthsTitle: "Months",
+ clear: "Clear",
+ weekStart: 0,
+ format: "yyyy-mm-dd"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/en-GB.js b/src/ui/static/js/datepicker/locales/en-GB.js
new file mode 100644
index 000000000..da848f97f
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/en-GB.js
@@ -0,0 +1,18 @@
+/**
+ * British English translation for bootstrap-datepicker
+ * Xavier Dutreilh
+ */
+(function () {
+ Datepicker.locales['en-GB'] = {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today",
+ monthsTitle: "Months",
+ clear: "Clear",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/en-IE.js b/src/ui/static/js/datepicker/locales/en-IE.js
new file mode 100644
index 000000000..1a41302cf
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/en-IE.js
@@ -0,0 +1,17 @@
+/**
+ * Irish English translation for bootstrap-datepicker
+ */
+(function () {
+ Datepicker.locales['en-IE'] = {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today",
+ monthsTitle: "Months",
+ clear: "Clear",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/en-NZ.js b/src/ui/static/js/datepicker/locales/en-NZ.js
new file mode 100644
index 000000000..dd34ac41d
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/en-NZ.js
@@ -0,0 +1,17 @@
+/**
+ * New Zealand English translation for bootstrap-datepicker
+ */
+(function () {
+ Datepicker.locales['en-NZ'] = {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today",
+ monthsTitle: "Months",
+ clear: "Clear",
+ weekStart: 1,
+ format: "d/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/en-ZA.js b/src/ui/static/js/datepicker/locales/en-ZA.js
new file mode 100644
index 000000000..6455097d1
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/en-ZA.js
@@ -0,0 +1,17 @@
+/**
+ * South African English translation for bootstrap-datepicker
+ */
+(function () {
+ Datepicker.locales['en-ZA'] = {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today",
+ monthsTitle: "Months",
+ clear: "Clear",
+ weekStart: 1,
+ format: "yyyy/mm/d"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/eo.js b/src/ui/static/js/datepicker/locales/eo.js
new file mode 100644
index 000000000..f68306559
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/eo.js
@@ -0,0 +1,17 @@
+/**
+ * Esperanto translation for bootstrap-datepicker
+ * Emmanuel Debanne
+ */
+(function () {
+ Datepicker.locales.eo = {
+ days: ["dimanĉo", "lundo", "mardo", "merkredo", "ĵaŭdo", "vendredo", "sabato"],
+ daysShort: ["dim.", "lun.", "mar.", "mer.", "ĵaŭ.", "ven.", "sam."],
+ daysMin: ["d", "l", "ma", "me", "ĵ", "v", "s"],
+ months: ["januaro", "februaro", "marto", "aprilo", "majo", "junio", "julio", "aŭgusto", "septembro", "oktobro", "novembro", "decembro"],
+ monthsShort: ["jan.", "feb.", "mar.", "apr.", "majo", "jun.", "jul.", "aŭg.", "sep.", "okt.", "nov.", "dec."],
+ today: "Hodiaŭ",
+ clear: "Nuligi",
+ weekStart: 1,
+ format: "yyyy-mm-dd"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/es.js b/src/ui/static/js/datepicker/locales/es.js
new file mode 100644
index 000000000..d2be3f872
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/es.js
@@ -0,0 +1,18 @@
+/**
+ * Spanish translation for bootstrap-datepicker
+ * Bruno Bonamin
+ */
+(function () {
+ Datepicker.locales.es = {
+ days: ["Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"],
+ daysShort: ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb"],
+ daysMin: ["Do", "Lu", "Ma", "Mi", "Ju", "Vi", "Sa"],
+ months: ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"],
+ monthsShort: ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"],
+ today: "Hoy",
+ monthsTitle: "Meses",
+ clear: "Borrar",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/et.js b/src/ui/static/js/datepicker/locales/et.js
new file mode 100644
index 000000000..35a71def0
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/et.js
@@ -0,0 +1,18 @@
+/**
+ * Estonian translation for bootstrap-datepicker
+ * Ando Roots
+ * Fixes by Illimar Tambek <
+ */
+(function () {
+ Datepicker.locales.et = {
+ days: ["Pühapäev", "Esmaspäev", "Teisipäev", "Kolmapäev", "Neljapäev", "Reede", "Laupäev"],
+ daysShort: ["Pühap", "Esmasp", "Teisip", "Kolmap", "Neljap", "Reede", "Laup"],
+ daysMin: ["P", "E", "T", "K", "N", "R", "L"],
+ months: ["Jaanuar", "Veebruar", "Märts", "Aprill", "Mai", "Juuni", "Juuli", "August", "September", "Oktoober", "November", "Detsember"],
+ monthsShort: ["Jaan", "Veebr", "Märts", "Apr", "Mai", "Juuni", "Juuli", "Aug", "Sept", "Okt", "Nov", "Dets"],
+ today: "Täna",
+ clear: "Tühjenda",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/eu.js b/src/ui/static/js/datepicker/locales/eu.js
new file mode 100644
index 000000000..fd664f209
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/eu.js
@@ -0,0 +1,18 @@
+/**
+ * Basque translation for bootstrap-datepicker
+ * Arkaitz Etxeberria
+ */
+(function () {
+ Datepicker.locales.eu = {
+ days: ['Igandea', 'Astelehena', 'Asteartea', 'Asteazkena', 'Osteguna', 'Ostirala', 'Larunbata'],
+ daysShort: ['Ig', 'Al', 'Ar', 'Az', 'Og', 'Ol', 'Lr'],
+ daysMin: ['Ig', 'Al', 'Ar', 'Az', 'Og', 'Ol', 'Lr'],
+ months: ['Urtarrila', 'Otsaila', 'Martxoa', 'Apirila', 'Maiatza', 'Ekaina', 'Uztaila', 'Abuztua', 'Iraila', 'Urria', 'Azaroa', 'Abendua'],
+ monthsShort: ['Urt', 'Ots', 'Mar', 'Api', 'Mai', 'Eka', 'Uzt', 'Abu', 'Ira', 'Urr', 'Aza', 'Abe'],
+ today: "Gaur",
+ monthsTitle: "Hilabeteak",
+ clear: "Ezabatu",
+ weekStart: 1,
+ format: "yyyy/mm/dd"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/fa.js b/src/ui/static/js/datepicker/locales/fa.js
new file mode 100644
index 000000000..3e1e43e83
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/fa.js
@@ -0,0 +1,17 @@
+/**
+ * Persian translation for bootstrap-datepicker
+ * Mostafa Rokooie
+ */
+(function () {
+ Datepicker.locales.fa = {
+ days: ["یکشنبه", "دوشنبه", "سهشنبه", "چهارشنبه", "پنجشنبه", "جمعه", "شنبه", "یکشنبه"],
+ daysShort: ["یک", "دو", "سه", "چهار", "پنج", "جمعه", "شنبه", "یک"],
+ daysMin: ["ی", "د", "س", "چ", "پ", "ج", "ش", "ی"],
+ months: ["ژانویه", "فوریه", "مارس", "آوریل", "مه", "ژوئن", "ژوئیه", "اوت", "سپتامبر", "اکتبر", "نوامبر", "دسامبر"],
+ monthsShort: ["ژان", "فور", "مار", "آور", "مه", "ژون", "ژوی", "اوت", "سپت", "اکت", "نوا", "دسا"],
+ today: "امروز",
+ clear: "پاک کن",
+ weekStart: 1,
+ format: "yyyy/mm/dd"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/fi.js b/src/ui/static/js/datepicker/locales/fi.js
new file mode 100644
index 000000000..bfd8cd052
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/fi.js
@@ -0,0 +1,17 @@
+/**
+ * Finnish translation for bootstrap-datepicker
+ * Jaakko Salonen
+ */
+(function () {
+ Datepicker.locales.fi = {
+ days: ["sunnuntai", "maanantai", "tiistai", "keskiviikko", "torstai", "perjantai", "lauantai"],
+ daysShort: ["sun", "maa", "tii", "kes", "tor", "per", "lau"],
+ daysMin: ["su", "ma", "ti", "ke", "to", "pe", "la"],
+ months: ["tammikuu", "helmikuu", "maaliskuu", "huhtikuu", "toukokuu", "kesäkuu", "heinäkuu", "elokuu", "syyskuu", "lokakuu", "marraskuu", "joulukuu"],
+ monthsShort: ["tammi", "helmi", "maalis", "huhti", "touko", "kesä", "heinä", "elo", "syys", "loka", "marras", "joulu"],
+ today: "tänään",
+ clear: "Tyhjennä",
+ weekStart: 1,
+ format: "d.m.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/fo.js b/src/ui/static/js/datepicker/locales/fo.js
new file mode 100644
index 000000000..18d19491b
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/fo.js
@@ -0,0 +1,15 @@
+/**
+ * Faroese translation for bootstrap-datepicker
+ * Theodor Johannesen
+ */
+(function () {
+ Datepicker.locales.fo = {
+ days: ["Sunnudagur", "Mánadagur", "Týsdagur", "Mikudagur", "Hósdagur", "Fríggjadagur", "Leygardagur"],
+ daysShort: ["Sun", "Mán", "Týs", "Mik", "Hós", "Frí", "Ley"],
+ daysMin: ["Su", "Má", "Tý", "Mi", "Hó", "Fr", "Le"],
+ months: ["Januar", "Februar", "Marts", "Apríl", "Mei", "Juni", "Juli", "August", "Septembur", "Oktobur", "Novembur", "Desembur"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Des"],
+ today: "Í Dag",
+ clear: "Reinsa"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/fr-CH.js b/src/ui/static/js/datepicker/locales/fr-CH.js
new file mode 100644
index 000000000..278a638b6
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/fr-CH.js
@@ -0,0 +1,21 @@
+/**
+ * French (Switzerland) translation for bootstrap-datepicker
+ * Christoph Jossi
+ * Based on
+ * French translation for bootstrap-datepicker
+ * Nico Mollet
+ */
+(function () {
+ Datepicker.locales['fr-CH'] = {
+ days: ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"],
+ daysShort: ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"],
+ daysMin: ["D", "L", "Ma", "Me", "J", "V", "S"],
+ months: ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"],
+ monthsShort: ["Jan", "Fév", "Mar", "Avr", "Mai", "Jui", "Jul", "Aou", "Sep", "Oct", "Nov", "Déc"],
+ today: "Aujourd'hui",
+ monthsTitle: "Mois",
+ clear: "Effacer",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/fr.js b/src/ui/static/js/datepicker/locales/fr.js
new file mode 100644
index 000000000..86323f884
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/fr.js
@@ -0,0 +1,18 @@
+/**
+ * French translation for bootstrap-datepicker
+ * Nico Mollet
+ */
+(function () {
+ Datepicker.locales.fr = {
+ days: ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"],
+ daysShort: ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+ daysMin: ["d", "l", "ma", "me", "j", "v", "s"],
+ months: ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"],
+ monthsShort: ["janv.", "févr.", "mars", "avril", "mai", "juin", "juil.", "août", "sept.", "oct.", "nov.", "déc."],
+ today: "Aujourd'hui",
+ monthsTitle: "Mois",
+ clear: "Effacer",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/gl.js b/src/ui/static/js/datepicker/locales/gl.js
new file mode 100644
index 000000000..1b52b5a4a
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/gl.js
@@ -0,0 +1,16 @@
+/**
+ * Galician translation
+ */
+(function () {
+ Datepicker.locales.gl = {
+ days: ["Domingo", "Luns", "Martes", "Mércores", "Xoves", "Venres", "Sábado"],
+ daysShort: ["Dom", "Lun", "Mar", "Mér", "Xov", "Ven", "Sáb"],
+ daysMin: ["Do", "Lu", "Ma", "Me", "Xo", "Ve", "Sa"],
+ months: ["Xaneiro", "Febreiro", "Marzo", "Abril", "Maio", "Xuño", "Xullo", "Agosto", "Setembro", "Outubro", "Novembro", "Decembro"],
+ monthsShort: ["Xan", "Feb", "Mar", "Abr", "Mai", "Xun", "Xul", "Ago", "Sep", "Out", "Nov", "Dec"],
+ today: "Hoxe",
+ clear: "Limpar",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/he.js b/src/ui/static/js/datepicker/locales/he.js
new file mode 100644
index 000000000..4c1138f7a
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/he.js
@@ -0,0 +1,15 @@
+/**
+ * Hebrew translation for bootstrap-datepicker
+ * Sagie Maoz
+ */
+(function () {
+ Datepicker.locales.he = {
+ days: ["ראשון", "שני", "שלישי", "רביעי", "חמישי", "שישי", "שבת", "ראשון"],
+ daysShort: ["א", "ב", "ג", "ד", "ה", "ו", "ש", "א"],
+ daysMin: ["א", "ב", "ג", "ד", "ה", "ו", "ש", "א"],
+ months: ["ינואר", "פברואר", "מרץ", "אפריל", "מאי", "יוני", "יולי", "אוגוסט", "ספטמבר", "אוקטובר", "נובמבר", "דצמבר"],
+ monthsShort: ["ינו", "פבר", "מרץ", "אפר", "מאי", "יונ", "יול", "אוג", "ספט", "אוק", "נוב", "דצמ"],
+ today: "היום",
+ rtl: true
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/hi.js b/src/ui/static/js/datepicker/locales/hi.js
new file mode 100644
index 000000000..48e41cc35
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/hi.js
@@ -0,0 +1,18 @@
+/**
+ * Hindi translation for bootstrap-datepicker
+ * Visar Uruqi
+ */
+(function () {
+ Datepicker.locales.hi = {
+ days: ["रविवार", "सोमवार", "मंगलवार", "बुधवार", "गुरुवार", "शुक्रवार", "शनिवार"],
+ daysShort: ["सूर्य", "सोम", "मंगल", "बुध", "गुरु", "शुक्र", "शनि"],
+ daysMin: ["र", "सो", "मं", "बु", "गु", "शु", "श"],
+ months: ["जनवरी", "फ़रवरी", "मार्च", "अप्रैल", "मई", "जून", "जुलाई", "अगस्त", "सितम्बर", "अक्टूबर", "नवंबर", "दिसम्बर"],
+ monthsShort: ["जन", "फ़रवरी", "मार्च", "अप्रैल", "मई", "जून", "जुलाई", "अगस्त", "सितं", "अक्टूबर", "नवं", "दिसम्बर"],
+ today: "आज",
+ monthsTitle: "महीने",
+ clear: "साफ",
+ weekStart: 1,
+ format: "dd / mm / yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/hr.js b/src/ui/static/js/datepicker/locales/hr.js
new file mode 100644
index 000000000..18544a0e2
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/hr.js
@@ -0,0 +1,13 @@
+/**
+ * Croatian localisation
+ */
+(function () {
+ Datepicker.locales.hr = {
+ days: ["Nedjelja", "Ponedjeljak", "Utorak", "Srijeda", "Četvrtak", "Petak", "Subota"],
+ daysShort: ["Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub"],
+ daysMin: ["Ne", "Po", "Ut", "Sr", "Če", "Pe", "Su"],
+ months: ["Siječanj", "Veljača", "Ožujak", "Travanj", "Svibanj", "Lipanj", "Srpanj", "Kolovoz", "Rujan", "Listopad", "Studeni", "Prosinac"],
+ monthsShort: ["Sij", "Velj", "Ožu", "Tra", "Svi", "Lip", "Srp", "Kol", "Ruj", "Lis", "Stu", "Pro"],
+ today: "Danas"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/hu.js b/src/ui/static/js/datepicker/locales/hu.js
new file mode 100644
index 000000000..5eaffa61d
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/hu.js
@@ -0,0 +1,18 @@
+/**
+ * Hungarian translation for bootstrap-datepicker
+ * Sotus László
+ */
+(function () {
+ Datepicker.locales.hu = {
+ days: ["vasárnap", "hétfő", "kedd", "szerda", "csütörtök", "péntek", "szombat"],
+ daysShort: ["vas", "hét", "ked", "sze", "csü", "pén", "szo"],
+ daysMin: ["V", "H", "K", "Sze", "Cs", "P", "Szo"],
+ months: ["január", "február", "március", "április", "május", "június", "július", "augusztus", "szeptember", "október", "november", "december"],
+ monthsShort: ["jan", "feb", "már", "ápr", "máj", "jún", "júl", "aug", "sze", "okt", "nov", "dec"],
+ today: "ma",
+ weekStart: 1,
+ clear: "töröl",
+ titleFormat: "y. MM",
+ format: "yyyy.mm.dd"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/hy.js b/src/ui/static/js/datepicker/locales/hy.js
new file mode 100644
index 000000000..dd662d0df
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/hy.js
@@ -0,0 +1,18 @@
+/**
+ * Armenian translation for bootstrap-datepicker
+ * Hayk Chamyan
+ */
+(function () {
+ Datepicker.locales.hy = {
+ days: ["Կիրակի", "Երկուշաբթի", "Երեքշաբթի", "Չորեքշաբթի", "Հինգշաբթի", "Ուրբաթ", "Շաբաթ"],
+ daysShort: ["Կիր", "Երկ", "Երե", "Չոր", "Հին", "Ուրբ", "Շաբ"],
+ daysMin: ["Կի", "Եկ", "Եք", "Չո", "Հի", "Ու", "Շա"],
+ months: ["Հունվար", "Փետրվար", "Մարտ", "Ապրիլ", "Մայիս", "Հունիս", "Հուլիս", "Օգոստոս", "Սեպտեմբեր", "Հոկտեմբեր", "Նոյեմբեր", "Դեկտեմբեր"],
+ monthsShort: ["Հնվ", "Փետ", "Մար", "Ապր", "Մայ", "Հուն", "Հուլ", "Օգս", "Սեպ", "Հոկ", "Նոյ", "Դեկ"],
+ today: "Այսօր",
+ clear: "Ջնջել",
+ format: "dd.mm.yyyy",
+ weekStart: 1,
+ monthsTitle: 'Ամիսնէր'
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/id.js b/src/ui/static/js/datepicker/locales/id.js
new file mode 100644
index 000000000..0f0c2b9fa
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/id.js
@@ -0,0 +1,19 @@
+/**
+ * Bahasa translation for bootstrap-datepicker
+ * Azwar Akbar
+ * Ardeman
+ */
+(function () {
+ Datepicker.locales.id = {
+ days: ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"],
+ daysShort: ["Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"],
+ daysMin: ["Mg", "Sn", "Sl", "Rb", "Km", "Jm", "Sb"],
+ months: ["Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Agt", "Sep", "Okt", "Nov", "Des"],
+ today: "Hari Ini",
+ monthsTitle: "Bulan",
+ clear: "Kosongkan",
+ weekStart: 0,
+ format: "dd-mm-yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/is.js b/src/ui/static/js/datepicker/locales/is.js
new file mode 100644
index 000000000..18f818dc4
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/is.js
@@ -0,0 +1,14 @@
+/**
+ * Icelandic translation for bootstrap-datepicker
+ * Hinrik Örn Sigurðsson
+ */
+(function () {
+ Datepicker.locales.is = {
+ days: ["Sunnudagur", "Mánudagur", "Þriðjudagur", "Miðvikudagur", "Fimmtudagur", "Föstudagur", "Laugardagur"],
+ daysShort: ["Sun", "Mán", "Þri", "Mið", "Fim", "Fös", "Lau"],
+ daysMin: ["Su", "Má", "Þr", "Mi", "Fi", "Fö", "La"],
+ months: ["Janúar", "Febrúar", "Mars", "Apríl", "Maí", "Júní", "Júlí", "Ágúst", "September", "Október", "Nóvember", "Desember"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maí", "Jún", "Júl", "Ágú", "Sep", "Okt", "Nóv", "Des"],
+ today: "Í Dag"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/it-CH.js b/src/ui/static/js/datepicker/locales/it-CH.js
new file mode 100644
index 000000000..b1081cd4a
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/it-CH.js
@@ -0,0 +1,20 @@
+/**
+ * Italian (Switzerland) translation for bootstrap-datepicker
+ * Christoph Jossi
+ * Based on
+ * Italian translation for bootstrap-datepicker
+ * Enrico Rubboli
+ */
+(function () {
+ Datepicker.locales['it-CH'] = {
+ days: ["Domenica", "Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato"],
+ daysShort: ["Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"],
+ daysMin: ["Do", "Lu", "Ma", "Me", "Gi", "Ve", "Sa"],
+ months: ["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"],
+ monthsShort: ["Gen", "Feb", "Mar", "Apr", "Mag", "Giu", "Lug", "Ago", "Set", "Ott", "Nov", "Dic"],
+ today: "Oggi",
+ clear: "Cancella",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/it.js b/src/ui/static/js/datepicker/locales/it.js
new file mode 100644
index 000000000..70d98ab70
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/it.js
@@ -0,0 +1,18 @@
+/**
+ * Italian translation for bootstrap-datepicker
+ * Enrico Rubboli
+ */
+(function () {
+ Datepicker.locales.it = {
+ days: ["Domenica", "Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato"],
+ daysShort: ["Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"],
+ daysMin: ["Do", "Lu", "Ma", "Me", "Gi", "Ve", "Sa"],
+ months: ["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"],
+ monthsShort: ["Gen", "Feb", "Mar", "Apr", "Mag", "Giu", "Lug", "Ago", "Set", "Ott", "Nov", "Dic"],
+ today: "Oggi",
+ monthsTitle: "Mesi",
+ clear: "Cancella",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/ja.js b/src/ui/static/js/datepicker/locales/ja.js
new file mode 100644
index 000000000..f1c1b3030
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/ja.js
@@ -0,0 +1,17 @@
+/**
+ * Japanese translation for bootstrap-datepicker
+ * Norio Suzuki
+ */
+(function () {
+ Datepicker.locales.ja = {
+ days: ["日曜", "月曜", "火曜", "水曜", "木曜", "金曜", "土曜"],
+ daysShort: ["日", "月", "火", "水", "木", "金", "土"],
+ daysMin: ["日", "月", "火", "水", "木", "金", "土"],
+ months: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
+ monthsShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
+ today: "今日",
+ format: "yyyy/mm/dd",
+ titleFormat: "y年mm月",
+ clear: "クリア"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/ka.js b/src/ui/static/js/datepicker/locales/ka.js
new file mode 100644
index 000000000..b0cd474a4
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/ka.js
@@ -0,0 +1,17 @@
+/**
+ * Georgian translation for bootstrap-datepicker
+ * Levan Melikishvili
+ */
+(function () {
+ Datepicker.locales.ka = {
+ days: ["კვირა", "ორშაბათი", "სამშაბათი", "ოთხშაბათი", "ხუთშაბათი", "პარასკევი", "შაბათი"],
+ daysShort: ["კვი", "ორშ", "სამ", "ოთხ", "ხუთ", "პარ", "შაბ"],
+ daysMin: ["კვ", "ორ", "სა", "ოთ", "ხუ", "პა", "შა"],
+ months: ["იანვარი", "თებერვალი", "მარტი", "აპრილი", "მაისი", "ივნისი", "ივლისი", "აგვისტო", "სექტემბერი", "ოქტომბერი", "ნოემბერი", "დეკემბერი"],
+ monthsShort: ["იან", "თებ", "მარ", "აპრ", "მაი", "ივნ", "ივლ", "აგვ", "სექ", "ოქტ", "ნოე", "დეკ"],
+ today: "დღეს",
+ clear: "გასუფთავება",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/kk.js b/src/ui/static/js/datepicker/locales/kk.js
new file mode 100644
index 000000000..9b85f8cf9
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/kk.js
@@ -0,0 +1,15 @@
+/**
+ * Kazakh translation for bootstrap-datepicker
+ * Yerzhan Tolekov
+ */
+(function () {
+ Datepicker.locales.kk = {
+ days: ["Жексенбі", "Дүйсенбі", "Сейсенбі", "Сәрсенбі", "Бейсенбі", "Жұма", "Сенбі"],
+ daysShort: ["Жек", "Дүй", "Сей", "Сәр", "Бей", "Жұм", "Сен"],
+ daysMin: ["Жк", "Дс", "Сс", "Ср", "Бс", "Жм", "Сн"],
+ months: ["Қаңтар", "Ақпан", "Наурыз", "Сәуір", "Мамыр", "Маусым", "Шілде", "Тамыз", "Қыркүйек", "Қазан", "Қараша", "Желтоқсан"],
+ monthsShort: ["Қаң", "Ақп", "Нау", "Сәу", "Мам", "Мау", "Шіл", "Там", "Қыр", "Қаз", "Қар", "Жел"],
+ today: "Бүгін",
+ weekStart: 1
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/km.js b/src/ui/static/js/datepicker/locales/km.js
new file mode 100644
index 000000000..71e294cd3
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/km.js
@@ -0,0 +1,15 @@
+/**
+ * Khmer translation for bootstrap-datepicker
+ * This is the Updated Version of: https: //github.com/uxsolutions/bootstrap-datepicker/blob/71308d42cce9524284c50c6fac50422d1790ac0f/js/locales/bootstrap-datepicker.kh.js
+ */
+(function () {
+ Datepicker.locales.km = {
+ days: ["អាទិត្យ", "ចន្ទ", "អង្គារ", "ពុធ", "ព្រហស្បតិ៍", "សុក្រ", "សៅរ៍"],
+ daysShort: ["អា.ទិ", "ចន្ទ", "អង្គារ", "ពុធ", "ព្រ.ហ", "សុក្រ", "សៅរ៍"],
+ daysMin: ["អា.ទិ", "ចន្ទ", "អង្គារ", "ពុធ", "ព្រ.ហ", "សុក្រ", "សៅរ៍"],
+ months: ["មករា", "កុម្ភះ", "មិនា", "មេសា", "ឧសភា", "មិថុនា", "កក្កដា", "សីហា", "កញ្ញា", "តុលា", "វិច្ឆិកា", "ធ្នូ"],
+ monthsShort: ["មករា", "កុម្ភះ", "មិនា", "មេសា", "ឧសភា", "មិថុនា", "កក្កដា", "សីហា", "កញ្ញា", "តុលា", "វិច្ឆិកា", "ធ្នូ"],
+ today: "ថ្ងៃនេះ",
+ clear: "សំអាត"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/ko.js b/src/ui/static/js/datepicker/locales/ko.js
new file mode 100644
index 000000000..3be9400b9
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/ko.js
@@ -0,0 +1,18 @@
+/**
+ * Korean translation for bootstrap-datepicker
+ * This is a port from https: //github.com/moment/moment/blob/develop/src/locale/ko.js
+ */
+(function () {
+ Datepicker.locales.ko = {
+ days: ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일"],
+ daysShort: ["일", "월", "화", "수", "목", "금", "토"],
+ daysMin: ["일", "월", "화", "수", "목", "금", "토"],
+ months: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"],
+ monthsShort: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"],
+ today: "오늘",
+ clear: "삭제",
+ format: "yyyy-mm-dd",
+ titleFormat: "y년mm월",
+ weekStart: 0
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/lt.js b/src/ui/static/js/datepicker/locales/lt.js
new file mode 100644
index 000000000..894e8cbc5
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/lt.js
@@ -0,0 +1,19 @@
+/**
+ * Lithuanian translation for bootstrap-datepicker
+ * Šarūnas Gliebus
+ */
+
+(function () {
+ Datepicker.locales.lt = {
+ days: ["Sekmadienis", "Pirmadienis", "Antradienis", "Trečiadienis", "Ketvirtadienis", "Penktadienis", "Šeštadienis"],
+ daysShort: ["S", "Pr", "A", "T", "K", "Pn", "Š"],
+ daysMin: ["Sk", "Pr", "An", "Tr", "Ke", "Pn", "Št"],
+ months: ["Sausis", "Vasaris", "Kovas", "Balandis", "Gegužė", "Birželis", "Liepa", "Rugpjūtis", "Rugsėjis", "Spalis", "Lapkritis", "Gruodis"],
+ monthsShort: ["Sau", "Vas", "Kov", "Bal", "Geg", "Bir", "Lie", "Rugp", "Rugs", "Spa", "Lap", "Gru"],
+ today: "Šiandien",
+ monthsTitle: "Mėnesiai",
+ clear: "Išvalyti",
+ weekStart: 1,
+ format: "yyyy-mm-dd"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/lv.js b/src/ui/static/js/datepicker/locales/lv.js
new file mode 100644
index 000000000..109014b9a
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/lv.js
@@ -0,0 +1,18 @@
+/**
+ * Latvian translation for bootstrap-datepicker
+ * Artis Avotins
+ */
+
+(function () {
+ Datepicker.locales.lv = {
+ days: ["Svētdiena", "Pirmdiena", "Otrdiena", "Trešdiena", "Ceturtdiena", "Piektdiena", "Sestdiena"],
+ daysShort: ["Sv", "P", "O", "T", "C", "Pk", "S"],
+ daysMin: ["Sv", "Pr", "Ot", "Tr", "Ce", "Pk", "Se"],
+ months: ["Janvāris", "Februāris", "Marts", "Aprīlis", "Maijs", "Jūnijs", "Jūlijs", "Augusts", "Septembris", "Oktobris", "Novembris", "Decembris"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mai", "Jūn", "Jūl", "Aug", "Sep", "Okt", "Nov", "Dec"],
+ monthsTitle: "Mēneši",
+ today: "Šodien",
+ clear: "Nodzēst",
+ weekStart: 1
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/me.js b/src/ui/static/js/datepicker/locales/me.js
new file mode 100644
index 000000000..6ecc09729
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/me.js
@@ -0,0 +1,17 @@
+/**
+ * Montenegrin translation for bootstrap-datepicker
+ * Miodrag Nikač
+ */
+(function () {
+ Datepicker.locales.me = {
+ days: ["Nedjelja","Ponedjeljak", "Utorak", "Srijeda", "Četvrtak", "Petak", "Subota"],
+ daysShort: ["Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub"],
+ daysMin: ["Ne", "Po", "Ut", "Sr", "Če", "Pe", "Su"],
+ months: ["Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Avg", "Sep", "Okt", "Nov", "Dec"],
+ today: "Danas",
+ weekStart: 1,
+ clear: "Izbriši",
+ format: "dd.mm.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/mk.js b/src/ui/static/js/datepicker/locales/mk.js
new file mode 100644
index 000000000..df87e2d18
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/mk.js
@@ -0,0 +1,15 @@
+/**
+ * Macedonian translation for bootstrap-datepicker
+ * Marko Aleksic
+ */
+(function () {
+ Datepicker.locales.mk = {
+ days: ["Недела", "Понеделник", "Вторник", "Среда", "Четврток", "Петок", "Сабота"],
+ daysShort: ["Нед", "Пон", "Вто", "Сре", "Чет", "Пет", "Саб"],
+ daysMin: ["Не", "По", "Вт", "Ср", "Че", "Пе", "Са"],
+ months: ["Јануари", "Февруари", "Март", "Април", "Мај", "Јуни", "Јули", "Август", "Септември", "Октомври", "Ноември", "Декември"],
+ monthsShort: ["Јан", "Фев", "Мар", "Апр", "Мај", "Јун", "Јул", "Авг", "Сеп", "Окт", "Ное", "Дек"],
+ today: "Денес",
+ format: "dd.mm.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/mn.js b/src/ui/static/js/datepicker/locales/mn.js
new file mode 100644
index 000000000..c080280be
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/mn.js
@@ -0,0 +1,17 @@
+/**
+ * Mongolian translation for bootstrap-datepicker
+ * Andrey Torsunov
+ */
+(function () {
+ Datepicker.locales.mn = {
+ days: ["Ням", "Даваа", "Мягмар", "Лхагва", "Пүрэв", "Баасан", "Бямба"],
+ daysShort: ["Ням", "Дав", "Мяг", "Лха", "Пүр", "Баа", "Бям"],
+ daysMin: ["Ня", "Да", "Мя", "Лх", "Пү", "Ба", "Бя"],
+ months: ["Хулгана", "Үхэр", "Бар", "Туулай", "Луу", "Могой", "Морь", "Хонь", "Бич", "Тахиа", "Нохой", "Гахай"],
+ monthsShort: ["Хул", "Үхэ", "Бар", "Туу", "Луу", "Мог", "Мор", "Хон", "Бич", "Тах", "Нох", "Гах"],
+ today: "Өнөөдөр",
+ clear: "Тодорхой",
+ format: "yyyy.mm.dd",
+ weekStart: 1
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/mr.js b/src/ui/static/js/datepicker/locales/mr.js
new file mode 100644
index 000000000..67289df2a
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/mr.js
@@ -0,0 +1,18 @@
+/**
+ * Marathi translation for bootstrap-datepicker
+ * Sushant Pimple
+ */
+(function () {
+ Datepicker.locales.mr = {
+ days: ["रविवार", "सोमवार", "मंगळवार", "बुधवार", "गुरुवार", "शुक्रवार", "शनिवार"],
+ daysShort: ["रवि", "सोम", "मंगळ", "बुध", "गुरु", "शुक्र", "शनि"],
+ daysMin: ["र", "सो", "मं", "बु", "गु", "शु", "श"],
+ months: ["जानेवारी", "फेब्रुवारी", "मार्च", "एप्रिल", "मे", "जून", "जुलै", "ऑगस्ट", "सप्टेंबर", "ऑक्टोबर", "नोव्हेंबर", "डिसेंबर"],
+ monthsShort: ["जाने.", "फेब्रु.", "मार्च", "एप्रिल", "मे", "जून", "जुलै", "ऑगस्ट", "सप्टें.", "ऑक्टो.", "नोव्हें.", "डिसें."],
+ today: "आज",
+ monthsTitle: "महीने",
+ clear: "हटवा",
+ weekStart: 1,
+ format: "dd / mm / yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/ms.js b/src/ui/static/js/datepicker/locales/ms.js
new file mode 100644
index 000000000..20283bd4d
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/ms.js
@@ -0,0 +1,15 @@
+/**
+ * Malay translation for bootstrap-datepicker
+ * Ateman Faiz
+ */
+(function () {
+ Datepicker.locales.ms = {
+ days: ["Ahad", "Isnin", "Selasa", "Rabu", "Khamis", "Jumaat", "Sabtu"],
+ daysShort: ["Aha", "Isn", "Sel", "Rab", "Kha", "Jum", "Sab"],
+ daysMin: ["Ah", "Is", "Se", "Ra", "Kh", "Ju", "Sa"],
+ months: ["Januari", "Februari", "Mac", "April", "Mei", "Jun", "Julai", "Ogos", "September", "Oktober", "November", "Disember"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Ogo", "Sep", "Okt", "Nov", "Dis"],
+ today: "Hari Ini",
+ clear: "Bersihkan"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/nl-BE.js b/src/ui/static/js/datepicker/locales/nl-BE.js
new file mode 100644
index 000000000..6cf3d5654
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/nl-BE.js
@@ -0,0 +1,18 @@
+/**
+ * Belgium-Dutch translation for bootstrap-datepicker
+ * Julien Poulin
+ */
+(function () {
+ Datepicker.locales['nl-BE'] = {
+ days: ["zondag", "maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag"],
+ daysShort: ["zo", "ma", "di", "wo", "do", "vr", "za"],
+ daysMin: ["zo", "ma", "di", "wo", "do", "vr", "za"],
+ months: ["januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december"],
+ monthsShort: ["jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec"],
+ today: "Vandaag",
+ monthsTitle: "Maanden",
+ clear: "Leegmaken",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/nl.js b/src/ui/static/js/datepicker/locales/nl.js
new file mode 100644
index 000000000..1bc12ea12
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/nl.js
@@ -0,0 +1,18 @@
+/**
+ * Dutch translation for bootstrap-datepicker
+ * Reinier Goltstein
+ */
+(function () {
+ Datepicker.locales.nl = {
+ days: ["zondag", "maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag"],
+ daysShort: ["zo", "ma", "di", "wo", "do", "vr", "za"],
+ daysMin: ["zo", "ma", "di", "wo", "do", "vr", "za"],
+ months: ["januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december"],
+ monthsShort: ["jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec"],
+ today: "Vandaag",
+ monthsTitle: "Maanden",
+ clear: "Wissen",
+ weekStart: 1,
+ format: "dd-mm-yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/no.js b/src/ui/static/js/datepicker/locales/no.js
new file mode 100644
index 000000000..d9c55fe4b
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/no.js
@@ -0,0 +1,18 @@
+/**
+ * Norwegian translation for bootstrap-datepicker
+ * George Gooding
+ */
+(function () {
+ Datepicker.locales.no = {
+ days: ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag'],
+ daysShort: ['søn', 'man', 'tir', 'ons', 'tor', 'fre', 'lør'],
+ daysMin: ['sø', 'ma', 'ti', 'on', 'to', 'fr', 'lø'],
+ months: ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember'],
+ monthsShort: ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des'],
+ today: 'i dag',
+ monthsTitle: 'Måneder',
+ clear: 'Nullstill',
+ weekStart: 1,
+ format: 'dd.mm.yyyy'
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/oc.js b/src/ui/static/js/datepicker/locales/oc.js
new file mode 100644
index 000000000..c1d600743
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/oc.js
@@ -0,0 +1,17 @@
+/**
+ * Occitan translation for bootstrap-datepicker
+ */
+(function () {
+ Datepicker.locales.oc = {
+ days: ["Dimenge", "Diluns", "Dimars", "Dimècres", "Dijòus", "Divendres", "Dissabte"],
+ daysShort: ["Dim", "Dil", "Dmr", "Dmc", "Dij", "Div", "Dis"],
+ daysMin: ["dg", "dl", "dr", "dc", "dj", "dv", "ds"],
+ months: ["Genièr", "Febrièr", "Març", "Abrial", "Mai", "Junh", "Julhet", "Agost", "Setembre", "Octobre", "Novembre", "Decembre"],
+ monthsShort: ["Gen", "Feb", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Oct", "Nov", "Dec"],
+ today: "Uèi",
+ monthsTitle: "Meses",
+ clear: "Escafar",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/pl.js b/src/ui/static/js/datepicker/locales/pl.js
new file mode 100644
index 000000000..55444afd7
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/pl.js
@@ -0,0 +1,17 @@
+/**
+ * Polish translation for bootstrap-datepicker
+ * Robert
+ */
+(function () {
+ Datepicker.locales.pl = {
+ days: ["Niedziela", "Poniedziałek", "Wtorek", "Środa", "Czwartek", "Piątek", "Sobota"],
+ daysShort: ["Niedz.", "Pon.", "Wt.", "Śr.", "Czw.", "Piąt.", "Sob."],
+ daysMin: ["Ndz.", "Pn.", "Wt.", "Śr.", "Czw.", "Pt.", "Sob."],
+ months: ["Styczeń", "Luty", "Marzec", "Kwiecień", "Maj", "Czerwiec", "Lipiec", "Sierpień", "Wrzesień", "Październik", "Listopad", "Grudzień"],
+ monthsShort: ["Sty.", "Lut.", "Mar.", "Kwi.", "Maj", "Cze.", "Lip.", "Sie.", "Wrz.", "Paź.", "Lis.", "Gru."],
+ today: "Dzisiaj",
+ weekStart: 1,
+ clear: "Wyczyść",
+ format: "dd.mm.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/pt-BR.js b/src/ui/static/js/datepicker/locales/pt-BR.js
new file mode 100644
index 000000000..fb855c904
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/pt-BR.js
@@ -0,0 +1,17 @@
+/**
+ * Brazilian translation for bootstrap-datepicker
+ * Cauan Cabral
+ */
+(function () {
+ Datepicker.locales['pt-BR'] = {
+ days: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"],
+ daysShort: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"],
+ daysMin: ["Do", "Se", "Te", "Qu", "Qu", "Se", "Sa"],
+ months: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
+ monthsShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"],
+ today: "Hoje",
+ monthsTitle: "Meses",
+ clear: "Limpar",
+ format: "dd/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/pt.js b/src/ui/static/js/datepicker/locales/pt.js
new file mode 100644
index 000000000..f25cf9b48
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/pt.js
@@ -0,0 +1,18 @@
+/**
+ * Portuguese translation for bootstrap-datepicker
+ * Original code: Cauan Cabral
+ * Tiago Melo
+ */
+(function () {
+ Datepicker.locales.pt = {
+ days: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"],
+ daysShort: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"],
+ daysMin: ["Do", "Se", "Te", "Qu", "Qu", "Se", "Sa"],
+ months: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
+ monthsShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"],
+ today: "Hoje",
+ monthsTitle: "Meses",
+ clear: "Limpar",
+ format: "dd/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/ro.js b/src/ui/static/js/datepicker/locales/ro.js
new file mode 100644
index 000000000..0b98838e8
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/ro.js
@@ -0,0 +1,17 @@
+/**
+ * Romanian translation for bootstrap-datepicker
+ * Cristian Vasile
+ */
+(function () {
+ Datepicker.locales.ro = {
+ days: ["Duminică", "Luni", "Marţi", "Miercuri", "Joi", "Vineri", "Sâmbătă"],
+ daysShort: ["Dum", "Lun", "Mar", "Mie", "Joi", "Vin", "Sâm"],
+ daysMin: ["Du", "Lu", "Ma", "Mi", "Jo", "Vi", "Sâ"],
+ months: ["Ianuarie", "Februarie", "Martie", "Aprilie", "Mai", "Iunie", "Iulie", "August", "Septembrie", "Octombrie", "Noiembrie", "Decembrie"],
+ monthsShort: ["Ian", "Feb", "Mar", "Apr", "Mai", "Iun", "Iul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Astăzi",
+ clear: "Șterge",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/ru.js b/src/ui/static/js/datepicker/locales/ru.js
new file mode 100644
index 000000000..f85cc51ea
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/ru.js
@@ -0,0 +1,18 @@
+/**
+ * Russian translation for bootstrap-datepicker
+ * Victor Taranenko
+ */
+(function () {
+ Datepicker.locales.ru = {
+ days: ["Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"],
+ daysShort: ["Вск", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Суб"],
+ daysMin: ["Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"],
+ months: ["Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"],
+ monthsShort: ["Янв", "Фев", "Мар", "Апр", "Май", "Июн", "Июл", "Авг", "Сен", "Окт", "Ноя", "Дек"],
+ today: "Сегодня",
+ clear: "Очистить",
+ format: "dd.mm.yyyy",
+ weekStart: 1,
+ monthsTitle: 'Месяцы'
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/si.js b/src/ui/static/js/datepicker/locales/si.js
new file mode 100644
index 000000000..04bf43f0c
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/si.js
@@ -0,0 +1,18 @@
+/**
+ * Sinhala translation for bootstrap-datepicker
+ * Chanaka Fernando
+ */
+(function () {
+ Datepicker.locales.si = {
+ days: ["ඉරිදා", "සඳුදා", "අඟහරුවාදා", "බදාදා", "බ්රහස්පතින්දා", "සිකුරාදා", "සෙනසුරාදා"],
+ daysShort: ["ඉරි", "සඳු", "අඟ", "බදා", "බ්රහ", "සිකු", "සෙන"],
+ daysMin: ["ඉ", "ස", "අ", "බ", "බ්ර", "සි", "සෙ"],
+ months: ["ජනවාරි", "පෙබරවාරි", "මාර්තු", "අප්රේල්", "මැයි", "ජුනි", "ජූලි", "අගෝස්තු", "සැප්තැම්බර්", "ඔක්තෝබර්", "නොවැම්බර්", "දෙසැම්බර්"],
+ monthsShort: ["ජන", "පෙබ", "මාර්", "අප්රේ", "මැයි", "ජුනි", "ජූලි", "අගෝ", "සැප්", "ඔක්", "නොවැ", "දෙසැ"],
+ today: "අද",
+ monthsTitle: "මාස",
+ clear: "මකන්න",
+ weekStart: 0,
+ format: "yyyy-mm-dd"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/sk.js b/src/ui/static/js/datepicker/locales/sk.js
new file mode 100644
index 000000000..0f813abf3
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/sk.js
@@ -0,0 +1,18 @@
+/**
+ * Slovak translation for bootstrap-datepicker
+ * Marek Lichtner
+ * Fixes by Michal Remiš
+ */
+(function () {
+ Datepicker.locales.sk = {
+ days: ["Nedeľa", "Pondelok", "Utorok", "Streda", "Štvrtok", "Piatok", "Sobota"],
+ daysShort: ["Ned", "Pon", "Uto", "Str", "Štv", "Pia", "Sob"],
+ daysMin: ["Ne", "Po", "Ut", "St", "Št", "Pia", "So"],
+ months: ["Január", "Február", "Marec", "Apríl", "Máj", "Jún", "Júl", "August", "September", "Október", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Máj", "Jún", "Júl", "Aug", "Sep", "Okt", "Nov", "Dec"],
+ today: "Dnes",
+ clear: "Vymazať",
+ weekStart: 1,
+ format: "d.m.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/sl.js b/src/ui/static/js/datepicker/locales/sl.js
new file mode 100644
index 000000000..6fae7fd23
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/sl.js
@@ -0,0 +1,15 @@
+/**
+ * Slovene translation for bootstrap-datepicker
+ * Gregor Rudolf
+ */
+(function () {
+ Datepicker.locales.sl = {
+ days: ["Nedelja", "Ponedeljek", "Torek", "Sreda", "Četrtek", "Petek", "Sobota"],
+ daysShort: ["Ned", "Pon", "Tor", "Sre", "Čet", "Pet", "Sob"],
+ daysMin: ["Ne", "Po", "To", "Sr", "Če", "Pe", "So"],
+ months: ["Januar", "Februar", "Marec", "April", "Maj", "Junij", "Julij", "Avgust", "September", "Oktober", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Avg", "Sep", "Okt", "Nov", "Dec"],
+ today: "Danes",
+ weekStart: 1
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/sq.js b/src/ui/static/js/datepicker/locales/sq.js
new file mode 100644
index 000000000..32b2f1d6b
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/sq.js
@@ -0,0 +1,18 @@
+/**
+ * Albanian translation for bootstrap-datepicker
+ * Tomor Pupovci
+ */
+(function () {
+ Datepicker.locales.sq = {
+ days: ["E Diel", "E Hënë", "E Martē", "E Mërkurë", "E Enjte", "E Premte", "E Shtunë"],
+ daysShort: ["Die", "Hën", "Mar", "Mër", "Enj", "Pre", "Shtu"],
+ daysMin: ["Di", "Hë", "Ma", "Më", "En", "Pr", "Sht"],
+ months: ["Janar", "Shkurt", "Mars", "Prill", "Maj", "Qershor", "Korrik", "Gusht", "Shtator", "Tetor", "Nëntor", "Dhjetor"],
+ monthsShort: ["Jan", "Shk", "Mar", "Pri", "Maj", "Qer", "Korr", "Gu", "Sht", "Tet", "Nën", "Dhjet"],
+ monthsTitle: "Muaj",
+ today: "Sot",
+ weekStart: 1,
+ format: "dd/mm/yyyy",
+ clear: "Pastro"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/sr-latn.js b/src/ui/static/js/datepicker/locales/sr-latn.js
new file mode 100644
index 000000000..65281d434
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/sr-latn.js
@@ -0,0 +1,16 @@
+/**
+ * Serbian latin translation for bootstrap-datepicker
+ * Bojan Milosavlević
+ */
+(function () {
+ Datepicker.locales['sr-latn'] = {
+ days: ["Nedelja","Ponedeljak", "Utorak", "Sreda", "Četvrtak", "Petak", "Subota"],
+ daysShort: ["Ned", "Pon", "Uto", "Sre", "Čet", "Pet", "Sub"],
+ daysMin: ["N", "Po", "U", "Sr", "Č", "Pe", "Su"],
+ months: ["Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Avg", "Sep", "Okt", "Nov", "Dec"],
+ today: "Danas",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/sr.js b/src/ui/static/js/datepicker/locales/sr.js
new file mode 100644
index 000000000..275edd7d4
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/sr.js
@@ -0,0 +1,16 @@
+/**
+ * Serbian cyrillic translation for bootstrap-datepicker
+ * Bojan Milosavlević
+ */
+(function () {
+ Datepicker.locales.sr = {
+ days: ["Недеља","Понедељак", "Уторак", "Среда", "Четвртак", "Петак", "Субота"],
+ daysShort: ["Нед", "Пон", "Уто", "Сре", "Чет", "Пет", "Суб"],
+ daysMin: ["Н", "По", "У", "Ср", "Ч", "Пе", "Су"],
+ months: ["Јануар", "Фебруар", "Март", "Април", "Мај", "Јун", "Јул", "Август", "Септембар", "Октобар", "Новембар", "Децембар"],
+ monthsShort: ["Јан", "Феб", "Мар", "Апр", "Мај", "Јун", "Јул", "Авг", "Сеп", "Окт", "Нов", "Дец"],
+ today: "Данас",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/sv.js b/src/ui/static/js/datepicker/locales/sv.js
new file mode 100644
index 000000000..955ff67d0
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/sv.js
@@ -0,0 +1,17 @@
+/**
+ * Swedish translation for bootstrap-datepicker
+ * Patrik Ragnarsson
+ */
+(function () {
+ Datepicker.locales.sv = {
+ days: ["söndag", "måndag", "tisdag", "onsdag", "torsdag", "fredag", "lördag"],
+ daysShort: ["sön", "mån", "tis", "ons", "tor", "fre", "lör"],
+ daysMin: ["sö", "må", "ti", "on", "to", "fr", "lö"],
+ months: ["januari", "februari", "mars", "april", "maj", "juni", "juli", "augusti", "september", "oktober", "november", "december"],
+ monthsShort: ["jan", "feb", "mar", "apr", "maj", "jun", "jul", "aug", "sep", "okt", "nov", "dec"],
+ today: "Idag",
+ format: "yyyy-mm-dd",
+ weekStart: 1,
+ clear: "Rensa"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/sw.js b/src/ui/static/js/datepicker/locales/sw.js
new file mode 100644
index 000000000..e67b01f76
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/sw.js
@@ -0,0 +1,15 @@
+/**
+ * Swahili translation for bootstrap-datepicker
+ * Edwin Mugendi
+ * Source: http: //scriptsource.org/cms/scripts/page.php?item_id=entry_detail&uid=xnfaqyzcku
+ */
+(function () {
+ Datepicker.locales.sw = {
+ days: ["Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi"],
+ daysShort: ["J2", "J3", "J4", "J5", "Alh", "Ij", "J1"],
+ daysMin: ["2", "3", "4", "5", "A", "I", "1"],
+ months: ["Januari", "Februari", "Machi", "Aprili", "Mei", "Juni", "Julai", "Agosti", "Septemba", "Oktoba", "Novemba", "Desemba"],
+ monthsShort: ["Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nov", "Des"],
+ today: "Leo"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/ta.js b/src/ui/static/js/datepicker/locales/ta.js
new file mode 100644
index 000000000..ff3389286
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/ta.js
@@ -0,0 +1,18 @@
+/**
+ * Tamil translation for bootstrap-datepicker
+ * Abubacker Siddik A
+ */
+(function () {
+ Datepicker.locales.ta = {
+ days: ["ஞாயிறு", "திங்கள்", "செவ்வாய்", "புதன்", "வியாழன்", "வெள்ளி", "சனி"],
+ daysShort: ["ஞாயி", "திங்", "செவ்", "புத", "வியா", "வெள்", "சனி"],
+ daysMin: ["ஞா", "தி", "செ", "பு", "வி", "வெ", "ச"],
+ months: ["ஜனவரி", "பிப்ரவரி", "மார்ச்", "ஏப்ரல்", "மே", "ஜூன்", "ஜூலை", "ஆகஸ்டு", "செப்டம்பர்", "அக்டோபர்", "நவம்பர்", "டிசம்பர்"],
+ monthsShort: ["ஜன", "பிப்", "மார்", "ஏப்", "மே", "ஜூன்", "ஜூலை", "ஆக", "செப்", "அக்", "நவ", "டிச"],
+ today: "இன்று",
+ monthsTitle: "மாதங்கள்",
+ clear: "நீக்கு",
+ weekStart: 1,
+ format: "dd/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/tg.js b/src/ui/static/js/datepicker/locales/tg.js
new file mode 100644
index 000000000..497c83717
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/tg.js
@@ -0,0 +1,19 @@
+/**
+ * Tajik (cyrillic) translation for bootstrap-datepicker
+ * Bakhtiyor Bahritidinov
+ * Orif N. Jr.
+ */
+(function () {
+ Datepicker.locales.tg = {
+ days: ["Якшанбе", "Душанбе", "Сешанбе", "Чоршанбе", "Панҷшанбе", "Ҷумъа", "Шанбе"],
+ daysShort: ["Яшб", "Дшб", "Сшб", "Чшб", "Пшб", "Ҷум", "Шнб"],
+ daysMin: ["Яш", "Дш", "Сш", "Чш", "Пш", "Ҷм", "Шб"],
+ months: ["Январ", "Феврал", "Март", "Апрел", "Май", "Июн", "Июл", "Август", "Сентябр", "Октябр", "Ноябр", "Декабр"],
+ monthsShort: ["Янв", "Фев", "Мар", "Апр", "Май", "Июн", "Июл", "Авг", "Сен", "Окт", "Ноя", "Дек"],
+ today: "Имрӯз",
+ monthsTitle: "Моҳҳо",
+ clear: "Тоза намудан",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/th.js b/src/ui/static/js/datepicker/locales/th.js
new file mode 100644
index 000000000..bdeb7904e
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/th.js
@@ -0,0 +1,14 @@
+/**
+ * Thai translation for bootstrap-datepicker
+ * Suchau Jiraprapot
+ */
+(function () {
+ Datepicker.locales.th = {
+ days: ["อาทิตย์", "จันทร์", "อังคาร", "พุธ", "พฤหัส", "ศุกร์", "เสาร์", "อาทิตย์"],
+ daysShort: ["อา", "จ", "อ", "พ", "พฤ", "ศ", "ส", "อา"],
+ daysMin: ["อา", "จ", "อ", "พ", "พฤ", "ศ", "ส", "อา"],
+ months: ["มกราคม", "กุมภาพันธ์", "มีนาคม", "เมษายน", "พฤษภาคม", "มิถุนายน", "กรกฎาคม", "สิงหาคม", "กันยายน", "ตุลาคม", "พฤศจิกายน", "ธันวาคม"],
+ monthsShort: ["ม.ค.", "ก.พ.", "มี.ค.", "เม.ย.", "พ.ค.", "มิ.ย.", "ก.ค.", "ส.ค.", "ก.ย.", "ต.ค.", "พ.ย.", "ธ.ค."],
+ today: "วันนี้"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/tk.js b/src/ui/static/js/datepicker/locales/tk.js
new file mode 100644
index 000000000..4c8affd4a
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/tk.js
@@ -0,0 +1,18 @@
+/**
+ * Turkmen translation for bootstrap-datepicker
+ * N'Bayramberdiyev
+ */
+(function () {
+ Datepicker.locales.tk = {
+ days: ["Ýekşenbe", "Duşenbe", "Sişenbe", "Çarşenbe", "Penşenbe", "Anna", "Şenbe"],
+ daysShort: ["Ýek", "Duş", "Siş", "Çar", "Pen", "Ann", "Şen"],
+ daysMin: ["Ýe", "Du", "Si", "Ça", "Pe", "An", "Şe"],
+ months: ["Ýanwar", "Fewral", "Mart", "Aprel", "Maý", "Iýun", "Iýul", "Awgust", "Sentýabr", "Oktýabr", "Noýabr", "Dekabr"],
+ monthsShort: ["Ýan", "Few", "Mar", "Apr", "Maý", "Iýn", "Iýl", "Awg", "Sen", "Okt", "Noý", "Dek"],
+ today: "Bu gün",
+ monthsTitle: "Aýlar",
+ clear: "Aýyr",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/tr.js b/src/ui/static/js/datepicker/locales/tr.js
new file mode 100644
index 000000000..5a97ed774
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/tr.js
@@ -0,0 +1,17 @@
+/**
+ * Turkish translation for bootstrap-datepicker
+ * Serkan Algur
+ */
+(function () {
+ Datepicker.locales.tr = {
+ days: ["Pazar", "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi"],
+ daysShort: ["Pz", "Pzt", "Sal", "Çrş", "Prş", "Cu", "Cts"],
+ daysMin: ["Pz", "Pzt", "Sa", "Çr", "Pr", "Cu", "Ct"],
+ months: ["Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"],
+ monthsShort: ["Oca", "Şub", "Mar", "Nis", "May", "Haz", "Tem", "Ağu", "Eyl", "Eki", "Kas", "Ara"],
+ today: "Bugün",
+ clear: "Temizle",
+ weekStart: 1,
+ format: "dd.mm.yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/uk.js b/src/ui/static/js/datepicker/locales/uk.js
new file mode 100644
index 000000000..382103527
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/uk.js
@@ -0,0 +1,17 @@
+/**
+ * Ukrainian translation for bootstrap-datepicker
+ * Igor Polynets
+ */
+(function () {
+ Datepicker.locales.uk = {
+ days: ["Неділя", "Понеділок", "Вівторок", "Середа", "Четвер", "П'ятниця", "Субота"],
+ daysShort: ["Нед", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Суб"],
+ daysMin: ["Нд", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"],
+ months: ["Cічень", "Лютий", "Березень", "Квітень", "Травень", "Червень", "Липень", "Серпень", "Вересень", "Жовтень", "Листопад", "Грудень"],
+ monthsShort: ["Січ", "Лют", "Бер", "Кві", "Тра", "Чер", "Лип", "Сер", "Вер", "Жов", "Лис", "Гру"],
+ today: "Сьогодні",
+ clear: "Очистити",
+ format: "dd.mm.yyyy",
+ weekStart: 1
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/uz-cyrl.js b/src/ui/static/js/datepicker/locales/uz-cyrl.js
new file mode 100644
index 000000000..3dd8dde79
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/uz-cyrl.js
@@ -0,0 +1,18 @@
+/**
+ * Uzbek cyrillic translation for bootstrap-datepicker
+ * Kakhramonov Javlonbek
+ */
+(function () {
+ Datepicker.locales['uz-cyrl'] = {
+ days: ["Якшанба", "Душанба", "Сешанба", "Чоршанба", "Пайшанба", "Жума", "Шанба"],
+ daysShort: ["Якш", "Ду", "Се", "Чор", "Пай", "Жу", "Ша"],
+ daysMin: ["Як", "Ду", "Се", "Чо", "Па", "Жу", "Ша"],
+ months: ["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],
+ monthsShort: ["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],
+ today: "Бугун",
+ clear: "Ўчириш",
+ format: "dd.mm.yyyy",
+ weekStart: 1,
+ monthsTitle: 'Ойлар'
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/uz-latn.js b/src/ui/static/js/datepicker/locales/uz-latn.js
new file mode 100644
index 000000000..8f18dac68
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/uz-latn.js
@@ -0,0 +1,18 @@
+/**
+ * Uzbek latin translation for bootstrap-datepicker
+ * Kakhramonov Javlonbek
+ */
+(function () {
+ Datepicker.locales['uz-latn'] = {
+ days: ["Yakshanba", "Dushanba", "Seshanba", "Chorshanba", "Payshanba", "Juma", "Shanba"],
+ daysShort: ["Yak", "Du", "Se", "Chor", "Pay", "Ju", "Sha"],
+ daysMin: ["Ya", "Du", "Se", "Cho", "Pa", "Ju", "Sha"],
+ months: ["Yanvar", "Fevral", "Mart", "Aprel", "May", "Iyun", "Iyul", "Avgust", "Sentabr", "Oktabr", "Noyabr", "Dekabr"],
+ monthsShort: ["Yan", "Fev", "Mar", "Apr", "May", "Iyn", "Iyl", "Avg", "Sen", "Okt", "Noy", "Dek"],
+ today: "Bugun",
+ clear: "O'chirish",
+ format: "dd.mm.yyyy",
+ weekStart: 1,
+ monthsTitle: 'Oylar'
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/vi.js b/src/ui/static/js/datepicker/locales/vi.js
new file mode 100644
index 000000000..90b5cb8a6
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/vi.js
@@ -0,0 +1,16 @@
+/**
+ * Vietnamese translation for bootstrap-datepicker
+ * An Vo
+ */
+(function () {
+ Datepicker.locales.vi = {
+ days: ["Chủ nhật", "Thứ hai", "Thứ ba", "Thứ tư", "Thứ năm", "Thứ sáu", "Thứ bảy"],
+ daysShort: ["CN", "Thứ 2", "Thứ 3", "Thứ 4", "Thứ 5", "Thứ 6", "Thứ 7"],
+ daysMin: ["CN", "T2", "T3", "T4", "T5", "T6", "T7"],
+ months: ["Tháng 1", "Tháng 2", "Tháng 3", "Tháng 4", "Tháng 5", "Tháng 6", "Tháng 7", "Tháng 8", "Tháng 9", "Tháng 10", "Tháng 11", "Tháng 12"],
+ monthsShort: ["Th1", "Th2", "Th3", "Th4", "Th5", "Th6", "Th7", "Th8", "Th9", "Th10", "Th11", "Th12"],
+ today: "Hôm nay",
+ clear: "Xóa",
+ format: "dd/mm/yyyy"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/zh-CN.js b/src/ui/static/js/datepicker/locales/zh-CN.js
new file mode 100644
index 000000000..adec7d20c
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/zh-CN.js
@@ -0,0 +1,19 @@
+/**
+ * Simplified Chinese translation for bootstrap-datepicker
+ * Yuan Cheung
+ */
+(function () {
+ Datepicker.locales['zh-CN'] = {
+ days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
+ daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
+ daysMin: ["日", "一", "二", "三", "四", "五", "六"],
+ months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
+ monthsShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
+ today: "今天",
+ monthsTitle: "选择月份",
+ clear: "清除",
+ format: "yyyy-mm-dd",
+ titleFormat: "y年mm月",
+ weekStart: 1
+ };
+}());
diff --git a/src/ui/static/js/datepicker/locales/zh-TW.js b/src/ui/static/js/datepicker/locales/zh-TW.js
new file mode 100644
index 000000000..379f7d89e
--- /dev/null
+++ b/src/ui/static/js/datepicker/locales/zh-TW.js
@@ -0,0 +1,20 @@
+/**
+ * Traditional Chinese translation for bootstrap-datepicker
+ * Rung-Sheng Jang
+ * FrankWu Fix more appropriate use of Traditional Chinese habit
+ */
+(function () {
+ Datepicker.locales['zh-TW'] = {
+ days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
+ daysShort: ["週日", "週一", "週二", "週三", "週四", "週五", "週六"],
+ daysMin: ["日", "一", "二", "三", "四", "五", "六"],
+ months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
+ monthsShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
+ today: "今天",
+ monthsTitle: "月份",
+ format: "yyyy/mm/dd",
+ weekStart: 0,
+ titleFormat: "y年mm月",
+ clear: "清除"
+ };
+}());
diff --git a/src/ui/static/js/datepicker/main.js b/src/ui/static/js/datepicker/main.js
new file mode 100644
index 000000000..7b589db91
--- /dev/null
+++ b/src/ui/static/js/datepicker/main.js
@@ -0,0 +1,4 @@
+import Datepicker from './Datepicker.js';
+import DateRangePicker from './DateRangePicker.js';
+
+export {Datepicker, DateRangePicker};
diff --git a/src/ui/static/js/datepicker/options/defaultOptions.js b/src/ui/static/js/datepicker/options/defaultOptions.js
new file mode 100644
index 000000000..d037ede4b
--- /dev/null
+++ b/src/ui/static/js/datepicker/options/defaultOptions.js
@@ -0,0 +1,38 @@
+// config options updatable by setOptions() and their default values
+const defaultOptions = {
+ autohide: false,
+ beforeShowDay: null,
+ beforeShowDecade: null,
+ beforeShowMonth: null,
+ beforeShowYear: null,
+ calendarWeeks: false,
+ clearBtn: false,
+ dateDelimiter: ',',
+ datesDisabled: [],
+ daysOfWeekDisabled: [],
+ daysOfWeekHighlighted: [],
+ defaultViewDate: undefined, // placeholder, defaults to today() by the program
+ disableTouchKeyboard: false,
+ format: 'mm/dd/yyyy',
+ language: 'en',
+ maxDate: null,
+ maxNumberOfDates: 1,
+ maxView: 3,
+ minDate: null,
+ nextArrow: '»',
+ orientation: 'auto',
+ pickLevel: 0,
+ prevArrow: '«',
+ showDaysOfWeek: true,
+ showOnClick: true,
+ showOnFocus: true,
+ startView: 0,
+ title: '',
+ todayBtn: false,
+ todayBtnMode: 0,
+ todayHighlight: false,
+ updateOnBlur: true,
+ weekStart: 0,
+};
+
+export default defaultOptions;
diff --git a/src/ui/static/js/datepicker/options/processOptions.js b/src/ui/static/js/datepicker/options/processOptions.js
new file mode 100644
index 000000000..007b83676
--- /dev/null
+++ b/src/ui/static/js/datepicker/options/processOptions.js
@@ -0,0 +1,288 @@
+import {hasProperty, pushUnique} from '../lib/utils.js';
+import {dateValue, regularizeDate} from '../lib/date.js';
+import {reFormatTokens, parseDate} from '../lib/date-format.js';
+import {parseHTML} from '../lib/dom.js';
+import defaultOptions from './defaultOptions.js';
+
+const {
+ language: defaultLang,
+ format: defaultFormat,
+ weekStart: defaultWeekStart,
+} = defaultOptions;
+
+// Reducer function to filter out invalid day-of-week from the input
+function sanitizeDOW(dow, day) {
+ return dow.length < 6 && day >= 0 && day < 7
+ ? pushUnique(dow, day)
+ : dow;
+}
+
+function calcEndOfWeek(startOfWeek) {
+ return (startOfWeek + 6) % 7;
+}
+
+// validate input date. if invalid, fallback to the original value
+function validateDate(value, format, locale, origValue) {
+ const date = parseDate(value, format, locale);
+ return date !== undefined ? date : origValue;
+}
+
+// Validate viewId. if invalid, fallback to the original value
+function validateViewId(value, origValue, max = 3) {
+ const viewId = parseInt(value, 10);
+ return viewId >= 0 && viewId <= max ? viewId : origValue;
+}
+
+// Create Datepicker configuration to set
+export default function processOptions(options, datepicker) {
+ const inOpts = Object.assign({}, options);
+ const config = {};
+ const locales = datepicker.constructor.locales;
+ const rangeSideIndex = datepicker.rangeSideIndex;
+ let {
+ format,
+ language,
+ locale,
+ maxDate,
+ maxView,
+ minDate,
+ pickLevel,
+ startView,
+ weekStart,
+ } = datepicker.config || {};
+
+ if (inOpts.language) {
+ let lang;
+ if (inOpts.language !== language) {
+ if (locales[inOpts.language]) {
+ lang = inOpts.language;
+ } else {
+ // Check if langauge + region tag can fallback to the one without
+ // region (e.g. fr-CA → fr)
+ lang = inOpts.language.split('-')[0];
+ if (locales[lang] === undefined) {
+ lang = false;
+ }
+ }
+ }
+ delete inOpts.language;
+ if (lang) {
+ language = config.language = lang;
+
+ // update locale as well when updating language
+ const origLocale = locale || locales[defaultLang];
+ // use default language's properties for the fallback
+ locale = Object.assign({
+ format: defaultFormat,
+ weekStart: defaultWeekStart
+ }, locales[defaultLang]);
+ if (language !== defaultLang) {
+ Object.assign(locale, locales[language]);
+ }
+ config.locale = locale;
+ // if format and/or weekStart are the same as old locale's defaults,
+ // update them to new locale's defaults
+ if (format === origLocale.format) {
+ format = config.format = locale.format;
+ }
+ if (weekStart === origLocale.weekStart) {
+ weekStart = config.weekStart = locale.weekStart;
+ config.weekEnd = calcEndOfWeek(locale.weekStart);
+ }
+ }
+ }
+
+ if (inOpts.format) {
+ const hasToDisplay = typeof inOpts.format.toDisplay === 'function';
+ const hasToValue = typeof inOpts.format.toValue === 'function';
+ const validFormatString = reFormatTokens.test(inOpts.format);
+ if ((hasToDisplay && hasToValue) || validFormatString) {
+ format = config.format = inOpts.format;
+ }
+ delete inOpts.format;
+ }
+
+ //*** pick level ***//
+ let newPickLevel = pickLevel;
+ if (inOpts.pickLevel !== undefined) {
+ newPickLevel = validateViewId(inOpts.pickLevel, 2);
+ delete inOpts.pickLevel;
+ }
+ if (newPickLevel !== pickLevel) {
+ if (newPickLevel > pickLevel) {
+ // complement current minDate/madDate so that the existing range will be
+ // expanded to fit the new level later
+ if (inOpts.minDate === undefined) {
+ inOpts.minDate = minDate;
+ }
+ if (inOpts.maxDate === undefined) {
+ inOpts.maxDate = maxDate;
+ }
+ }
+ // complement datesDisabled so that it will be reset later
+ if (!inOpts.datesDisabled) {
+ inOpts.datesDisabled = [];
+ }
+ pickLevel = config.pickLevel = newPickLevel;
+ }
+
+ //*** dates ***//
+ // while min and maxDate for "no limit" in the options are better to be null
+ // (especially when updating), the ones in the config have to be undefined
+ // because null is treated as 0 (= unix epoch) when comparing with time value
+ let minDt = minDate;
+ let maxDt = maxDate;
+ if (inOpts.minDate !== undefined) {
+ const defaultMinDt = dateValue(0, 0, 1);
+ minDt = inOpts.minDate === null
+ ? defaultMinDt // set 0000-01-01 to prevent negative values for year
+ : validateDate(inOpts.minDate, format, locale, minDt);
+ if (minDt !== defaultMinDt) {
+ minDt = regularizeDate(minDt, pickLevel, false);
+ }
+ delete inOpts.minDate;
+ }
+ if (inOpts.maxDate !== undefined) {
+ maxDt = inOpts.maxDate === null
+ ? undefined
+ : validateDate(inOpts.maxDate, format, locale, maxDt);
+ if (maxDt !== undefined) {
+ maxDt = regularizeDate(maxDt, pickLevel, true);
+ }
+ delete inOpts.maxDate;
+ }
+ if (maxDt < minDt) {
+ minDate = config.minDate = maxDt;
+ maxDate = config.maxDate = minDt;
+ } else {
+ if (minDate !== minDt) {
+ minDate = config.minDate = minDt;
+ }
+ if (maxDate !== maxDt) {
+ maxDate = config.maxDate = maxDt;
+ }
+ }
+
+ if (inOpts.datesDisabled) {
+ config.datesDisabled = inOpts.datesDisabled.reduce((dates, dt) => {
+ const date = parseDate(dt, format, locale);
+ return date !== undefined
+ ? pushUnique(dates, regularizeDate(date, pickLevel, rangeSideIndex))
+ : dates;
+ }, []);
+ delete inOpts.datesDisabled;
+ }
+ if (inOpts.defaultViewDate !== undefined) {
+ const viewDate = parseDate(inOpts.defaultViewDate, format, locale);
+ if (viewDate !== undefined) {
+ config.defaultViewDate = viewDate;
+ }
+ delete inOpts.defaultViewDate;
+ }
+
+ //*** days of week ***//
+ if (inOpts.weekStart !== undefined) {
+ const wkStart = Number(inOpts.weekStart) % 7;
+ if (!isNaN(wkStart)) {
+ weekStart = config.weekStart = wkStart;
+ config.weekEnd = calcEndOfWeek(wkStart);
+ }
+ delete inOpts.weekStart;
+ }
+ if (inOpts.daysOfWeekDisabled) {
+ config.daysOfWeekDisabled = inOpts.daysOfWeekDisabled.reduce(sanitizeDOW, []);
+ delete inOpts.daysOfWeekDisabled;
+ }
+ if (inOpts.daysOfWeekHighlighted) {
+ config.daysOfWeekHighlighted = inOpts.daysOfWeekHighlighted.reduce(sanitizeDOW, []);
+ delete inOpts.daysOfWeekHighlighted;
+ }
+
+ //*** multi date ***//
+ if (inOpts.maxNumberOfDates !== undefined) {
+ const maxNumberOfDates = parseInt(inOpts.maxNumberOfDates, 10);
+ if (maxNumberOfDates >= 0) {
+ config.maxNumberOfDates = maxNumberOfDates;
+ config.multidate = maxNumberOfDates !== 1;
+ }
+ delete inOpts.maxNumberOfDates;
+ }
+ if (inOpts.dateDelimiter) {
+ config.dateDelimiter = String(inOpts.dateDelimiter);
+ delete inOpts.dateDelimiter;
+ }
+
+ //*** view ***//
+ let newMaxView = maxView;
+ if (inOpts.maxView !== undefined) {
+ newMaxView = validateViewId(inOpts.maxView, maxView);
+ delete inOpts.maxView;
+ }
+ // ensure max view >= pick level
+ newMaxView = pickLevel > newMaxView ? pickLevel : newMaxView;
+ if (newMaxView !== maxView) {
+ maxView = config.maxView = newMaxView;
+ }
+
+ let newStartView = startView;
+ if (inOpts.startView !== undefined) {
+ newStartView = validateViewId(inOpts.startView, newStartView);
+ delete inOpts.startView;
+ }
+ // ensure pick level <= start view <= max view
+ if (newStartView < pickLevel) {
+ newStartView = pickLevel;
+ } else if (newStartView > maxView) {
+ newStartView = maxView;
+ }
+ if (newStartView !== startView) {
+ config.startView = newStartView;
+ }
+
+ //*** template ***//
+ if (inOpts.prevArrow) {
+ const prevArrow = parseHTML(inOpts.prevArrow);
+ if (prevArrow.childNodes.length > 0) {
+ config.prevArrow = prevArrow.childNodes;
+ }
+ delete inOpts.prevArrow;
+ }
+ if (inOpts.nextArrow) {
+ const nextArrow = parseHTML(inOpts.nextArrow);
+ if (nextArrow.childNodes.length > 0) {
+ config.nextArrow = nextArrow.childNodes;
+ }
+ delete inOpts.nextArrow;
+ }
+
+ //*** misc ***//
+ if (inOpts.disableTouchKeyboard !== undefined) {
+ config.disableTouchKeyboard = 'ontouchstart' in document && !!inOpts.disableTouchKeyboard;
+ delete inOpts.disableTouchKeyboard;
+ }
+ if (inOpts.orientation) {
+ const orientation = inOpts.orientation.toLowerCase().split(/\s+/g);
+ config.orientation = {
+ x: orientation.find(x => (x === 'left' || x === 'right')) || 'auto',
+ y: orientation.find(y => (y === 'top' || y === 'bottom')) || 'auto',
+ };
+ delete inOpts.orientation;
+ }
+ if (inOpts.todayBtnMode !== undefined) {
+ switch(inOpts.todayBtnMode) {
+ case 0:
+ case 1:
+ config.todayBtnMode = inOpts.todayBtnMode;
+ }
+ delete inOpts.todayBtnMode;
+ }
+
+ //*** copy the rest ***//
+ Object.keys(inOpts).forEach((key) => {
+ if (inOpts[key] !== undefined && hasProperty(defaultOptions, key)) {
+ config[key] = inOpts[key];
+ }
+ });
+
+ return config;
+}
diff --git a/src/ui/static/js/datepicker/picker/Picker.js b/src/ui/static/js/datepicker/picker/Picker.js
new file mode 100644
index 000000000..cbcfaaf71
--- /dev/null
+++ b/src/ui/static/js/datepicker/picker/Picker.js
@@ -0,0 +1,389 @@
+import {hasProperty, lastItemOf, isInRange, limitToRange} from '../lib/utils.js';
+import {today} from '../lib/date.js';
+import {parseHTML, getParent, showElement, hideElement, emptyChildNodes} from '../lib/dom.js';
+import {registerListeners} from '../lib/event.js';
+import pickerTemplate from './templates/pickerTemplate.js';
+import DaysView from './views/DaysView.js';
+import MonthsView from './views/MonthsView.js';
+import YearsView from './views/YearsView.js';
+import {triggerDatepickerEvent} from '../events/functions.js';
+import {
+ onClickTodayBtn,
+ onClickClearBtn,
+ onClickViewSwitch,
+ onClickPrevBtn,
+ onClickNextBtn,
+ onClickView,
+ onMousedownPicker,
+} from '../events/pickerListeners.js';
+
+const orientClasses = ['left', 'top', 'right', 'bottom'].reduce((obj, key) => {
+ obj[key] = `datepicker-orient-${key}`;
+ return obj;
+}, {});
+const toPx = num => num ? `${num}px` : num;
+
+function processPickerOptions(picker, options) {
+ if (options.title !== undefined) {
+ if (options.title) {
+ picker.controls.title.textContent = options.title;
+ showElement(picker.controls.title);
+ } else {
+ picker.controls.title.textContent = '';
+ hideElement(picker.controls.title);
+ }
+ }
+ if (options.prevArrow) {
+ const prevBtn = picker.controls.prevBtn;
+ emptyChildNodes(prevBtn);
+ options.prevArrow.forEach((node) => {
+ prevBtn.appendChild(node.cloneNode(true));
+ });
+ }
+ if (options.nextArrow) {
+ const nextBtn = picker.controls.nextBtn;
+ emptyChildNodes(nextBtn);
+ options.nextArrow.forEach((node) => {
+ nextBtn.appendChild(node.cloneNode(true));
+ });
+ }
+ if (options.locale) {
+ picker.controls.todayBtn.textContent = options.locale.today;
+ picker.controls.clearBtn.textContent = options.locale.clear;
+ }
+ if (options.todayBtn !== undefined) {
+ if (options.todayBtn) {
+ showElement(picker.controls.todayBtn);
+ } else {
+ hideElement(picker.controls.todayBtn);
+ }
+ }
+ if (hasProperty(options, 'minDate') || hasProperty(options, 'maxDate')) {
+ const {minDate, maxDate} = picker.datepicker.config;
+ picker.controls.todayBtn.disabled = !isInRange(today(), minDate, maxDate);
+ }
+ if (options.clearBtn !== undefined) {
+ if (options.clearBtn) {
+ showElement(picker.controls.clearBtn);
+ } else {
+ hideElement(picker.controls.clearBtn);
+ }
+ }
+}
+
+// Compute view date to reset, which will be...
+// - the last item of the selected dates or defaultViewDate if no selection
+// - limitted to minDate or maxDate if it exceeds the range
+function computeResetViewDate(datepicker) {
+ const {dates, config} = datepicker;
+ const viewDate = dates.length > 0 ? lastItemOf(dates) : config.defaultViewDate;
+ return limitToRange(viewDate, config.minDate, config.maxDate);
+}
+
+// Change current view's view date
+function setViewDate(picker, newDate) {
+ const oldViewDate = new Date(picker.viewDate);
+ const newViewDate = new Date(newDate);
+ const {id, year, first, last} = picker.currentView;
+ const viewYear = newViewDate.getFullYear();
+
+ picker.viewDate = newDate;
+ if (viewYear !== oldViewDate.getFullYear()) {
+ triggerDatepickerEvent(picker.datepicker, 'changeYear');
+ }
+ if (newViewDate.getMonth() !== oldViewDate.getMonth()) {
+ triggerDatepickerEvent(picker.datepicker, 'changeMonth');
+ }
+
+ // return whether the new date is in different period on time from the one
+ // displayed in the current view
+ // when true, the view needs to be re-rendered on the next UI refresh.
+ switch (id) {
+ case 0:
+ return newDate < first || newDate > last;
+ case 1:
+ return viewYear !== year;
+ default:
+ return viewYear < first || viewYear > last;
+ }
+}
+
+function getTextDirection(el) {
+ return window.getComputedStyle(el).direction;
+}
+
+// find the closet scrollable ancestor elemnt under the body
+function findScrollParents(el) {
+ const parent = getParent(el);
+ if (parent === document.body || !parent) {
+ return;
+ }
+
+ // checking overflow only is enough because computed overflow cannot be
+ // visible or a combination of visible and other when either axis is set
+ // to other than visible.
+ // (Setting one axis to other than 'visible' while the other is 'visible'
+ // results in the other axis turning to 'auto')
+ return window.getComputedStyle(parent).overflow !== 'visible'
+ ? parent
+ : findScrollParents(parent);
+}
+
+// Class representing the picker UI
+export default class Picker {
+ constructor(datepicker) {
+ const {config} = this.datepicker = datepicker;
+
+ const template = pickerTemplate.replace(/%buttonClass%/g, config.buttonClass);
+ const element = this.element = parseHTML(template).firstChild;
+ const [header, main, footer] = element.firstChild.children;
+ const title = header.firstElementChild;
+ const [prevBtn, viewSwitch, nextBtn] = header.lastElementChild.children;
+ const [todayBtn, clearBtn] = footer.firstChild.children;
+ const controls = {
+ title,
+ prevBtn,
+ viewSwitch,
+ nextBtn,
+ todayBtn,
+ clearBtn,
+ };
+ this.main = main;
+ this.controls = controls;
+
+ const elementClass = datepicker.inline ? 'inline' : 'dropdown';
+ element.classList.add(`datepicker-${elementClass}`);
+
+ processPickerOptions(this, config);
+ this.viewDate = computeResetViewDate(datepicker);
+
+ // set up event listeners
+ registerListeners(datepicker, [
+ [element, 'mousedown', onMousedownPicker],
+ [main, 'click', onClickView.bind(null, datepicker)],
+ [controls.viewSwitch, 'click', onClickViewSwitch.bind(null, datepicker)],
+ [controls.prevBtn, 'click', onClickPrevBtn.bind(null, datepicker)],
+ [controls.nextBtn, 'click', onClickNextBtn.bind(null, datepicker)],
+ [controls.todayBtn, 'click', onClickTodayBtn.bind(null, datepicker)],
+ [controls.clearBtn, 'click', onClickClearBtn.bind(null, datepicker)],
+ ]);
+
+ // set up views
+ this.views = [
+ new DaysView(this),
+ new MonthsView(this),
+ new YearsView(this, {id: 2, name: 'years', cellClass: 'year', step: 1}),
+ new YearsView(this, {id: 3, name: 'decades', cellClass: 'decade', step: 10}),
+ ];
+ this.currentView = this.views[config.startView];
+
+ this.currentView.render();
+ this.main.appendChild(this.currentView.element);
+ if (config.container) {
+ config.container.appendChild(this.element);
+ } else {
+ datepicker.inputField.after(this.element);
+ }
+ }
+
+ setOptions(options) {
+ processPickerOptions(this, options);
+ this.views.forEach((view) => {
+ view.init(options, false);
+ });
+ this.currentView.render();
+ }
+
+ detach() {
+ this.element.remove();
+ }
+
+ show() {
+ if (this.active) {
+ return;
+ }
+
+ const {datepicker, element} = this;
+ if (datepicker.inline) {
+ element.classList.add('active');
+ } else {
+ // ensure picker's direction matches input's
+ const inputDirection = getTextDirection(datepicker.inputField);
+ if (inputDirection !== getTextDirection(getParent(element))) {
+ element.dir = inputDirection;
+ } else if (element.dir) {
+ element.removeAttribute('dir');
+ }
+
+ element.style.visiblity = 'hidden';
+ element.classList.add('active');
+ this.place();
+ element.style.visiblity = '';
+
+ if (datepicker.config.disableTouchKeyboard) {
+ datepicker.inputField.blur();
+ }
+ }
+ this.active = true;
+ triggerDatepickerEvent(datepicker, 'show');
+ }
+
+ hide() {
+ if (!this.active) {
+ return;
+ }
+ this.datepicker.exitEditMode();
+ this.element.classList.remove('active');
+ this.active = false;
+ triggerDatepickerEvent(this.datepicker, 'hide');
+ }
+
+ place() {
+ const {classList, offsetParent, style} = this.element;
+ const {config, inputField} = this.datepicker;
+ const {
+ width: calendarWidth,
+ height: calendarHeight,
+ } = this.element.getBoundingClientRect();
+ const {
+ left: inputLeft,
+ top: inputTop,
+ right: inputRight,
+ bottom: inputBottom,
+ width: inputWidth,
+ height: inputHeight
+ } = inputField.getBoundingClientRect();
+ let {x: orientX, y: orientY} = config.orientation;
+ let left = inputLeft;
+ let top = inputTop;
+
+ // caliculate offsetLeft/Top of inputField
+ if (offsetParent === document.body || !offsetParent) {
+ left += window.scrollX;
+ top += window.scrollY;
+ } else {
+ const offsetParentRect = offsetParent.getBoundingClientRect();
+ left -= offsetParentRect.left - offsetParent.scrollLeft;
+ top -= offsetParentRect.top - offsetParent.scrollTop;
+ }
+
+ // caliculate the boundaries of the visible area that contains inputField
+ const scrollParent = findScrollParents(inputField);
+ let scrollAreaLeft = 0;
+ let scrollAreaTop = 0;
+ let {
+ clientWidth: scrollAreaRight,
+ clientHeight: scrollAreaBottom,
+ } = document.documentElement;
+
+ if (scrollParent) {
+ const scrollParentRect = scrollParent.getBoundingClientRect();
+ if (scrollParentRect.top > 0) {
+ scrollAreaTop = scrollParentRect.top;
+ }
+ if (scrollParentRect.left > 0) {
+ scrollAreaLeft = scrollParentRect.left;
+ }
+ if (scrollParentRect.right < scrollAreaRight) {
+ scrollAreaRight = scrollParentRect.right;
+ }
+ if (scrollParentRect.bottom < scrollAreaBottom) {
+ scrollAreaBottom = scrollParentRect.bottom;
+ }
+ }
+
+ // determine the horizontal orientation and left position
+ let adjustment = 0;
+ if (orientX === 'auto') {
+ if (inputLeft < scrollAreaLeft) {
+ orientX = 'left';
+ adjustment = scrollAreaLeft - inputLeft;
+ } else if (inputLeft + calendarWidth > scrollAreaRight) {
+ orientX = 'right';
+ if (scrollAreaRight < inputRight) {
+ adjustment = scrollAreaRight - inputRight;
+ }
+ } else if (getTextDirection(inputField) === 'rtl') {
+ orientX = inputRight - calendarWidth < scrollAreaLeft ? 'left' : 'right';
+ } else {
+ orientX = 'left';
+ }
+ }
+ if (orientX === 'right') {
+ left += inputWidth - calendarWidth;
+ }
+ left += adjustment;
+
+ // determine the vertical orientation and top position
+ if (orientY === 'auto') {
+ if (inputTop - calendarHeight > scrollAreaTop) {
+ orientY = inputBottom + calendarHeight > scrollAreaBottom ? 'top' : 'bottom';
+ } else {
+ orientY = 'bottom';
+ }
+ }
+ if (orientY === 'top') {
+ top -= calendarHeight;
+ } else {
+ top += inputHeight;
+ }
+
+ classList.remove(...Object.values(orientClasses));
+ classList.add(orientClasses[orientX], orientClasses[orientY]);
+
+ style.left = toPx(left);
+ style.top = toPx(top);
+ }
+
+ setViewSwitchLabel(labelText) {
+ this.controls.viewSwitch.textContent = labelText;
+ }
+
+ setPrevBtnDisabled(disabled) {
+ this.controls.prevBtn.disabled = disabled;
+ }
+
+ setNextBtnDisabled(disabled) {
+ this.controls.nextBtn.disabled = disabled;
+ }
+
+ changeView(viewId) {
+ const oldView = this.currentView;
+ const newView = this.views[viewId];
+ if (newView.id !== oldView.id) {
+ this.currentView = newView;
+ this._renderMethod = 'render';
+ triggerDatepickerEvent(this.datepicker, 'changeView');
+ this.main.replaceChild(newView.element, oldView.element);
+ }
+ return this;
+ }
+
+ // Change the focused date (view date)
+ changeFocus(newViewDate) {
+ this._renderMethod = setViewDate(this, newViewDate) ? 'render' : 'refreshFocus';
+ this.views.forEach((view) => {
+ view.updateFocus();
+ });
+ return this;
+ }
+
+ // Apply the change of the selected dates
+ update() {
+ const newViewDate = computeResetViewDate(this.datepicker);
+ this._renderMethod = setViewDate(this, newViewDate) ? 'render' : 'refresh';
+ this.views.forEach((view) => {
+ view.updateFocus();
+ view.updateSelection();
+ });
+ return this;
+ }
+
+ // Refresh the picker UI
+ render(quickRender = true) {
+ const renderMethod = (quickRender && this._renderMethod) || 'render';
+ delete this._renderMethod;
+
+ this.currentView[renderMethod]();
+ }
+}
diff --git a/src/ui/static/js/datepicker/picker/templates/calendarWeeksTemplate.js b/src/ui/static/js/datepicker/picker/templates/calendarWeeksTemplate.js
new file mode 100644
index 000000000..ca70ae727
--- /dev/null
+++ b/src/ui/static/js/datepicker/picker/templates/calendarWeeksTemplate.js
@@ -0,0 +1,8 @@
+import {createTagRepeat, optimizeTemplateHTML} from '../../lib/utils.js';
+
+const calendarWeeksTemplate = optimizeTemplateHTML(`
+
+
${createTagRepeat('span', 6, {class: 'week'})}
+
`);
+
+export default calendarWeeksTemplate;
diff --git a/src/ui/static/js/datepicker/picker/templates/daysTemplate.js b/src/ui/static/js/datepicker/picker/templates/daysTemplate.js
new file mode 100644
index 000000000..c51c6d46d
--- /dev/null
+++ b/src/ui/static/js/datepicker/picker/templates/daysTemplate.js
@@ -0,0 +1,8 @@
+import {createTagRepeat, optimizeTemplateHTML} from '../../lib/utils.js';
+
+const daysTemplate = optimizeTemplateHTML(`
+
${createTagRepeat('span', 7, {class: 'dow'})}
+
${createTagRepeat('span', 42)}
+
`);
+
+export default daysTemplate;
diff --git a/src/ui/static/js/datepicker/picker/templates/pickerTemplate.js b/src/ui/static/js/datepicker/picker/templates/pickerTemplate.js
new file mode 100644
index 000000000..b389a4eb0
--- /dev/null
+++ b/src/ui/static/js/datepicker/picker/templates/pickerTemplate.js
@@ -0,0 +1,23 @@
+import {optimizeTemplateHTML} from '../../lib/utils.js';
+
+const pickerTemplate = optimizeTemplateHTML(``);
+
+export default pickerTemplate;
diff --git a/src/ui/static/js/datepicker/picker/views/DaysView.js b/src/ui/static/js/datepicker/picker/views/DaysView.js
new file mode 100644
index 000000000..de3c33617
--- /dev/null
+++ b/src/ui/static/js/datepicker/picker/views/DaysView.js
@@ -0,0 +1,238 @@
+import {hasProperty, pushUnique} from '../../lib/utils.js';
+import {today, dateValue, addDays, addWeeks, dayOfTheWeekOf, getWeek} from '../../lib/date.js';
+import {formatDate} from '../../lib/date-format.js';
+import {parseHTML, showElement, hideElement} from '../../lib/dom.js';
+import daysTemplate from '../templates/daysTemplate.js';
+import calendarWeeksTemplate from '../templates/calendarWeeksTemplate.js';
+import View from './View.js';
+
+export default class DaysView extends View {
+ constructor(picker) {
+ super(picker, {
+ id: 0,
+ name: 'days',
+ cellClass: 'day',
+ });
+ }
+
+ init(options, onConstruction = true) {
+ if (onConstruction) {
+ const inner = parseHTML(daysTemplate).firstChild;
+ this.dow = inner.firstChild;
+ this.grid = inner.lastChild;
+ this.element.appendChild(inner);
+ }
+ super.init(options);
+ }
+
+ setOptions(options) {
+ let updateDOW;
+
+ if (hasProperty(options, 'minDate')) {
+ this.minDate = options.minDate;
+ }
+ if (hasProperty(options, 'maxDate')) {
+ this.maxDate = options.maxDate;
+ }
+ if (options.datesDisabled) {
+ this.datesDisabled = options.datesDisabled;
+ }
+ if (options.daysOfWeekDisabled) {
+ this.daysOfWeekDisabled = options.daysOfWeekDisabled;
+ updateDOW = true;
+ }
+ if (options.daysOfWeekHighlighted) {
+ this.daysOfWeekHighlighted = options.daysOfWeekHighlighted;
+ }
+ if (options.todayHighlight !== undefined) {
+ this.todayHighlight = options.todayHighlight;
+ }
+ if (options.weekStart !== undefined) {
+ this.weekStart = options.weekStart;
+ this.weekEnd = options.weekEnd;
+ updateDOW = true;
+ }
+ if (options.locale) {
+ const locale = this.locale = options.locale;
+ this.dayNames = locale.daysMin;
+ this.switchLabelFormat = locale.titleFormat;
+ updateDOW = true;
+ }
+ if (options.beforeShowDay !== undefined) {
+ this.beforeShow = typeof options.beforeShowDay === 'function'
+ ? options.beforeShowDay
+ : undefined;
+ }
+
+ if (options.calendarWeeks !== undefined) {
+ if (options.calendarWeeks && !this.calendarWeeks) {
+ const weeksElem = parseHTML(calendarWeeksTemplate).firstChild;
+ this.calendarWeeks = {
+ element: weeksElem,
+ dow: weeksElem.firstChild,
+ weeks: weeksElem.lastChild,
+ };
+ this.element.insertBefore(weeksElem, this.element.firstChild);
+ } else if (this.calendarWeeks && !options.calendarWeeks) {
+ this.element.removeChild(this.calendarWeeks.element);
+ this.calendarWeeks = null;
+ }
+ }
+ if (options.showDaysOfWeek !== undefined) {
+ if (options.showDaysOfWeek) {
+ showElement(this.dow);
+ if (this.calendarWeeks) {
+ showElement(this.calendarWeeks.dow);
+ }
+ } else {
+ hideElement(this.dow);
+ if (this.calendarWeeks) {
+ hideElement(this.calendarWeeks.dow);
+ }
+ }
+ }
+
+ // update days-of-week when locale, daysOfweekDisabled or weekStart is changed
+ if (updateDOW) {
+ Array.from(this.dow.children).forEach((el, index) => {
+ const dow = (this.weekStart + index) % 7;
+ el.textContent = this.dayNames[dow];
+ el.className = this.daysOfWeekDisabled.includes(dow) ? 'dow disabled' : 'dow';
+ });
+ }
+ }
+
+ // Apply update on the focused date to view's settings
+ updateFocus() {
+ const viewDate = new Date(this.picker.viewDate);
+ const viewYear = viewDate.getFullYear();
+ const viewMonth = viewDate.getMonth();
+ const firstOfMonth = dateValue(viewYear, viewMonth, 1);
+ const start = dayOfTheWeekOf(firstOfMonth, this.weekStart, this.weekStart);
+
+ this.first = firstOfMonth;
+ this.last = dateValue(viewYear, viewMonth + 1, 0);
+ this.start = start;
+ this.focused = this.picker.viewDate;
+ }
+
+ // Apply update on the selected dates to view's settings
+ updateSelection() {
+ const {dates, rangepicker} = this.picker.datepicker;
+ this.selected = dates;
+ if (rangepicker) {
+ this.range = rangepicker.dates;
+ }
+ }
+
+ // Update the entire view UI
+ render() {
+ // update today marker on ever render
+ this.today = this.todayHighlight ? today() : undefined;
+ // refresh disabled dates on every render in order to clear the ones added
+ // by beforeShow hook at previous render
+ this.disabled = [...this.datesDisabled];
+
+ const switchLabel = formatDate(this.focused, this.switchLabelFormat, this.locale);
+ this.picker.setViewSwitchLabel(switchLabel);
+ this.picker.setPrevBtnDisabled(this.first <= this.minDate);
+ this.picker.setNextBtnDisabled(this.last >= this.maxDate);
+
+ if (this.calendarWeeks) {
+ // start of the UTC week (Monday) of the 1st of the month
+ const startOfWeek = dayOfTheWeekOf(this.first, 1, 1);
+ Array.from(this.calendarWeeks.weeks.children).forEach((el, index) => {
+ el.textContent = getWeek(addWeeks(startOfWeek, index));
+ });
+ }
+ Array.from(this.grid.children).forEach((el, index) => {
+ const classList = el.classList;
+ const current = addDays(this.start, index);
+ const date = new Date(current);
+ const day = date.getDay();
+
+ el.className = `datepicker-cell ${this.cellClass}`;
+ el.dataset.date = current;
+ el.textContent = date.getDate();
+
+ if (current < this.first) {
+ classList.add('prev');
+ } else if (current > this.last) {
+ classList.add('next');
+ }
+ if (this.today === current) {
+ classList.add('today');
+ }
+ if (current < this.minDate || current > this.maxDate || this.disabled.includes(current)) {
+ classList.add('disabled');
+ }
+ if (this.daysOfWeekDisabled.includes(day)) {
+ classList.add('disabled');
+ pushUnique(this.disabled, current);
+ }
+ if (this.daysOfWeekHighlighted.includes(day)) {
+ classList.add('highlighted');
+ }
+ if (this.range) {
+ const [rangeStart, rangeEnd] = this.range;
+ if (current > rangeStart && current < rangeEnd) {
+ classList.add('range');
+ }
+ if (current === rangeStart) {
+ classList.add('range-start');
+ }
+ if (current === rangeEnd) {
+ classList.add('range-end');
+ }
+ }
+ if (this.selected.includes(current)) {
+ classList.add('selected');
+ }
+ if (current === this.focused) {
+ classList.add('focused');
+ }
+
+ if (this.beforeShow) {
+ this.performBeforeHook(el, current, current);
+ }
+ });
+ }
+
+ // Update the view UI by applying the changes of selected and focused items
+ refresh() {
+ const [rangeStart, rangeEnd] = this.range || [];
+ this.grid
+ .querySelectorAll('.range, .range-start, .range-end, .selected, .focused')
+ .forEach((el) => {
+ el.classList.remove('range', 'range-start', 'range-end', 'selected', 'focused');
+ });
+ Array.from(this.grid.children).forEach((el) => {
+ const current = Number(el.dataset.date);
+ const classList = el.classList;
+ if (current > rangeStart && current < rangeEnd) {
+ classList.add('range');
+ }
+ if (current === rangeStart) {
+ classList.add('range-start');
+ }
+ if (current === rangeEnd) {
+ classList.add('range-end');
+ }
+ if (this.selected.includes(current)) {
+ classList.add('selected');
+ }
+ if (current === this.focused) {
+ classList.add('focused');
+ }
+ });
+ }
+
+ // Update the view UI by applying the change of focused item
+ refreshFocus() {
+ const index = Math.round((this.focused - this.start) / 86400000);
+ this.grid.querySelectorAll('.focused').forEach((el) => {
+ el.classList.remove('focused');
+ });
+ this.grid.children[index].classList.add('focused');
+ }
+}
diff --git a/src/ui/static/js/datepicker/picker/views/MonthsView.js b/src/ui/static/js/datepicker/picker/views/MonthsView.js
new file mode 100644
index 000000000..af4f6432c
--- /dev/null
+++ b/src/ui/static/js/datepicker/picker/views/MonthsView.js
@@ -0,0 +1,210 @@
+import {hasProperty, pushUnique, createTagRepeat} from '../../lib/utils.js';
+import {dateValue} from '../../lib/date.js';
+import {parseHTML} from '../../lib/dom.js';
+import View from './View.js';
+
+function computeMonthRange(range, thisYear) {
+ if (!range || !range[0] || !range[1]) {
+ return;
+ }
+
+ const [[startY, startM], [endY, endM]] = range;
+ if (startY > thisYear || endY < thisYear) {
+ return;
+ }
+ return [
+ startY === thisYear ? startM : -1,
+ endY === thisYear ? endM : 12,
+ ];
+}
+
+export default class MonthsView extends View {
+ constructor(picker) {
+ super(picker, {
+ id: 1,
+ name: 'months',
+ cellClass: 'month',
+ });
+ }
+
+ init(options, onConstruction = true) {
+ if (onConstruction) {
+ this.grid = this.element;
+ this.element.classList.add('months', 'datepicker-grid');
+ this.grid.appendChild(parseHTML(createTagRepeat('span', 12, {'data-month': ix => ix})));
+ }
+ super.init(options);
+ }
+
+ setOptions(options) {
+ if (options.locale) {
+ this.monthNames = options.locale.monthsShort;
+ }
+ if (hasProperty(options, 'minDate')) {
+ if (options.minDate === undefined) {
+ this.minYear = this.minMonth = this.minDate = undefined;
+ } else {
+ const minDateObj = new Date(options.minDate);
+ this.minYear = minDateObj.getFullYear();
+ this.minMonth = minDateObj.getMonth();
+ this.minDate = minDateObj.setDate(1);
+ }
+ }
+ if (hasProperty(options, 'maxDate')) {
+ if (options.maxDate === undefined) {
+ this.maxYear = this.maxMonth = this.maxDate = undefined;
+ } else {
+ const maxDateObj = new Date(options.maxDate);
+ this.maxYear = maxDateObj.getFullYear();
+ this.maxMonth = maxDateObj.getMonth();
+ this.maxDate = dateValue(this.maxYear, this.maxMonth + 1, 0);
+ }
+ }
+ if (this.isMinView) {
+ if (options.datesDisabled) {
+ this.datesDisabled = options.datesDisabled;
+ }
+ } else {
+ this.datesDisabled = [];
+ }
+ if (options.beforeShowMonth !== undefined) {
+ this.beforeShow = typeof options.beforeShowMonth === 'function'
+ ? options.beforeShowMonth
+ : undefined;
+ }
+ }
+
+ // Update view's settings to reflect the viewDate set on the picker
+ updateFocus() {
+ const viewDate = new Date(this.picker.viewDate);
+ this.year = viewDate.getFullYear();
+ this.focused = viewDate.getMonth();
+ }
+
+ // Update view's settings to reflect the selected dates
+ updateSelection() {
+ const {dates, rangepicker} = this.picker.datepicker;
+ this.selected = dates.reduce((selected, timeValue) => {
+ const date = new Date(timeValue);
+ const year = date.getFullYear();
+ const month = date.getMonth();
+ if (selected[year] === undefined) {
+ selected[year] = [month];
+ } else {
+ pushUnique(selected[year], month);
+ }
+ return selected;
+ }, {});
+ if (rangepicker && rangepicker.dates) {
+ this.range = rangepicker.dates.map(timeValue => {
+ const date = new Date(timeValue);
+ return isNaN(date) ? undefined : [date.getFullYear(), date.getMonth()];
+ });
+ }
+ }
+
+ // Update the entire view UI
+ render() {
+ // refresh disabled months on every render in order to clear the ones added
+ // by beforeShow hook at previous render
+ // this.disabled = [...this.datesDisabled];
+ this.disabled = this.datesDisabled.reduce((arr, disabled) => {
+ const dt = new Date(disabled);
+ if (this.year === dt.getFullYear()) {
+ arr.push(dt.getMonth());
+ }
+ return arr;
+ }, []);
+
+ this.picker.setViewSwitchLabel(this.year);
+ this.picker.setPrevBtnDisabled(this.year <= this.minYear);
+ this.picker.setNextBtnDisabled(this.year >= this.maxYear);
+
+ const selected = this.selected[this.year] || [];
+ const yrOutOfRange = this.year < this.minYear || this.year > this.maxYear;
+ const isMinYear = this.year === this.minYear;
+ const isMaxYear = this.year === this.maxYear;
+ const range = computeMonthRange(this.range, this.year);
+
+ Array.from(this.grid.children).forEach((el, index) => {
+ const classList = el.classList;
+ const date = dateValue(this.year, index, 1);
+
+ el.className = `datepicker-cell ${this.cellClass}`;
+ if (this.isMinView) {
+ el.dataset.date = date;
+ }
+ // reset text on every render to clear the custom content set
+ // by beforeShow hook at previous render
+ el.textContent = this.monthNames[index];
+
+ if (
+ yrOutOfRange
+ || isMinYear && index < this.minMonth
+ || isMaxYear && index > this.maxMonth
+ || this.disabled.includes(index)
+ ) {
+ classList.add('disabled');
+ }
+ if (range) {
+ const [rangeStart, rangeEnd] = range;
+ if (index > rangeStart && index < rangeEnd) {
+ classList.add('range');
+ }
+ if (index === rangeStart) {
+ classList.add('range-start');
+ }
+ if (index === rangeEnd) {
+ classList.add('range-end');
+ }
+ }
+ if (selected.includes(index)) {
+ classList.add('selected');
+ }
+ if (index === this.focused) {
+ classList.add('focused');
+ }
+
+ if (this.beforeShow) {
+ this.performBeforeHook(el, index, date);
+ }
+ });
+ }
+
+ // Update the view UI by applying the changes of selected and focused items
+ refresh() {
+ const selected = this.selected[this.year] || [];
+ const [rangeStart, rangeEnd] = computeMonthRange(this.range, this.year) || [];
+ this.grid
+ .querySelectorAll('.range, .range-start, .range-end, .selected, .focused')
+ .forEach((el) => {
+ el.classList.remove('range', 'range-start', 'range-end', 'selected', 'focused');
+ });
+ Array.from(this.grid.children).forEach((el, index) => {
+ const classList = el.classList;
+ if (index > rangeStart && index < rangeEnd) {
+ classList.add('range');
+ }
+ if (index === rangeStart) {
+ classList.add('range-start');
+ }
+ if (index === rangeEnd) {
+ classList.add('range-end');
+ }
+ if (selected.includes(index)) {
+ classList.add('selected');
+ }
+ if (index === this.focused) {
+ classList.add('focused');
+ }
+ });
+ }
+
+ // Update the view UI by applying the change of focused item
+ refreshFocus() {
+ this.grid.querySelectorAll('.focused').forEach((el) => {
+ el.classList.remove('focused');
+ });
+ this.grid.children[this.focused].classList.add('focused');
+ }
+}
\ No newline at end of file
diff --git a/src/ui/static/js/datepicker/picker/views/View.js b/src/ui/static/js/datepicker/picker/views/View.js
new file mode 100644
index 000000000..fe9123136
--- /dev/null
+++ b/src/ui/static/js/datepicker/picker/views/View.js
@@ -0,0 +1,55 @@
+import {pushUnique} from '../../lib/utils.js';
+import {parseHTML, replaceChildNodes} from '../../lib/dom.js';
+
+// Base class of the view classes
+export default class View {
+ constructor(picker, config) {
+ Object.assign(this, config, {
+ picker,
+ element: parseHTML(``).firstChild,
+ selected: [],
+ });
+ this.init(this.picker.datepicker.config);
+ }
+
+ init(options) {
+ if (options.pickLevel !== undefined) {
+ this.isMinView = this.id === options.pickLevel;
+ }
+ this.setOptions(options);
+ this.updateFocus();
+ this.updateSelection();
+ }
+
+ // Execute beforeShow() callback and apply the result to the element
+ // args:
+ // - current - current value on the iteration on view rendering
+ // - timeValue - time value of the date to pass to beforeShow()
+ performBeforeHook(el, current, timeValue) {
+ let result = this.beforeShow(new Date(timeValue));
+ switch (typeof result) {
+ case 'boolean':
+ result = {enabled: result};
+ break;
+ case 'string':
+ result = {classes: result};
+ }
+
+ if (result) {
+ if (result.enabled === false) {
+ el.classList.add('disabled');
+ pushUnique(this.disabled, current);
+ }
+ if (result.classes) {
+ const extraClasses = result.classes.split(/\s+/);
+ el.classList.add(...extraClasses);
+ if (extraClasses.includes('disabled')) {
+ pushUnique(this.disabled, current);
+ }
+ }
+ if (result.content) {
+ replaceChildNodes(el, result.content);
+ }
+ }
+ }
+}
diff --git a/src/ui/static/js/datepicker/picker/views/YearsView.js b/src/ui/static/js/datepicker/picker/views/YearsView.js
new file mode 100644
index 000000000..5faed1f91
--- /dev/null
+++ b/src/ui/static/js/datepicker/picker/views/YearsView.js
@@ -0,0 +1,176 @@
+import {hasProperty, pushUnique, createTagRepeat} from '../../lib/utils.js';
+import {dateValue, startOfYearPeriod} from '../../lib/date.js';
+import {parseHTML} from '../../lib/dom.js';
+import View from './View.js';
+
+function toTitleCase(word) {
+ return [...word].reduce((str, ch, ix) => str += ix ? ch : ch.toUpperCase(), '');
+}
+
+// Class representing the years and decades view elements
+export default class YearsView extends View {
+ constructor(picker, config) {
+ super(picker, config);
+ }
+
+ init(options, onConstruction = true) {
+ if (onConstruction) {
+ this.navStep = this.step * 10;
+ this.beforeShowOption = `beforeShow${toTitleCase(this.cellClass)}`;
+ this.grid = this.element;
+ this.element.classList.add(this.name, 'datepicker-grid');
+ this.grid.appendChild(parseHTML(createTagRepeat('span', 12)));
+ }
+ super.init(options);
+ }
+
+ setOptions(options) {
+ if (hasProperty(options, 'minDate')) {
+ if (options.minDate === undefined) {
+ this.minYear = this.minDate = undefined;
+ } else {
+ this.minYear = startOfYearPeriod(options.minDate, this.step);
+ this.minDate = dateValue(this.minYear, 0, 1);
+ }
+ }
+ if (hasProperty(options, 'maxDate')) {
+ if (options.maxDate === undefined) {
+ this.maxYear = this.maxDate = undefined;
+ } else {
+ this.maxYear = startOfYearPeriod(options.maxDate, this.step);
+ this.maxDate = dateValue(this.maxYear, 11, 31);
+ }
+ }
+ if (this.isMinView) {
+ if (options.datesDisabled) {
+ this.datesDisabled = options.datesDisabled;
+ }
+ } else {
+ this.datesDisabled = [];
+ }
+ if (options[this.beforeShowOption] !== undefined) {
+ const beforeShow = options[this.beforeShowOption];
+ this.beforeShow = typeof beforeShow === 'function' ? beforeShow : undefined;
+ }
+ }
+
+ // Update view's settings to reflect the viewDate set on the picker
+ updateFocus() {
+ const viewDate = new Date(this.picker.viewDate);
+ const first = startOfYearPeriod(viewDate, this.navStep);
+ const last = first + 9 * this.step;
+
+ this.first = first;
+ this.last = last;
+ this.start = first - this.step;
+ this.focused = startOfYearPeriod(viewDate, this.step);
+ }
+
+ // Update view's settings to reflect the selected dates
+ updateSelection() {
+ const {dates, rangepicker} = this.picker.datepicker;
+ this.selected = dates.reduce((years, timeValue) => {
+ return pushUnique(years, startOfYearPeriod(timeValue, this.step));
+ }, []);
+ if (rangepicker && rangepicker.dates) {
+ this.range = rangepicker.dates.map(timeValue => {
+ if (timeValue !== undefined) {
+ return startOfYearPeriod(timeValue, this.step);
+ }
+ });
+ }
+ }
+
+ // Update the entire view UI
+ render() {
+ // refresh disabled years on every render in order to clear the ones added
+ // by beforeShow hook at previous render
+ // this.disabled = [...this.datesDisabled];
+ this.disabled = this.datesDisabled.map(disabled => new Date(disabled).getFullYear());
+
+ this.picker.setViewSwitchLabel(`${this.first}-${this.last}`);
+ this.picker.setPrevBtnDisabled(this.first <= this.minYear);
+ this.picker.setNextBtnDisabled(this.last >= this.maxYear);
+
+ Array.from(this.grid.children).forEach((el, index) => {
+ const classList = el.classList;
+ const current = this.start + (index * this.step);
+ const date = dateValue(current, 0, 1);
+
+ el.className = `datepicker-cell ${this.cellClass}`;
+ if (this.isMinView) {
+ el.dataset.date = date;
+ }
+ el.textContent = el.dataset.year = current;
+
+ if (index === 0) {
+ classList.add('prev');
+ } else if (index === 11) {
+ classList.add('next');
+ }
+ if (current < this.minYear || current > this.maxYear || this.disabled.includes(current)) {
+ classList.add('disabled');
+ }
+ if (this.range) {
+ const [rangeStart, rangeEnd] = this.range;
+ if (current > rangeStart && current < rangeEnd) {
+ classList.add('range');
+ }
+ if (current === rangeStart) {
+ classList.add('range-start');
+ }
+ if (current === rangeEnd) {
+ classList.add('range-end');
+ }
+ }
+ if (this.selected.includes(current)) {
+ classList.add('selected');
+ }
+ if (current === this.focused) {
+ classList.add('focused');
+ }
+
+ if (this.beforeShow) {
+ this.performBeforeHook(el, current, date);
+ }
+ });
+ }
+
+ // Update the view UI by applying the changes of selected and focused items
+ refresh() {
+ const [rangeStart, rangeEnd] = this.range || [];
+ this.grid
+ .querySelectorAll('.range, .range-start, .range-end, .selected, .focused')
+ .forEach((el) => {
+ el.classList.remove('range', 'range-start', 'range-end', 'selected', 'focused');
+ });
+ Array.from(this.grid.children).forEach((el) => {
+ const current = Number(el.textContent);
+ const classList = el.classList;
+ if (current > rangeStart && current < rangeEnd) {
+ classList.add('range');
+ }
+ if (current === rangeStart) {
+ classList.add('range-start');
+ }
+ if (current === rangeEnd) {
+ classList.add('range-end');
+ }
+ if (this.selected.includes(current)) {
+ classList.add('selected');
+ }
+ if (current === this.focused) {
+ classList.add('focused');
+ }
+ });
+ }
+
+ // Update the view UI by applying the change of focused item
+ refreshFocus() {
+ const index = Math.round((this.focused - this.start) / this.step);
+ this.grid.querySelectorAll('.focused').forEach((el) => {
+ el.classList.remove('focused');
+ });
+ this.grid.children[index].classList.add('focused');
+ }
+}
diff --git a/src/ui/static/js/dropzone/dropzone-min.js b/src/ui/static/js/dropzone/dropzone-min.js
index cfced1204..e14ac2316 100644
--- a/src/ui/static/js/dropzone/dropzone-min.js
+++ b/src/ui/static/js/dropzone/dropzone-min.js
@@ -1,2 +1,2881 @@
-!function(){function e(e){return e&&e.__esModule?e.default:e}function t(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var i=0;i1?t-1:0),n=1;n'),this.element.appendChild(e));var l=e.getElementsByTagName("span")[0];return l&&(null!=l.textContent?l.textContent=this.options.dictFallbackMessage:null!=l.innerText&&(l.innerText=this.options.dictFallbackMessage)),this.element.appendChild(this.getFallbackForm())},resize:function(e,t,i,n){var r={srcX:0,srcY:0,srcWidth:e.width,srcHeight:e.height},a=e.width/e.height;null==t&&null==i?(t=r.srcWidth,i=r.srcHeight):null==t?t=i*a:null==i&&(i=t/a);var o=(t=Math.min(t,r.srcWidth))/(i=Math.min(i,r.srcHeight));if(r.srcWidth>t||r.srcHeight>i)if("crop"===n)a>o?(r.srcHeight=e.height,r.srcWidth=r.srcHeight*o):(r.srcWidth=e.width,r.srcHeight=r.srcWidth/o);else{if("contain"!==n)throw new Error("Unknown resizeMethod '".concat(n,"'"));a>o?i=t/a:t=i*a}return r.srcX=(e.width-r.srcWidth)/2,r.srcY=(e.height-r.srcHeight)/2,r.trgWidth=t,r.trgHeight=i,r},transformFile:function(e,t){return(this.options.resizeWidth||this.options.resizeHeight)&&e.type.match(/image.*/)?this.resizeImage(e,this.options.resizeWidth,this.options.resizeHeight,this.options.resizeMethod,t):t(e)},previewTemplate:e(''),drop:function(e){return this.element.classList.remove("dz-drag-hover")},dragstart:function(e){},dragend:function(e){return this.element.classList.remove("dz-drag-hover")},dragenter:function(e){return this.element.classList.add("dz-drag-hover")},dragover:function(e){return this.element.classList.add("dz-drag-hover")},dragleave:function(e){return this.element.classList.remove("dz-drag-hover")},paste:function(e){},reset:function(){return this.element.classList.remove("dz-started")},addedfile:function(e){if(this.element===this.previewsContainer&&this.element.classList.add("dz-started"),this.previewsContainer&&!this.options.disablePreviews){var t=this;e.previewElement=f.createElement(this.options.previewTemplate.trim()),e.previewTemplate=e.previewElement,this.previewsContainer.appendChild(e.previewElement);var i=!0,n=!1,r=void 0;try{for(var a,o=e.previewElement.querySelectorAll("[data-dz-name]")[Symbol.iterator]();!(i=(a=o.next()).done);i=!0){var l=a.value;l.textContent=e.name}}catch(e){n=!0,r=e}finally{try{i||null==o.return||o.return()}finally{if(n)throw r}}var s=!0,u=!1,c=void 0;try{for(var d,h=e.previewElement.querySelectorAll("[data-dz-size]")[Symbol.iterator]();!(s=(d=h.next()).done);s=!0)(l=d.value).innerHTML=this.filesize(e.size)}catch(e){u=!0,c=e}finally{try{s||null==h.return||h.return()}finally{if(u)throw c}}this.options.addRemoveLinks&&(e._removeLink=f.createElement(''.concat(this.options.dictRemoveFile,"")),e.previewElement.appendChild(e._removeLink));var p=function(i){var n=t;if(i.preventDefault(),i.stopPropagation(),e.status===f.UPLOADING)return f.confirm(t.options.dictCancelUploadConfirmation,(function(){return n.removeFile(e)}));var r=t;return t.options.dictRemoveFileConfirmation?f.confirm(t.options.dictRemoveFileConfirmation,(function(){return r.removeFile(e)})):t.removeFile(e)},m=!0,v=!1,y=void 0;try{for(var g,b=e.previewElement.querySelectorAll("[data-dz-remove]")[Symbol.iterator]();!(m=(g=b.next()).done);m=!0){g.value.addEventListener("click",p)}}catch(e){v=!0,y=e}finally{try{m||null==b.return||b.return()}finally{if(v)throw y}}}},removedfile:function(e){return null!=e.previewElement&&null!=e.previewElement.parentNode&&e.previewElement.parentNode.removeChild(e.previewElement),this._updateMaxFilesReachedClass()},thumbnail:function(e,t){if(e.previewElement){e.previewElement.classList.remove("dz-file-preview");var i=!0,n=!1,r=void 0;try{for(var a,o=e.previewElement.querySelectorAll("[data-dz-thumbnail]")[Symbol.iterator]();!(i=(a=o.next()).done);i=!0){var l=a.value;l.alt=e.name,l.src=t}}catch(e){n=!0,r=e}finally{try{i||null==o.return||o.return()}finally{if(n)throw r}}return setTimeout((function(){return e.previewElement.classList.add("dz-image-preview")}),1)}},error:function(e,t){if(e.previewElement){e.previewElement.classList.add("dz-error"),"string"!=typeof t&&t.error&&(t=t.error);var i=!0,n=!1,r=void 0;try{for(var a,o=e.previewElement.querySelectorAll("[data-dz-errormessage]")[Symbol.iterator]();!(i=(a=o.next()).done);i=!0){a.value.textContent=t}}catch(e){n=!0,r=e}finally{try{i||null==o.return||o.return()}finally{if(n)throw r}}}},errormultiple:function(){},processing:function(e){if(e.previewElement&&(e.previewElement.classList.add("dz-processing"),e._removeLink))return e._removeLink.innerHTML=this.options.dictCancelUpload},processingmultiple:function(){},uploadprogress:function(e,t,i){var n=!0,r=!1,a=void 0;if(e.previewElement)try{for(var o,l=e.previewElement.querySelectorAll("[data-dz-uploadprogress]")[Symbol.iterator]();!(n=(o=l.next()).done);n=!0){var s=o.value;"PROGRESS"===s.nodeName?s.value=t:s.style.width="".concat(t,"%")}}catch(e){r=!0,a=e}finally{try{n||null==l.return||l.return()}finally{if(r)throw a}}},totaluploadprogress:function(){},sending:function(){},sendingmultiple:function(){},success:function(e){if(e.previewElement)return e.previewElement.classList.add("dz-success")},successmultiple:function(){},canceled:function(e){return this.emit("error",e,this.options.dictUploadCanceled)},canceledmultiple:function(){},complete:function(e){if(e._removeLink&&(e._removeLink.innerHTML=this.options.dictRemoveFile),e.previewElement)return e.previewElement.classList.add("dz-complete")},completemultiple:function(){},maxfilesexceeded:function(){},maxfilesreached:function(){},queuecomplete:function(){},addedfiles:function(){}},f=function(n){"use strict";function o(n,r){var l,c,d,h;if(i(this,o),(l=s(this,(c=o,a(c)).call(this))).element=n,l.clickableElements=[],l.listeners=[],l.files=[],"string"==typeof l.element&&(l.element=document.querySelector(l.element)),!l.element||null==l.element.nodeType)throw new Error("Invalid dropzone element.");if(l.element.dropzone)throw new Error("Dropzone already attached.");o.instances.push(t(l)),l.element.dropzone=t(l);var f=null!=(h=o.optionsForElement(l.element))?h:{};if(l.options=e(u)(!0,{},p,f,null!=r?r:{}),l.options.previewTemplate=l.options.previewTemplate.replace(/\n*/g,""),l.options.forceFallback||!o.isBrowserSupported())return s(l,l.options.fallback.call(t(l)));if(null==l.options.url&&(l.options.url=l.element.getAttribute("action")),!l.options.url)throw new Error("No URL provided.");if(l.options.acceptedFiles&&l.options.acceptedMimeTypes)throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated.");if(l.options.uploadMultiple&&l.options.chunking)throw new Error("You cannot set both: uploadMultiple and chunking.");if(l.options.binaryBody&&l.options.uploadMultiple)throw new Error("You cannot set both: binaryBody and uploadMultiple.");return l.options.acceptedMimeTypes&&(l.options.acceptedFiles=l.options.acceptedMimeTypes,delete l.options.acceptedMimeTypes),null!=l.options.renameFilename&&(l.options.renameFile=function(e){return l.options.renameFilename.call(t(l),e.name,e)}),"string"==typeof l.options.method&&(l.options.method=l.options.method.toUpperCase()),(d=l.getExistingFallback())&&d.parentNode&&d.parentNode.removeChild(d),!1!==l.options.previewsContainer&&(l.options.previewsContainer?l.previewsContainer=o.getElement(l.options.previewsContainer,"previewsContainer"):l.previewsContainer=l.element),l.options.clickable&&(!0===l.options.clickable?l.clickableElements=[l.element]:l.clickableElements=o.getElements(l.options.clickable,"clickable")),l.init(),l}return l(o,n),r(o,[{key:"getAcceptedFiles",value:function(){return this.files.filter((function(e){return e.accepted})).map((function(e){return e}))}},{key:"getRejectedFiles",value:function(){return this.files.filter((function(e){return!e.accepted})).map((function(e){return e}))}},{key:"getFilesWithStatus",value:function(e){return this.files.filter((function(t){return t.status===e})).map((function(e){return e}))}},{key:"getQueuedFiles",value:function(){return this.getFilesWithStatus(o.QUEUED)}},{key:"getUploadingFiles",value:function(){return this.getFilesWithStatus(o.UPLOADING)}},{key:"getAddedFiles",value:function(){return this.getFilesWithStatus(o.ADDED)}},{key:"getActiveFiles",value:function(){return this.files.filter((function(e){return e.status===o.UPLOADING||e.status===o.QUEUED})).map((function(e){return e}))}},{key:"init",value:function(){var e=this,t=this,i=this,n=this,r=this,a=this,l=this,s=this,u=this,c=this,d=this;if("form"===this.element.tagName&&this.element.setAttribute("enctype","multipart/form-data"),this.element.classList.contains("dropzone")&&!this.element.querySelector(".dz-message")&&this.element.appendChild(o.createElement('"))),this.clickableElements.length){var h=this,p=function(){var e=h;h.hiddenFileInput&&h.hiddenFileInput.parentNode.removeChild(h.hiddenFileInput),h.hiddenFileInput=document.createElement("input"),h.hiddenFileInput.setAttribute("type","file"),(null===h.options.maxFiles||h.options.maxFiles>1)&&h.hiddenFileInput.setAttribute("multiple","multiple"),h.hiddenFileInput.className="dz-hidden-input",null!==h.options.acceptedFiles&&h.hiddenFileInput.setAttribute("accept",h.options.acceptedFiles),null!==h.options.capture&&h.hiddenFileInput.setAttribute("capture",h.options.capture),h.hiddenFileInput.setAttribute("tabindex","-1"),h.hiddenFileInput.style.visibility="hidden",h.hiddenFileInput.style.position="absolute",h.hiddenFileInput.style.top="0",h.hiddenFileInput.style.left="0",h.hiddenFileInput.style.height="0",h.hiddenFileInput.style.width="0",o.getElement(h.options.hiddenInputContainer,"hiddenInputContainer").appendChild(h.hiddenFileInput),h.hiddenFileInput.addEventListener("change",(function(){var t=e.hiddenFileInput.files,i=!0,n=!1,r=void 0;if(t.length)try{for(var a,o=t[Symbol.iterator]();!(i=(a=o.next()).done);i=!0){var l=a.value;e.addFile(l)}}catch(e){n=!0,r=e}finally{try{i||null==o.return||o.return()}finally{if(n)throw r}}e.emit("addedfiles",t),p()}))};p()}this.URL=null!==window.URL?window.URL:window.webkitURL;var f=!0,m=!1,v=void 0;try{for(var y,g=this.events[Symbol.iterator]();!(f=(y=g.next()).done);f=!0){var b=y.value;this.on(b,this.options[b])}}catch(e){m=!0,v=e}finally{try{f||null==g.return||g.return()}finally{if(m)throw v}}this.on("uploadprogress",(function(){return e.updateTotalUploadProgress()})),this.on("removedfile",(function(){return t.updateTotalUploadProgress()})),this.on("canceled",(function(e){return i.emit("complete",e)})),this.on("complete",(function(e){var t=n;if(0===n.getAddedFiles().length&&0===n.getUploadingFiles().length&&0===n.getQueuedFiles().length)return setTimeout((function(){return t.emit("queuecomplete")}),0)}));var k=function(e){if(function(e){if(e.dataTransfer.types)for(var t=0;t".concat(this.options.dictFallbackText,"
")),i+='');var n=o.createElement(i);return"FORM"!==this.element.tagName?(t=o.createElement(''))).appendChild(n):(this.element.setAttribute("enctype","multipart/form-data"),this.element.setAttribute("method",this.options.method)),null!=t?t:n}},{key:"getExistingFallback",value:function(){var e=function(e){var t=!0,i=!1,n=void 0;try{for(var r,a=e[Symbol.iterator]();!(t=(r=a.next()).done);t=!0){var o=r.value;if(/(^| )fallback($| )/.test(o.className))return o}}catch(e){i=!0,n=e}finally{try{t||null==a.return||a.return()}finally{if(i)throw n}}},t=!0,i=!1,n=void 0;try{for(var r,a=["div","form"][Symbol.iterator]();!(t=(r=a.next()).done);t=!0){var o,l=r.value;if(o=e(this.element.getElementsByTagName(l)))return o}}catch(e){i=!0,n=e}finally{try{t||null==a.return||a.return()}finally{if(i)throw n}}}},{key:"setupEventListeners",value:function(){return this.listeners.map((function(e){return function(){var t=[];for(var i in e.events){var n=e.events[i];t.push(e.element.addEventListener(i,n,!1))}return t}()}))}},{key:"removeEventListeners",value:function(){return this.listeners.map((function(e){return function(){var t=[];for(var i in e.events){var n=e.events[i];t.push(e.element.removeEventListener(i,n,!1))}return t}()}))}},{key:"disable",value:function(){var e=this;return this.clickableElements.forEach((function(e){return e.classList.remove("dz-clickable")})),this.removeEventListeners(),this.disabled=!0,this.files.map((function(t){return e.cancelUpload(t)}))}},{key:"enable",value:function(){return delete this.disabled,this.clickableElements.forEach((function(e){return e.classList.add("dz-clickable")})),this.setupEventListeners()}},{key:"filesize",value:function(e){var t=0,i="b";if(e>0){for(var n=["tb","gb","mb","kb","b"],r=0;r=Math.pow(this.options.filesizeBase,4-r)/10){t=e/Math.pow(this.options.filesizeBase,4-r),i=a;break}}t=Math.round(10*t)/10}return"".concat(t," ").concat(this.options.dictFileSizeUnits[i])}},{key:"_updateMaxFilesReachedClass",value:function(){return null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(this.getAcceptedFiles().length===this.options.maxFiles&&this.emit("maxfilesreached",this.files),this.element.classList.add("dz-max-files-reached")):this.element.classList.remove("dz-max-files-reached")}},{key:"drop",value:function(e){if(e.dataTransfer){this.emit("drop",e);for(var t=[],i=0;i0){var n=!0,r=!1,o=void 0;try{for(var l,s=i[Symbol.iterator]();!(n=(l=s.next()).done);n=!0){var u=l.value,c=e;u.isFile?u.file((function(e){if(!c.options.ignoreHiddenFiles||"."!==e.name.substring(0,1))return e.fullPath="".concat(t,"/").concat(e.name),c.addFile(e)})):u.isDirectory&&e._addFilesFromDirectory(u,"".concat(t,"/").concat(u.name))}}catch(e){r=!0,o=e}finally{try{n||null==s.return||s.return()}finally{if(r)throw o}}a()}return null}),r)};return a()}},{key:"accept",value:function(e,t){this.options.maxFilesize&&e.size>1048576*this.options.maxFilesize?t(this.options.dictFileTooBig.replace("{{filesize}}",Math.round(e.size/1024/10.24)/100).replace("{{maxFilesize}}",this.options.maxFilesize)):o.isValidFile(e,this.options.acceptedFiles)?null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(t(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}",this.options.maxFiles)),this.emit("maxfilesexceeded",e)):this.options.accept.call(this,e,t):t(this.options.dictInvalidFileType)}},{key:"addFile",value:function(e){var t=this;e.upload={uuid:o.uuidv4(),progress:0,total:e.size,bytesSent:0,filename:this._renameFile(e)},this.files.push(e),e.status=o.ADDED,this.emit("addedfile",e),this._enqueueThumbnail(e),this.accept(e,(function(i){i?(e.accepted=!1,t._errorProcessing([e],i)):(e.accepted=!0,t.options.autoQueue&&t.enqueueFile(e)),t._updateMaxFilesReachedClass()}))}},{key:"enqueueFiles",value:function(e){var t=!0,i=!1,n=void 0;try{for(var r,a=e[Symbol.iterator]();!(t=(r=a.next()).done);t=!0){var o=r.value;this.enqueueFile(o)}}catch(e){i=!0,n=e}finally{try{t||null==a.return||a.return()}finally{if(i)throw n}}return null}},{key:"enqueueFile",value:function(e){if(e.status!==o.ADDED||!0!==e.accepted)throw new Error("This file can't be queued because it has already been processed or was rejected.");var t=this;if(e.status=o.QUEUED,this.options.autoProcessQueue)return setTimeout((function(){return t.processQueue()}),0)}},{key:"_enqueueThumbnail",value:function(e){if(this.options.createImageThumbnails&&e.type.match(/image.*/)&&e.size<=1048576*this.options.maxThumbnailFilesize){var t=this;return this._thumbnailQueue.push(e),setTimeout((function(){return t._processThumbnailQueue()}),0)}}},{key:"_processThumbnailQueue",value:function(){var e=this;if(!this._processingThumbnail&&0!==this._thumbnailQueue.length){this._processingThumbnail=!0;var t=this._thumbnailQueue.shift();return this.createThumbnail(t,this.options.thumbnailWidth,this.options.thumbnailHeight,this.options.thumbnailMethod,!0,(function(i){return e.emit("thumbnail",t,i),e._processingThumbnail=!1,e._processThumbnailQueue()}))}}},{key:"removeFile",value:function(e){if(e.status===o.UPLOADING&&this.cancelUpload(e),this.files=m(this.files,e),this.emit("removedfile",e),0===this.files.length)return this.emit("reset")}},{key:"removeAllFiles",value:function(e){null==e&&(e=!1);var t=!0,i=!1,n=void 0;try{for(var r,a=this.files.slice()[Symbol.iterator]();!(t=(r=a.next()).done);t=!0){var l=r.value;(l.status!==o.UPLOADING||e)&&this.removeFile(l)}}catch(e){i=!0,n=e}finally{try{t||null==a.return||a.return()}finally{if(i)throw n}}return null}},{key:"resizeImage",value:function(e,t,i,n,r){var a=this;return this.createThumbnail(e,t,i,n,!0,(function(t,i){if(null==i)return r(e);var n=a.options.resizeMimeType;null==n&&(n=e.type);var l=i.toDataURL(n,a.options.resizeQuality);return"image/jpeg"!==n&&"image/jpg"!==n||(l=g.restore(e.dataURL,l)),r(o.dataURItoBlob(l))}))}},{key:"createThumbnail",value:function(e,t,i,n,r,a){var o=this,l=new FileReader;l.onload=function(){e.dataURL=l.result,"image/svg+xml"!==e.type?o.createThumbnailFromUrl(e,t,i,n,r,a):null!=a&&a(l.result)},l.readAsDataURL(e)}},{key:"displayExistingFile",value:function(e,t,i,n,r){var a=void 0===r||r;if(this.emit("addedfile",e),this.emit("complete",e),a){var o=this;e.dataURL=t,this.createThumbnailFromUrl(e,this.options.thumbnailWidth,this.options.thumbnailHeight,this.options.thumbnailMethod,this.options.fixOrientation,(function(t){o.emit("thumbnail",e,t),i&&i()}),n)}else this.emit("thumbnail",e,t),i&&i()}},{key:"createThumbnailFromUrl",value:function(e,t,i,n,r,a,o){var l=this,s=document.createElement("img");return o&&(s.crossOrigin=o),r="from-image"!=getComputedStyle(document.body).imageOrientation&&r,s.onload=function(){var o=l,u=function(e){return e(1)};return"undefined"!=typeof EXIF&&null!==EXIF&&r&&(u=function(e){return EXIF.getData(s,(function(){return e(EXIF.getTag(this,"Orientation"))}))}),u((function(r){e.width=s.width,e.height=s.height;var l=o.options.resize.call(o,e,t,i,n),u=document.createElement("canvas"),c=u.getContext("2d");switch(u.width=l.trgWidth,u.height=l.trgHeight,r>4&&(u.width=l.trgHeight,u.height=l.trgWidth),r){case 2:c.translate(u.width,0),c.scale(-1,1);break;case 3:c.translate(u.width,u.height),c.rotate(Math.PI);break;case 4:c.translate(0,u.height),c.scale(1,-1);break;case 5:c.rotate(.5*Math.PI),c.scale(1,-1);break;case 6:c.rotate(.5*Math.PI),c.translate(0,-u.width);break;case 7:c.rotate(.5*Math.PI),c.translate(u.height,-u.width),c.scale(-1,1);break;case 8:c.rotate(-.5*Math.PI),c.translate(-u.height,0)}y(c,s,null!=l.srcX?l.srcX:0,null!=l.srcY?l.srcY:0,l.srcWidth,l.srcHeight,null!=l.trgX?l.trgX:0,null!=l.trgY?l.trgY:0,l.trgWidth,l.trgHeight);var d=u.toDataURL("image/png");if(null!=a)return a(d,u)}))},null!=a&&(s.onerror=a),s.src=e.dataURL}},{key:"processQueue",value:function(){var e=this.options.parallelUploads,t=this.getUploadingFiles().length,i=t;if(!(t>=e)){var n=this.getQueuedFiles();if(n.length>0){if(this.options.uploadMultiple)return this.processFiles(n.slice(0,e-t));for(;i1?t-1:0),n=1;nt.options.chunkSize),e[0].upload.totalChunkCount=Math.ceil(n.size/t.options.chunkSize)}if(e[0].upload.chunked){var r=t,a=t,l=e[0];n=i[0];l.upload.chunks=[];var s=function(){for(var t=0;void 0!==l.upload.chunks[t];)t++;if(!(t>=l.upload.totalChunkCount)){0;var i=t*r.options.chunkSize,a=Math.min(i+r.options.chunkSize,n.size),s={name:r._getParamName(0),data:n.webkitSlice?n.webkitSlice(i,a):n.slice(i,a),filename:l.upload.filename,chunkIndex:t};l.upload.chunks[t]={file:l,index:t,dataBlock:s,status:o.UPLOADING,progress:0,retries:0},r._uploadData(e,[s])}};if(l.upload.finishedChunkUpload=function(t,i){var n=a,r=!0;t.status=o.SUCCESS,t.dataBlock=null,t.response=t.xhr.responseText,t.responseHeaders=t.xhr.getAllResponseHeaders(),t.xhr=null;for(var u=0;u=o;l?a++:a--)r[a]=t.charCodeAt(a);return new Blob([n],{type:i})};var m=function(e,t){return e.filter((function(e){return e!==t})).map((function(e){return e}))},v=function(e){return e.replace(/[\-_](\w)/g,(function(e){return e.charAt(1).toUpperCase()}))};f.createElement=function(e){var t=document.createElement("div");return t.innerHTML=e,t.childNodes[0]},f.elementInside=function(e,t){if(e===t)return!0;for(;e=e.parentNode;)if(e===t)return!0;return!1},f.getElement=function(e,t){var i;if("string"==typeof e?i=document.querySelector(e):null!=e.nodeType&&(i=e),null==i)throw new Error("Invalid `".concat(t,"` option provided. Please provide a CSS selector or a plain HTML element."));return i},f.getElements=function(e,t){var i,n;if(e instanceof Array){n=[];try{var r=!0,a=!1,o=void 0;try{for(var l=e[Symbol.iterator]();!(r=(s=l.next()).done);r=!0)i=s.value,n.push(this.getElement(i,t))}catch(e){a=!0,o=e}finally{try{r||null==l.return||l.return()}finally{if(a)throw o}}}catch(e){n=null}}else if("string"==typeof e){n=[];r=!0,a=!1,o=void 0;try{var s;for(l=document.querySelectorAll(e)[Symbol.iterator]();!(r=(s=l.next()).done);r=!0)i=s.value,n.push(i)}catch(e){a=!0,o=e}finally{try{r||null==l.return||l.return()}finally{if(a)throw o}}}else null!=e.nodeType&&(n=[e]);if(null==n||!n.length)throw new Error("Invalid `".concat(t,"` option provided. Please provide a CSS selector, a plain HTML element or a list of those."));return n},f.confirm=function(e,t,i){return window.confirm(e)?t():null!=i?i():void 0},f.isValidFile=function(e,t){if(!t)return!0;t=t.split(",");var i=e.type,n=i.replace(/\/.*$/,""),r=!0,a=!1,o=void 0;try{for(var l,s=t[Symbol.iterator]();!(r=(l=s.next()).done);r=!0){var u=l.value;if("."===(u=u.trim()).charAt(0)){if(-1!==e.name.toLowerCase().indexOf(u.toLowerCase(),e.name.length-u.length))return!0}else if(/\/\*$/.test(u)){if(n===u.replace(/\/.*$/,""))return!0}else if(i===u)return!0}}catch(e){a=!0,o=e}finally{try{r||null==s.return||s.return()}finally{if(a)throw o}}return!1},"undefined"!=typeof jQuery&&null!==jQuery&&(jQuery.fn.dropzone=function(e){return this.each((function(){return new f(this,e)}))}),f.ADDED="added",f.QUEUED="queued",f.ACCEPTED=f.QUEUED,f.UPLOADING="uploading",f.PROCESSING=f.UPLOADING,f.CANCELED="canceled",f.ERROR="error",f.SUCCESS="success";var y=function(e,t,i,n,r,a,o,l,s,u){var c=function(e){e.naturalWidth;var t=e.naturalHeight,i=document.createElement("canvas");i.width=1,i.height=t;var n=i.getContext("2d");n.drawImage(e,0,0);for(var r=n.getImageData(1,0,1,t).data,a=0,o=t,l=t;l>a;)0===r[4*(l-1)+3]?o=l:a=l,l=o+a>>1;var s=l/t;return 0===s?1:s}(t);return e.drawImage(t,i,n,r,a,o,l,s,u/c)},g=function(){"use strict";function e(){i(this,e)}return r(e,null,[{key:"initClass",value:function(){this.KEY_STR="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}},{key:"encode64",value:function(e){for(var t="",i=void 0,n=void 0,r="",a=void 0,o=void 0,l=void 0,s="",u=0;a=(i=e[u++])>>2,o=(3&i)<<4|(n=e[u++])>>4,l=(15&n)<<2|(r=e[u++])>>6,s=63&r,isNaN(n)?l=s=64:isNaN(r)&&(s=64),t=t+this.KEY_STR.charAt(a)+this.KEY_STR.charAt(o)+this.KEY_STR.charAt(l)+this.KEY_STR.charAt(s),i=n=r="",a=o=l=s="",ue.length)break}return i}},{key:"decode64",value:function(e){var t=void 0,i=void 0,n="",r=void 0,a=void 0,o="",l=0,s=[];for(/[^A-Za-z0-9\+\/\=]/g.exec(e)&&console.warn("There were invalid base64 characters in the input text.\nValid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\nExpect errors in decoding."),e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");t=this.KEY_STR.indexOf(e.charAt(l++))<<2|(r=this.KEY_STR.indexOf(e.charAt(l++)))>>4,i=(15&r)<<4|(a=this.KEY_STR.indexOf(e.charAt(l++)))>>2,n=(3&a)<<6|(o=this.KEY_STR.indexOf(e.charAt(l++))),s.push(t),64!==a&&s.push(i),64!==o&&s.push(n),t=i=n="",r=a=o="",l 1 ? t - 1 : 0), n = 1;
+ n < t;
+ n++
+ )
+ i[n - 1] = arguments[n];
+ this._callbacks = this._callbacks || {};
+ var r = this._callbacks[e],
+ a = !0,
+ o = !1,
+ l = void 0;
+ if (r)
+ try {
+ for (
+ var s, u = r[Symbol.iterator]();
+ !(a = (s = u.next()).done);
+ a = !0
+ ) {
+ var c = s.value;
+ c.apply(this, i);
+ }
+ } catch (e) {
+ (o = !0), (l = e);
+ } finally {
+ try {
+ a || null == u.return || u.return();
+ } finally {
+ if (o) throw l;
+ }
+ }
+ return (
+ this.element &&
+ this.element.dispatchEvent(
+ this.makeEvent("dropzone:" + e, { args: i })
+ ),
+ this
+ );
+ },
+ },
+ {
+ key: "makeEvent",
+ value: function (e, t) {
+ var i = { bubbles: !0, cancelable: !0, detail: t };
+ if ("function" == typeof window.CustomEvent)
+ return new CustomEvent(e, i);
+ var n = document.createEvent("CustomEvent");
+ return n.initCustomEvent(e, i.bubbles, i.cancelable, i.detail), n;
+ },
+ },
+ {
+ key: "off",
+ value: function (e, t) {
+ if (!this._callbacks || 0 === arguments.length)
+ return (this._callbacks = {}), this;
+ var i = this._callbacks[e];
+ if (!i) return this;
+ if (1 === arguments.length) return delete this._callbacks[e], this;
+ for (var n = 0; n < i.length; n++) {
+ var r = i[n];
+ if (r === t) {
+ i.splice(n, 1);
+ break;
+ }
+ }
+ return this;
+ },
+ },
+ ]),
+ e
+ );
+ })();
+ var p = {
+ url: null,
+ method: "post",
+ withCredentials: !1,
+ timeout: null,
+ parallelUploads: 2,
+ uploadMultiple: !1,
+ chunking: !1,
+ forceChunking: !1,
+ chunkSize: 2097152,
+ parallelChunkUploads: !1,
+ retryChunks: !1,
+ retryChunksLimit: 3,
+ maxFilesize: 256,
+ paramName: "file",
+ createImageThumbnails: !0,
+ maxThumbnailFilesize: 10,
+ thumbnailWidth: 120,
+ thumbnailHeight: 120,
+ thumbnailMethod: "crop",
+ resizeWidth: null,
+ resizeHeight: null,
+ resizeMimeType: null,
+ resizeQuality: 0.8,
+ resizeMethod: "contain",
+ filesizeBase: 1e3,
+ maxFiles: null,
+ headers: null,
+ defaultHeaders: !0,
+ clickable: !0,
+ ignoreHiddenFiles: !0,
+ acceptedFiles: null,
+ acceptedMimeTypes: null,
+ autoProcessQueue: !0,
+ autoQueue: !0,
+ addRemoveLinks: !1,
+ previewsContainer: null,
+ disablePreviews: !1,
+ hiddenInputContainer: "body",
+ capture: null,
+ renameFilename: null,
+ renameFile: null,
+ forceFallback: !1,
+ dictDefaultMessage: "Click or drop files",
+ dictFallbackMessage:
+ "Your browser does not support drag'n'drop file uploads.",
+ dictFallbackText:
+ "Please use the fallback form below to upload your files like in the olden days.",
+ dictFileTooBig:
+ "File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.",
+ dictInvalidFileType: "You can't upload files of this type.",
+ dictResponseError: "Server responded with {{statusCode}} code.",
+ dictCancelUpload: "Cancel upload",
+ dictUploadCanceled: "Upload canceled.",
+ dictCancelUploadConfirmation:
+ "Are you sure you want to cancel this upload?",
+ dictRemoveFile: "Remove file",
+ dictRemoveFileConfirmation: null,
+ dictMaxFilesExceeded: "You can not upload any more files.",
+ dictFileSizeUnits: { tb: "TB", gb: "GB", mb: "MB", kb: "KB", b: "b" },
+ init: function () {},
+ params: function (e, t, i) {
+ if (i)
+ return {
+ dzuuid: i.file.upload.uuid,
+ dzchunkindex: i.index,
+ dztotalfilesize: i.file.size,
+ dzchunksize: this.options.chunkSize,
+ dztotalchunkcount: i.file.upload.totalChunkCount,
+ dzchunkbyteoffset: i.index * this.options.chunkSize,
+ };
+ },
+ accept: function (e, t) {
+ return t();
+ },
+ chunksUploaded: function (e, t) {
+ t();
+ },
+ binaryBody: !1,
+ fallback: function () {
+ var e;
+ this.element.className = "".concat(
+ this.element.className,
+ " dz-browser-not-supported"
+ );
+ var t = !0,
+ i = !1,
+ n = void 0;
+ try {
+ for (
+ var r,
+ a = this.element.getElementsByTagName("div")[Symbol.iterator]();
+ !(t = (r = a.next()).done);
+ t = !0
+ ) {
+ var o = r.value;
+ if (/(^| )dz-message($| )/.test(o.className)) {
+ (e = o), (o.className = "dz-message");
+ break;
+ }
+ }
+ } catch (e) {
+ (i = !0), (n = e);
+ } finally {
+ try {
+ t || null == a.return || a.return();
+ } finally {
+ if (i) throw n;
+ }
+ }
+ e ||
+ ((e = f.createElement('
')),
+ this.element.appendChild(e));
+ var l = e.getElementsByTagName("span")[0];
+ return (
+ l &&
+ (null != l.textContent
+ ? (l.textContent = this.options.dictFallbackMessage)
+ : null != l.innerText &&
+ (l.innerText = this.options.dictFallbackMessage)),
+ this.element.appendChild(this.getFallbackForm())
+ );
+ },
+ resize: function (e, t, i, n) {
+ var r = { srcX: 0, srcY: 0, srcWidth: e.width, srcHeight: e.height },
+ a = e.width / e.height;
+ null == t && null == i
+ ? ((t = r.srcWidth), (i = r.srcHeight))
+ : null == t
+ ? (t = i * a)
+ : null == i && (i = t / a);
+ var o = (t = Math.min(t, r.srcWidth)) / (i = Math.min(i, r.srcHeight));
+ if (r.srcWidth > t || r.srcHeight > i)
+ if ("crop" === n)
+ a > o
+ ? ((r.srcHeight = e.height), (r.srcWidth = r.srcHeight * o))
+ : ((r.srcWidth = e.width), (r.srcHeight = r.srcWidth / o));
+ else {
+ if ("contain" !== n)
+ throw new Error("Unknown resizeMethod '".concat(n, "'"));
+ a > o ? (i = t / a) : (t = i * a);
+ }
+ return (
+ (r.srcX = (e.width - r.srcWidth) / 2),
+ (r.srcY = (e.height - r.srcHeight) / 2),
+ (r.trgWidth = t),
+ (r.trgHeight = i),
+ r
+ );
+ },
+ transformFile: function (e, t) {
+ return (this.options.resizeWidth || this.options.resizeHeight) &&
+ e.type.match(/image.*/)
+ ? this.resizeImage(
+ e,
+ this.options.resizeWidth,
+ this.options.resizeHeight,
+ this.options.resizeMethod,
+ t
+ )
+ : t(e);
+ },
+ previewTemplate: e(
+ ''
+ ),
+ drop: function (e) {
+ return this.element.classList.remove("dz-drag-hover");
+ },
+ dragstart: function (e) {},
+ dragend: function (e) {
+ return this.element.classList.remove("dz-drag-hover");
+ },
+ dragenter: function (e) {
+ return this.element.classList.add("dz-drag-hover");
+ },
+ dragover: function (e) {
+ return this.element.classList.add("dz-drag-hover");
+ },
+ dragleave: function (e) {
+ return this.element.classList.remove("dz-drag-hover");
+ },
+ paste: function (e) {},
+ reset: function () {
+ return this.element.classList.remove("dz-started");
+ },
+ addedfile: function (e) {
+ if (
+ (this.element === this.previewsContainer &&
+ this.element.classList.add("dz-started"),
+ this.previewsContainer && !this.options.disablePreviews)
+ ) {
+ var t = this;
+ (e.previewElement = f.createElement(
+ this.options.previewTemplate.trim()
+ )),
+ (e.previewTemplate = e.previewElement),
+ this.previewsContainer.appendChild(e.previewElement);
+ var i = !0,
+ n = !1,
+ r = void 0;
+ try {
+ for (
+ var a,
+ o = e.previewElement
+ .querySelectorAll("[data-dz-name]")
+ [Symbol.iterator]();
+ !(i = (a = o.next()).done);
+ i = !0
+ ) {
+ var l = a.value;
+ l.textContent = e.name;
+ }
+ } catch (e) {
+ (n = !0), (r = e);
+ } finally {
+ try {
+ i || null == o.return || o.return();
+ } finally {
+ if (n) throw r;
+ }
+ }
+ var s = !0,
+ u = !1,
+ c = void 0;
+ try {
+ for (
+ var d,
+ h = e.previewElement
+ .querySelectorAll("[data-dz-size]")
+ [Symbol.iterator]();
+ !(s = (d = h.next()).done);
+ s = !0
+ )
+ (l = d.value).innerHTML = this.filesize(e.size);
+ } catch (e) {
+ (u = !0), (c = e);
+ } finally {
+ try {
+ s || null == h.return || h.return();
+ } finally {
+ if (u) throw c;
+ }
+ }
+ this.options.addRemoveLinks &&
+ ((e._removeLink = f.createElement(
+ ''.concat(
+ this.options.dictRemoveFile,
+ ""
+ )
+ )),
+ e.previewElement.appendChild(e._removeLink));
+ var p = function (i) {
+ var n = t;
+ if (
+ (i.preventDefault(),
+ i.stopPropagation(),
+ e.status === f.UPLOADING)
+ )
+ return f.confirm(
+ t.options.dictCancelUploadConfirmation,
+ function () {
+ return n.removeFile(e);
+ }
+ );
+ var r = t;
+ return t.options.dictRemoveFileConfirmation
+ ? f.confirm(t.options.dictRemoveFileConfirmation, function () {
+ return r.removeFile(e);
+ })
+ : t.removeFile(e);
+ },
+ m = !0,
+ v = !1,
+ y = void 0;
+ try {
+ for (
+ var g,
+ b = e.previewElement
+ .querySelectorAll("[data-dz-remove]")
+ [Symbol.iterator]();
+ !(m = (g = b.next()).done);
+ m = !0
+ ) {
+ g.value.addEventListener("click", p);
+ }
+ } catch (e) {
+ (v = !0), (y = e);
+ } finally {
+ try {
+ m || null == b.return || b.return();
+ } finally {
+ if (v) throw y;
+ }
+ }
+ }
+ },
+ removedfile: function (e) {
+ return (
+ null != e.previewElement &&
+ null != e.previewElement.parentNode &&
+ e.previewElement.parentNode.removeChild(e.previewElement),
+ this._updateMaxFilesReachedClass()
+ );
+ },
+ thumbnail: function (e, t) {
+ if (e.previewElement) {
+ e.previewElement.classList.remove("dz-file-preview");
+ var i = !0,
+ n = !1,
+ r = void 0;
+ try {
+ for (
+ var a,
+ o = e.previewElement
+ .querySelectorAll("[data-dz-thumbnail]")
+ [Symbol.iterator]();
+ !(i = (a = o.next()).done);
+ i = !0
+ ) {
+ var l = a.value;
+ (l.alt = e.name), (l.src = t);
+ }
+ } catch (e) {
+ (n = !0), (r = e);
+ } finally {
+ try {
+ i || null == o.return || o.return();
+ } finally {
+ if (n) throw r;
+ }
+ }
+ return setTimeout(function () {
+ return e.previewElement.classList.add("dz-image-preview");
+ }, 1);
+ }
+ },
+ error: function (e, t) {
+ if (e.previewElement) {
+ e.previewElement.classList.add("dz-error"),
+ "string" != typeof t && t.error && (t = t.error);
+ var i = !0,
+ n = !1,
+ r = void 0;
+ try {
+ for (
+ var a,
+ o = e.previewElement
+ .querySelectorAll("[data-dz-errormessage]")
+ [Symbol.iterator]();
+ !(i = (a = o.next()).done);
+ i = !0
+ ) {
+ a.value.textContent = t;
+ }
+ } catch (e) {
+ (n = !0), (r = e);
+ } finally {
+ try {
+ i || null == o.return || o.return();
+ } finally {
+ if (n) throw r;
+ }
+ }
+ }
+ },
+ errormultiple: function () {},
+ processing: function (e) {
+ if (
+ e.previewElement &&
+ (e.previewElement.classList.add("dz-processing"), e._removeLink)
+ )
+ return (e._removeLink.innerHTML = this.options.dictCancelUpload);
+ },
+ processingmultiple: function () {},
+ uploadprogress: function (e, t, i) {
+ var n = !0,
+ r = !1,
+ a = void 0;
+ if (e.previewElement)
+ try {
+ for (
+ var o,
+ l = e.previewElement
+ .querySelectorAll("[data-dz-uploadprogress]")
+ [Symbol.iterator]();
+ !(n = (o = l.next()).done);
+ n = !0
+ ) {
+ var s = o.value;
+ "PROGRESS" === s.nodeName
+ ? (s.value = t)
+ : (s.style.width = "".concat(t, "%"));
+ }
+ } catch (e) {
+ (r = !0), (a = e);
+ } finally {
+ try {
+ n || null == l.return || l.return();
+ } finally {
+ if (r) throw a;
+ }
+ }
+ },
+ totaluploadprogress: function () {},
+ sending: function () {},
+ sendingmultiple: function () {},
+ success: function (e) {
+ if (e.previewElement)
+ return e.previewElement.classList.add("dz-success");
+ },
+ successmultiple: function () {},
+ canceled: function (e) {
+ return this.emit("error", e, this.options.dictUploadCanceled);
+ },
+ canceledmultiple: function () {},
+ complete: function (e) {
+ if (
+ (e._removeLink &&
+ (e._removeLink.innerHTML = this.options.dictRemoveFile),
+ e.previewElement)
+ )
+ return e.previewElement.classList.add("dz-complete");
+ },
+ completemultiple: function () {},
+ maxfilesexceeded: function () {},
+ maxfilesreached: function () {},
+ queuecomplete: function () {},
+ addedfiles: function () {},
+ },
+ f = (function (n) {
+ "use strict";
+ function o(n, r) {
+ var l, c, d, h;
+ if (
+ (i(this, o),
+ ((l = s(this, ((c = o), a(c)).call(this))).element = n),
+ (l.clickableElements = []),
+ (l.listeners = []),
+ (l.files = []),
+ "string" == typeof l.element &&
+ (l.element = document.querySelector(l.element)),
+ !l.element || null == l.element.nodeType)
+ )
+ throw new Error("Invalid dropzone element.");
+ if (l.element.dropzone) throw new Error("Dropzone already attached.");
+ o.instances.push(t(l)), (l.element.dropzone = t(l));
+ var f = null != (h = o.optionsForElement(l.element)) ? h : {};
+ if (
+ ((l.options = e(u)(!0, {}, p, f, null != r ? r : {})),
+ (l.options.previewTemplate = l.options.previewTemplate.replace(
+ /\n*/g,
+ ""
+ )),
+ l.options.forceFallback || !o.isBrowserSupported())
+ )
+ return s(l, l.options.fallback.call(t(l)));
+ if (
+ (null == l.options.url &&
+ (l.options.url = l.element.getAttribute("action")),
+ !l.options.url)
+ )
+ throw new Error("No URL provided.");
+ if (l.options.acceptedFiles && l.options.acceptedMimeTypes)
+ throw new Error(
+ "You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated."
+ );
+ if (l.options.uploadMultiple && l.options.chunking)
+ throw new Error("You cannot set both: uploadMultiple and chunking.");
+ if (l.options.binaryBody && l.options.uploadMultiple)
+ throw new Error(
+ "You cannot set both: binaryBody and uploadMultiple."
+ );
+ return (
+ l.options.acceptedMimeTypes &&
+ ((l.options.acceptedFiles = l.options.acceptedMimeTypes),
+ delete l.options.acceptedMimeTypes),
+ null != l.options.renameFilename &&
+ (l.options.renameFile = function (e) {
+ return l.options.renameFilename.call(t(l), e.name, e);
+ }),
+ "string" == typeof l.options.method &&
+ (l.options.method = l.options.method.toUpperCase()),
+ (d = l.getExistingFallback()) &&
+ d.parentNode &&
+ d.parentNode.removeChild(d),
+ !1 !== l.options.previewsContainer &&
+ (l.options.previewsContainer
+ ? (l.previewsContainer = o.getElement(
+ l.options.previewsContainer,
+ "previewsContainer"
+ ))
+ : (l.previewsContainer = l.element)),
+ l.options.clickable &&
+ (!0 === l.options.clickable
+ ? (l.clickableElements = [l.element])
+ : (l.clickableElements = o.getElements(
+ l.options.clickable,
+ "clickable"
+ ))),
+ l.init(),
+ l
+ );
+ }
+ return (
+ l(o, n),
+ r(
+ o,
+ [
+ {
+ key: "getAcceptedFiles",
+ value: function () {
+ return this.files
+ .filter(function (e) {
+ return e.accepted;
+ })
+ .map(function (e) {
+ return e;
+ });
+ },
+ },
+ {
+ key: "getRejectedFiles",
+ value: function () {
+ return this.files
+ .filter(function (e) {
+ return !e.accepted;
+ })
+ .map(function (e) {
+ return e;
+ });
+ },
+ },
+ {
+ key: "getFilesWithStatus",
+ value: function (e) {
+ return this.files
+ .filter(function (t) {
+ return t.status === e;
+ })
+ .map(function (e) {
+ return e;
+ });
+ },
+ },
+ {
+ key: "getQueuedFiles",
+ value: function () {
+ return this.getFilesWithStatus(o.QUEUED);
+ },
+ },
+ {
+ key: "getUploadingFiles",
+ value: function () {
+ return this.getFilesWithStatus(o.UPLOADING);
+ },
+ },
+ {
+ key: "getAddedFiles",
+ value: function () {
+ return this.getFilesWithStatus(o.ADDED);
+ },
+ },
+ {
+ key: "getActiveFiles",
+ value: function () {
+ return this.files
+ .filter(function (e) {
+ return e.status === o.UPLOADING || e.status === o.QUEUED;
+ })
+ .map(function (e) {
+ return e;
+ });
+ },
+ },
+ {
+ key: "init",
+ value: function () {
+ var e = this,
+ t = this,
+ i = this,
+ n = this,
+ r = this,
+ a = this,
+ l = this,
+ s = this,
+ u = this,
+ c = this,
+ d = this;
+ if (
+ ("form" === this.element.tagName &&
+ this.element.setAttribute("enctype", "multipart/form-data"),
+ this.element.classList.contains("dropzone") &&
+ !this.element.querySelector(".dz-message") &&
+ this.element.appendChild(
+ o.createElement(
+ '"
+ )
+ )
+ ),
+ this.clickableElements.length)
+ ) {
+ var h = this,
+ p = function () {
+ var e = h;
+ h.hiddenFileInput &&
+ h.hiddenFileInput.parentNode.removeChild(
+ h.hiddenFileInput
+ ),
+ (h.hiddenFileInput = document.createElement("input")),
+ h.hiddenFileInput.setAttribute("type", "file"),
+ (null === h.options.maxFiles ||
+ h.options.maxFiles > 1) &&
+ h.hiddenFileInput.setAttribute(
+ "multiple",
+ "multiple"
+ ),
+ (h.hiddenFileInput.className = "dz-hidden-input"),
+ null !== h.options.acceptedFiles &&
+ h.hiddenFileInput.setAttribute(
+ "accept",
+ h.options.acceptedFiles
+ ),
+ null !== h.options.capture &&
+ h.hiddenFileInput.setAttribute(
+ "capture",
+ h.options.capture
+ ),
+ h.hiddenFileInput.setAttribute("tabindex", "-1"),
+ (h.hiddenFileInput.style.visibility = "hidden"),
+ (h.hiddenFileInput.style.position = "absolute"),
+ (h.hiddenFileInput.style.top = "0"),
+ (h.hiddenFileInput.style.left = "0"),
+ (h.hiddenFileInput.style.height = "0"),
+ (h.hiddenFileInput.style.width = "0"),
+ o
+ .getElement(
+ h.options.hiddenInputContainer,
+ "hiddenInputContainer"
+ )
+ .appendChild(h.hiddenFileInput),
+ h.hiddenFileInput.addEventListener(
+ "change",
+ function () {
+ var t = e.hiddenFileInput.files,
+ i = !0,
+ n = !1,
+ r = void 0;
+ if (t.length)
+ try {
+ for (
+ var a, o = t[Symbol.iterator]();
+ !(i = (a = o.next()).done);
+ i = !0
+ ) {
+ var l = a.value;
+ e.addFile(l);
+ }
+ } catch (e) {
+ (n = !0), (r = e);
+ } finally {
+ try {
+ i || null == o.return || o.return();
+ } finally {
+ if (n) throw r;
+ }
+ }
+ e.emit("addedfiles", t), p();
+ }
+ );
+ };
+ p();
+ }
+ this.URL = null !== window.URL ? window.URL : window.webkitURL;
+ var f = !0,
+ m = !1,
+ v = void 0;
+ try {
+ for (
+ var y, g = this.events[Symbol.iterator]();
+ !(f = (y = g.next()).done);
+ f = !0
+ ) {
+ var b = y.value;
+ this.on(b, this.options[b]);
+ }
+ } catch (e) {
+ (m = !0), (v = e);
+ } finally {
+ try {
+ f || null == g.return || g.return();
+ } finally {
+ if (m) throw v;
+ }
+ }
+ this.on("uploadprogress", function () {
+ return e.updateTotalUploadProgress();
+ }),
+ this.on("removedfile", function () {
+ return t.updateTotalUploadProgress();
+ }),
+ this.on("canceled", function (e) {
+ return i.emit("complete", e);
+ }),
+ this.on("complete", function (e) {
+ var t = n;
+ if (
+ 0 === n.getAddedFiles().length &&
+ 0 === n.getUploadingFiles().length &&
+ 0 === n.getQueuedFiles().length
+ )
+ return setTimeout(function () {
+ return t.emit("queuecomplete");
+ }, 0);
+ });
+ var k = function (e) {
+ if (
+ (function (e) {
+ if (e.dataTransfer.types)
+ for (var t = 0; t < e.dataTransfer.types.length; t++)
+ if ("Files" === e.dataTransfer.types[t]) return !0;
+ return !1;
+ })(e)
+ )
+ return (
+ e.stopPropagation(),
+ e.preventDefault
+ ? e.preventDefault()
+ : (e.returnValue = !1)
+ );
+ };
+ return (
+ (this.listeners = [
+ {
+ element: this.element,
+ events: {
+ dragstart: function (e) {
+ return r.emit("dragstart", e);
+ },
+ dragenter: function (e) {
+ return k(e), a.emit("dragenter", e);
+ },
+ dragover: function (e) {
+ var t;
+ try {
+ t = e.dataTransfer.effectAllowed;
+ } catch (e) {}
+ return (
+ (e.dataTransfer.dropEffect =
+ "move" === t || "linkMove" === t
+ ? "move"
+ : "copy"),
+ k(e),
+ l.emit("dragover", e)
+ );
+ },
+ dragleave: function (e) {
+ return s.emit("dragleave", e);
+ },
+ drop: function (e) {
+ return k(e), u.drop(e);
+ },
+ dragend: function (e) {
+ return c.emit("dragend", e);
+ },
+ },
+ },
+ ]),
+ this.clickableElements.forEach(function (e) {
+ var t = d;
+ return d.listeners.push({
+ element: e,
+ events: {
+ click: function (i) {
+ return (
+ (e !== t.element ||
+ i.target === t.element ||
+ o.elementInside(
+ i.target,
+ t.element.querySelector(".dz-message")
+ )) &&
+ t.hiddenFileInput.click(),
+ !0
+ );
+ },
+ },
+ });
+ }),
+ this.enable(),
+ this.options.init.call(this)
+ );
+ },
+ },
+ {
+ key: "destroy",
+ value: function () {
+ return (
+ this.disable(),
+ this.removeAllFiles(!0),
+ (null != this.hiddenFileInput
+ ? this.hiddenFileInput.parentNode
+ : void 0) &&
+ (this.hiddenFileInput.parentNode.removeChild(
+ this.hiddenFileInput
+ ),
+ (this.hiddenFileInput = null)),
+ delete this.element.dropzone,
+ o.instances.splice(o.instances.indexOf(this), 1)
+ );
+ },
+ },
+ {
+ key: "updateTotalUploadProgress",
+ value: function () {
+ var e,
+ t = 0,
+ i = 0;
+ if (this.getActiveFiles().length) {
+ var n = !0,
+ r = !1,
+ a = void 0;
+ try {
+ for (
+ var o, l = this.getActiveFiles()[Symbol.iterator]();
+ !(n = (o = l.next()).done);
+ n = !0
+ ) {
+ var s = o.value;
+ (t += s.upload.bytesSent), (i += s.upload.total);
+ }
+ } catch (e) {
+ (r = !0), (a = e);
+ } finally {
+ try {
+ n || null == l.return || l.return();
+ } finally {
+ if (r) throw a;
+ }
+ }
+ e = (100 * t) / i;
+ } else e = 100;
+ return this.emit("totaluploadprogress", e, i, t);
+ },
+ },
+ {
+ key: "_getParamName",
+ value: function (e) {
+ return "function" == typeof this.options.paramName
+ ? this.options.paramName(e)
+ : ""
+ .concat(this.options.paramName)
+ .concat(
+ this.options.uploadMultiple ? "[".concat(e, "]") : ""
+ );
+ },
+ },
+ {
+ key: "_renameFile",
+ value: function (e) {
+ return "function" != typeof this.options.renameFile
+ ? e.name
+ : this.options.renameFile(e);
+ },
+ },
+ {
+ key: "getFallbackForm",
+ value: function () {
+ var e, t;
+ if ((e = this.getExistingFallback())) return e;
+ var i = ''
+ ));
+ var n = o.createElement(i);
+ return (
+ "FORM" !== this.element.tagName
+ ? (t = o.createElement(
+ '')
+ )).appendChild(n)
+ : (this.element.setAttribute(
+ "enctype",
+ "multipart/form-data"
+ ),
+ this.element.setAttribute("method", this.options.method)),
+ null != t ? t : n
+ );
+ },
+ },
+ {
+ key: "getExistingFallback",
+ value: function () {
+ var e = function (e) {
+ var t = !0,
+ i = !1,
+ n = void 0;
+ try {
+ for (
+ var r, a = e[Symbol.iterator]();
+ !(t = (r = a.next()).done);
+ t = !0
+ ) {
+ var o = r.value;
+ if (/(^| )fallback($| )/.test(o.className)) return o;
+ }
+ } catch (e) {
+ (i = !0), (n = e);
+ } finally {
+ try {
+ t || null == a.return || a.return();
+ } finally {
+ if (i) throw n;
+ }
+ }
+ },
+ t = !0,
+ i = !1,
+ n = void 0;
+ try {
+ for (
+ var r, a = ["div", "form"][Symbol.iterator]();
+ !(t = (r = a.next()).done);
+ t = !0
+ ) {
+ var o,
+ l = r.value;
+ if ((o = e(this.element.getElementsByTagName(l)))) return o;
+ }
+ } catch (e) {
+ (i = !0), (n = e);
+ } finally {
+ try {
+ t || null == a.return || a.return();
+ } finally {
+ if (i) throw n;
+ }
+ }
+ },
+ },
+ {
+ key: "setupEventListeners",
+ value: function () {
+ return this.listeners.map(function (e) {
+ return (function () {
+ var t = [];
+ for (var i in e.events) {
+ var n = e.events[i];
+ t.push(e.element.addEventListener(i, n, !1));
+ }
+ return t;
+ })();
+ });
+ },
+ },
+ {
+ key: "removeEventListeners",
+ value: function () {
+ return this.listeners.map(function (e) {
+ return (function () {
+ var t = [];
+ for (var i in e.events) {
+ var n = e.events[i];
+ t.push(e.element.removeEventListener(i, n, !1));
+ }
+ return t;
+ })();
+ });
+ },
+ },
+ {
+ key: "disable",
+ value: function () {
+ var e = this;
+ return (
+ this.clickableElements.forEach(function (e) {
+ return e.classList.remove("dz-clickable");
+ }),
+ this.removeEventListeners(),
+ (this.disabled = !0),
+ this.files.map(function (t) {
+ return e.cancelUpload(t);
+ })
+ );
+ },
+ },
+ {
+ key: "enable",
+ value: function () {
+ return (
+ delete this.disabled,
+ this.clickableElements.forEach(function (e) {
+ return e.classList.add("dz-clickable");
+ }),
+ this.setupEventListeners()
+ );
+ },
+ },
+ {
+ key: "filesize",
+ value: function (e) {
+ var t = 0,
+ i = "b";
+ if (e > 0) {
+ for (
+ var n = ["tb", "gb", "mb", "kb", "b"], r = 0;
+ r < n.length;
+ r++
+ ) {
+ var a = n[r];
+ if (e >= Math.pow(this.options.filesizeBase, 4 - r) / 10) {
+ (t = e / Math.pow(this.options.filesizeBase, 4 - r)),
+ (i = a);
+ break;
+ }
+ }
+ t = Math.round(10 * t) / 10;
+ }
+ return ""
+ .concat(t, " ")
+ .concat(this.options.dictFileSizeUnits[i]);
+ },
+ },
+ {
+ key: "_updateMaxFilesReachedClass",
+ value: function () {
+ return null != this.options.maxFiles &&
+ this.getAcceptedFiles().length >= this.options.maxFiles
+ ? (this.getAcceptedFiles().length === this.options.maxFiles &&
+ this.emit("maxfilesreached", this.files),
+ this.element.classList.add("dz-max-files-reached"))
+ : this.element.classList.remove("dz-max-files-reached");
+ },
+ },
+ {
+ key: "drop",
+ value: function (e) {
+ if (e.dataTransfer) {
+ this.emit("drop", e);
+ for (var t = [], i = 0; i < e.dataTransfer.files.length; i++)
+ t[i] = e.dataTransfer.files[i];
+ if (t.length) {
+ var n = e.dataTransfer.items;
+ n && n.length && null != n[0].webkitGetAsEntry
+ ? this._addFilesFromItems(n)
+ : this.handleFiles(t);
+ }
+ this.emit("addedfiles", t);
+ }
+ },
+ },
+ {
+ key: "paste",
+ value: function (e) {
+ if (
+ null !=
+ ((t = null != e ? e.clipboardData : void 0),
+ (i = function (e) {
+ return e.items;
+ }),
+ null != t ? i(t) : void 0)
+ ) {
+ var t, i;
+ this.emit("paste", e);
+ var n = e.clipboardData.items;
+ return n.length ? this._addFilesFromItems(n) : void 0;
+ }
+ },
+ },
+ {
+ key: "handleFiles",
+ value: function (e) {
+ var t = !0,
+ i = !1,
+ n = void 0;
+ try {
+ for (
+ var r, a = e[Symbol.iterator]();
+ !(t = (r = a.next()).done);
+ t = !0
+ ) {
+ var o = r.value;
+ this.addFile(o);
+ }
+ } catch (e) {
+ (i = !0), (n = e);
+ } finally {
+ try {
+ t || null == a.return || a.return();
+ } finally {
+ if (i) throw n;
+ }
+ }
+ },
+ },
+ {
+ key: "_addFilesFromItems",
+ value: function (e) {
+ var t = this;
+ return (function () {
+ var i = [],
+ n = !0,
+ r = !1,
+ a = void 0;
+ try {
+ for (
+ var o, l = e[Symbol.iterator]();
+ !(n = (o = l.next()).done);
+ n = !0
+ ) {
+ var s,
+ u = o.value;
+ null != u.webkitGetAsEntry && (s = u.webkitGetAsEntry())
+ ? s.isFile
+ ? i.push(t.addFile(u.getAsFile()))
+ : s.isDirectory
+ ? i.push(t._addFilesFromDirectory(s, s.name))
+ : i.push(void 0)
+ : null != u.getAsFile &&
+ (null == u.kind || "file" === u.kind)
+ ? i.push(t.addFile(u.getAsFile()))
+ : i.push(void 0);
+ }
+ } catch (e) {
+ (r = !0), (a = e);
+ } finally {
+ try {
+ n || null == l.return || l.return();
+ } finally {
+ if (r) throw a;
+ }
+ }
+ return i;
+ })();
+ },
+ },
+ {
+ key: "_addFilesFromDirectory",
+ value: function (e, t) {
+ var i = this,
+ n = e.createReader(),
+ r = function (e) {
+ return (
+ (t = console),
+ (i = "log"),
+ (n = function (t) {
+ return t.log(e);
+ }),
+ null != t && "function" == typeof t[i] ? n(t, i) : void 0
+ );
+ var t, i, n;
+ },
+ a = function () {
+ var e = i;
+ return n.readEntries(function (i) {
+ if (i.length > 0) {
+ var n = !0,
+ r = !1,
+ o = void 0;
+ try {
+ for (
+ var l, s = i[Symbol.iterator]();
+ !(n = (l = s.next()).done);
+ n = !0
+ ) {
+ var u = l.value,
+ c = e;
+ u.isFile
+ ? u.file(function (e) {
+ if (
+ !c.options.ignoreHiddenFiles ||
+ "." !== e.name.substring(0, 1)
+ )
+ return (
+ (e.fullPath = ""
+ .concat(t, "/")
+ .concat(e.name)),
+ c.addFile(e)
+ );
+ })
+ : u.isDirectory &&
+ e._addFilesFromDirectory(
+ u,
+ "".concat(t, "/").concat(u.name)
+ );
+ }
+ } catch (e) {
+ (r = !0), (o = e);
+ } finally {
+ try {
+ n || null == s.return || s.return();
+ } finally {
+ if (r) throw o;
+ }
+ }
+ a();
+ }
+ return null;
+ }, r);
+ };
+ return a();
+ },
+ },
+ {
+ key: "accept",
+ value: function (e, t) {
+ this.options.maxFilesize &&
+ e.size > 1048576 * this.options.maxFilesize
+ ? t(
+ this.options.dictFileTooBig
+ .replace(
+ "{{filesize}}",
+ Math.round(e.size / 1024 / 10.24) / 100
+ )
+ .replace("{{maxFilesize}}", this.options.maxFilesize)
+ )
+ : o.isValidFile(e, this.options.acceptedFiles)
+ ? null != this.options.maxFiles &&
+ this.getAcceptedFiles().length >= this.options.maxFiles
+ ? (t(
+ this.options.dictMaxFilesExceeded.replace(
+ "{{maxFiles}}",
+ this.options.maxFiles
+ )
+ ),
+ this.emit("maxfilesexceeded", e))
+ : this.options.accept.call(this, e, t)
+ : t(this.options.dictInvalidFileType);
+ },
+ },
+ {
+ key: "addFile",
+ value: function (e) {
+ var t = this;
+ (e.upload = {
+ uuid: o.uuidv4(),
+ progress: 0,
+ total: e.size,
+ bytesSent: 0,
+ filename: this._renameFile(e),
+ }),
+ this.files.push(e),
+ (e.status = o.ADDED),
+ this.emit("addedfile", e),
+ this._enqueueThumbnail(e),
+ this.accept(e, function (i) {
+ i
+ ? ((e.accepted = !1), t._errorProcessing([e], i))
+ : ((e.accepted = !0),
+ t.options.autoQueue && t.enqueueFile(e)),
+ t._updateMaxFilesReachedClass();
+ });
+ },
+ },
+ {
+ key: "enqueueFiles",
+ value: function (e) {
+ var t = !0,
+ i = !1,
+ n = void 0;
+ try {
+ for (
+ var r, a = e[Symbol.iterator]();
+ !(t = (r = a.next()).done);
+ t = !0
+ ) {
+ var o = r.value;
+ this.enqueueFile(o);
+ }
+ } catch (e) {
+ (i = !0), (n = e);
+ } finally {
+ try {
+ t || null == a.return || a.return();
+ } finally {
+ if (i) throw n;
+ }
+ }
+ return null;
+ },
+ },
+ {
+ key: "enqueueFile",
+ value: function (e) {
+ if (e.status !== o.ADDED || !0 !== e.accepted)
+ throw new Error(
+ "This file can't be queued because it has already been processed or was rejected."
+ );
+ var t = this;
+ if (((e.status = o.QUEUED), this.options.autoProcessQueue))
+ return setTimeout(function () {
+ return t.processQueue();
+ }, 0);
+ },
+ },
+ {
+ key: "_enqueueThumbnail",
+ value: function (e) {
+ if (
+ this.options.createImageThumbnails &&
+ e.type.match(/image.*/) &&
+ e.size <= 1048576 * this.options.maxThumbnailFilesize
+ ) {
+ var t = this;
+ return (
+ this._thumbnailQueue.push(e),
+ setTimeout(function () {
+ return t._processThumbnailQueue();
+ }, 0)
+ );
+ }
+ },
+ },
+ {
+ key: "_processThumbnailQueue",
+ value: function () {
+ var e = this;
+ if (
+ !this._processingThumbnail &&
+ 0 !== this._thumbnailQueue.length
+ ) {
+ this._processingThumbnail = !0;
+ var t = this._thumbnailQueue.shift();
+ return this.createThumbnail(
+ t,
+ this.options.thumbnailWidth,
+ this.options.thumbnailHeight,
+ this.options.thumbnailMethod,
+ !0,
+ function (i) {
+ return (
+ e.emit("thumbnail", t, i),
+ (e._processingThumbnail = !1),
+ e._processThumbnailQueue()
+ );
+ }
+ );
+ }
+ },
+ },
+ {
+ key: "removeFile",
+ value: function (e) {
+ if (
+ (e.status === o.UPLOADING && this.cancelUpload(e),
+ (this.files = m(this.files, e)),
+ this.emit("removedfile", e),
+ 0 === this.files.length)
+ )
+ return this.emit("reset");
+ },
+ },
+ {
+ key: "removeAllFiles",
+ value: function (e) {
+ null == e && (e = !1);
+ var t = !0,
+ i = !1,
+ n = void 0;
+ try {
+ for (
+ var r, a = this.files.slice()[Symbol.iterator]();
+ !(t = (r = a.next()).done);
+ t = !0
+ ) {
+ var l = r.value;
+ (l.status !== o.UPLOADING || e) && this.removeFile(l);
+ }
+ } catch (e) {
+ (i = !0), (n = e);
+ } finally {
+ try {
+ t || null == a.return || a.return();
+ } finally {
+ if (i) throw n;
+ }
+ }
+ return null;
+ },
+ },
+ {
+ key: "resizeImage",
+ value: function (e, t, i, n, r) {
+ var a = this;
+ return this.createThumbnail(e, t, i, n, !0, function (t, i) {
+ if (null == i) return r(e);
+ var n = a.options.resizeMimeType;
+ null == n && (n = e.type);
+ var l = i.toDataURL(n, a.options.resizeQuality);
+ return (
+ ("image/jpeg" !== n && "image/jpg" !== n) ||
+ (l = g.restore(e.dataURL, l)),
+ r(o.dataURItoBlob(l))
+ );
+ });
+ },
+ },
+ {
+ key: "createThumbnail",
+ value: function (e, t, i, n, r, a) {
+ var o = this,
+ l = new FileReader();
+ (l.onload = function () {
+ (e.dataURL = l.result),
+ "image/svg+xml" !== e.type
+ ? o.createThumbnailFromUrl(e, t, i, n, r, a)
+ : null != a && a(l.result);
+ }),
+ l.readAsDataURL(e);
+ },
+ },
+ {
+ key: "displayExistingFile",
+ value: function (e, t, i, n, r) {
+ var a = void 0 === r || r;
+ if ((this.emit("addedfile", e), this.emit("complete", e), a)) {
+ var o = this;
+ (e.dataURL = t),
+ this.createThumbnailFromUrl(
+ e,
+ this.options.thumbnailWidth,
+ this.options.thumbnailHeight,
+ this.options.thumbnailMethod,
+ this.options.fixOrientation,
+ function (t) {
+ o.emit("thumbnail", e, t), i && i();
+ },
+ n
+ );
+ } else this.emit("thumbnail", e, t), i && i();
+ },
+ },
+ {
+ key: "createThumbnailFromUrl",
+ value: function (e, t, i, n, r, a, o) {
+ var l = this,
+ s = document.createElement("img");
+ return (
+ o && (s.crossOrigin = o),
+ (r =
+ "from-image" !=
+ getComputedStyle(document.body).imageOrientation && r),
+ (s.onload = function () {
+ var o = l,
+ u = function (e) {
+ return e(1);
+ };
+ return (
+ "undefined" != typeof EXIF &&
+ null !== EXIF &&
+ r &&
+ (u = function (e) {
+ return EXIF.getData(s, function () {
+ return e(EXIF.getTag(this, "Orientation"));
+ });
+ }),
+ u(function (r) {
+ (e.width = s.width), (e.height = s.height);
+ var l = o.options.resize.call(o, e, t, i, n),
+ u = document.createElement("canvas"),
+ c = u.getContext("2d");
+ switch (
+ ((u.width = l.trgWidth),
+ (u.height = l.trgHeight),
+ r > 4 &&
+ ((u.width = l.trgHeight), (u.height = l.trgWidth)),
+ r)
+ ) {
+ case 2:
+ c.translate(u.width, 0), c.scale(-1, 1);
+ break;
+ case 3:
+ c.translate(u.width, u.height), c.rotate(Math.PI);
+ break;
+ case 4:
+ c.translate(0, u.height), c.scale(1, -1);
+ break;
+ case 5:
+ c.rotate(0.5 * Math.PI), c.scale(1, -1);
+ break;
+ case 6:
+ c.rotate(0.5 * Math.PI), c.translate(0, -u.width);
+ break;
+ case 7:
+ c.rotate(0.5 * Math.PI),
+ c.translate(u.height, -u.width),
+ c.scale(-1, 1);
+ break;
+ case 8:
+ c.rotate(-0.5 * Math.PI), c.translate(-u.height, 0);
+ }
+ y(
+ c,
+ s,
+ null != l.srcX ? l.srcX : 0,
+ null != l.srcY ? l.srcY : 0,
+ l.srcWidth,
+ l.srcHeight,
+ null != l.trgX ? l.trgX : 0,
+ null != l.trgY ? l.trgY : 0,
+ l.trgWidth,
+ l.trgHeight
+ );
+ var d = u.toDataURL("image/png");
+ if (null != a) return a(d, u);
+ })
+ );
+ }),
+ null != a && (s.onerror = a),
+ (s.src = e.dataURL)
+ );
+ },
+ },
+ {
+ key: "processQueue",
+ value: function () {
+ var e = this.options.parallelUploads,
+ t = this.getUploadingFiles().length,
+ i = t;
+ if (!(t >= e)) {
+ var n = this.getQueuedFiles();
+ if (n.length > 0) {
+ if (this.options.uploadMultiple)
+ return this.processFiles(n.slice(0, e - t));
+ for (; i < e; ) {
+ if (!n.length) return;
+ this.processFile(n.shift()), i++;
+ }
+ }
+ }
+ },
+ },
+ {
+ key: "processFile",
+ value: function (e) {
+ return this.processFiles([e]);
+ },
+ },
+ {
+ key: "processFiles",
+ value: function (e) {
+ var t = !0,
+ i = !1,
+ n = void 0;
+ try {
+ for (
+ var r, a = e[Symbol.iterator]();
+ !(t = (r = a.next()).done);
+ t = !0
+ ) {
+ var l = r.value;
+ (l.processing = !0),
+ (l.status = o.UPLOADING),
+ this.emit("processing", l);
+ }
+ } catch (e) {
+ (i = !0), (n = e);
+ } finally {
+ try {
+ t || null == a.return || a.return();
+ } finally {
+ if (i) throw n;
+ }
+ }
+ return (
+ this.options.uploadMultiple &&
+ this.emit("processingmultiple", e),
+ this.uploadFiles(e)
+ );
+ },
+ },
+ {
+ key: "_getFilesWithXhr",
+ value: function (e) {
+ return this.files
+ .filter(function (t) {
+ return t.xhr === e;
+ })
+ .map(function (e) {
+ return e;
+ });
+ },
+ },
+ {
+ key: "cancelUpload",
+ value: function (e) {
+ if (e.status === o.UPLOADING) {
+ var t = this._getFilesWithXhr(e.xhr),
+ i = !0,
+ n = !1,
+ r = void 0;
+ try {
+ for (
+ var a, l = t[Symbol.iterator]();
+ !(i = (a = l.next()).done);
+ i = !0
+ ) {
+ (p = a.value).status = o.CANCELED;
+ }
+ } catch (e) {
+ (n = !0), (r = e);
+ } finally {
+ try {
+ i || null == l.return || l.return();
+ } finally {
+ if (n) throw r;
+ }
+ }
+ void 0 !== e.xhr && e.xhr.abort();
+ var s = !0,
+ u = !1,
+ c = void 0;
+ try {
+ for (
+ var d, h = t[Symbol.iterator]();
+ !(s = (d = h.next()).done);
+ s = !0
+ ) {
+ var p = d.value;
+ this.emit("canceled", p);
+ }
+ } catch (e) {
+ (u = !0), (c = e);
+ } finally {
+ try {
+ s || null == h.return || h.return();
+ } finally {
+ if (u) throw c;
+ }
+ }
+ this.options.uploadMultiple &&
+ this.emit("canceledmultiple", t);
+ } else
+ (e.status !== o.ADDED && e.status !== o.QUEUED) ||
+ ((e.status = o.CANCELED),
+ this.emit("canceled", e),
+ this.options.uploadMultiple &&
+ this.emit("canceledmultiple", [e]));
+ if (this.options.autoProcessQueue) return this.processQueue();
+ },
+ },
+ {
+ key: "resolveOption",
+ value: function (e) {
+ for (
+ var t = arguments.length,
+ i = new Array(t > 1 ? t - 1 : 0),
+ n = 1;
+ n < t;
+ n++
+ )
+ i[n - 1] = arguments[n];
+ return "function" == typeof e ? e.apply(this, i) : e;
+ },
+ },
+ {
+ key: "uploadFile",
+ value: function (e) {
+ return this.uploadFiles([e]);
+ },
+ },
+ {
+ key: "uploadFiles",
+ value: function (e) {
+ var t = this;
+ this._transformFiles(e, function (i) {
+ if (t.options.chunking) {
+ var n = i[0];
+ (e[0].upload.chunked =
+ t.options.chunking &&
+ (t.options.forceChunking ||
+ n.size > t.options.chunkSize)),
+ (e[0].upload.totalChunkCount = Math.ceil(
+ n.size / t.options.chunkSize
+ ));
+ }
+ if (e[0].upload.chunked) {
+ var r = t,
+ a = t,
+ l = e[0];
+ n = i[0];
+ l.upload.chunks = [];
+ var s = function () {
+ for (var t = 0; void 0 !== l.upload.chunks[t]; ) t++;
+ if (!(t >= l.upload.totalChunkCount)) {
+ 0;
+ var i = t * r.options.chunkSize,
+ a = Math.min(i + r.options.chunkSize, n.size),
+ s = {
+ name: r._getParamName(0),
+ data: n.webkitSlice
+ ? n.webkitSlice(i, a)
+ : n.slice(i, a),
+ filename: l.upload.filename,
+ chunkIndex: t,
+ };
+ (l.upload.chunks[t] = {
+ file: l,
+ index: t,
+ dataBlock: s,
+ status: o.UPLOADING,
+ progress: 0,
+ retries: 0,
+ }),
+ r._uploadData(e, [s]);
+ }
+ };
+ if (
+ ((l.upload.finishedChunkUpload = function (t, i) {
+ var n = a,
+ r = !0;
+ (t.status = o.SUCCESS),
+ (t.dataBlock = null),
+ (t.response = t.xhr.responseText),
+ (t.responseHeaders = t.xhr.getAllResponseHeaders()),
+ (t.xhr = null);
+ for (var u = 0; u < l.upload.totalChunkCount; u++) {
+ if (void 0 === l.upload.chunks[u]) return s();
+ l.upload.chunks[u].status !== o.SUCCESS && (r = !1);
+ }
+ r &&
+ a.options.chunksUploaded(l, function () {
+ n._finished(e, i, null);
+ });
+ }),
+ t.options.parallelChunkUploads)
+ )
+ for (var u = 0; u < l.upload.totalChunkCount; u++) s();
+ else s();
+ } else {
+ var c = [];
+ for (u = 0; u < e.length; u++)
+ c[u] = {
+ name: t._getParamName(u),
+ data: i[u],
+ filename: e[u].upload.filename,
+ };
+ t._uploadData(e, c);
+ }
+ });
+ },
+ },
+ {
+ key: "_getChunk",
+ value: function (e, t) {
+ for (var i = 0; i < e.upload.totalChunkCount; i++)
+ if (
+ void 0 !== e.upload.chunks[i] &&
+ e.upload.chunks[i].xhr === t
+ )
+ return e.upload.chunks[i];
+ },
+ },
+ {
+ key: "_uploadData",
+ value: function (t, i) {
+ var n = this,
+ r = this,
+ a = this,
+ o = this,
+ l = new XMLHttpRequest(),
+ s = !0,
+ c = !1,
+ d = void 0;
+ try {
+ for (
+ var h = t[Symbol.iterator]();
+ !(s = (x = h.next()).done);
+ s = !0
+ ) {
+ (g = x.value).xhr = l;
+ }
+ } catch (e) {
+ (c = !0), (d = e);
+ } finally {
+ try {
+ s || null == h.return || h.return();
+ } finally {
+ if (c) throw d;
+ }
+ }
+ t[0].upload.chunked &&
+ (t[0].upload.chunks[i[0].chunkIndex].xhr = l);
+ var p = this.resolveOption(this.options.method, t, i),
+ f = this.resolveOption(this.options.url, t, i);
+ l.open(p, f, !0),
+ this.resolveOption(this.options.timeout, t) &&
+ (l.timeout = this.resolveOption(this.options.timeout, t)),
+ (l.withCredentials = !!this.options.withCredentials),
+ (l.onload = function (e) {
+ n._finishedUploading(t, l, e);
+ }),
+ (l.ontimeout = function () {
+ r._handleUploadError(
+ t,
+ l,
+ "Request timedout after ".concat(
+ r.options.timeout / 1e3,
+ " seconds"
+ )
+ );
+ }),
+ (l.onerror = function () {
+ a._handleUploadError(t, l);
+ }),
+ ((null != l.upload ? l.upload : l).onprogress = function (e) {
+ return o._updateFilesUploadProgress(t, l, e);
+ });
+ var m = this.options.defaultHeaders
+ ? {
+ Accept: "application/json",
+ "Cache-Control": "no-cache",
+ "X-Requested-With": "XMLHttpRequest",
+ }
+ : {};
+ for (var v in (this.options.binaryBody &&
+ (m["Content-Type"] = t[0].type),
+ this.options.headers && e(u)(m, this.options.headers),
+ m)) {
+ var y = m[v];
+ y && l.setRequestHeader(v, y);
+ }
+ if (this.options.binaryBody) {
+ (s = !0), (c = !1), (d = void 0);
+ try {
+ for (
+ h = t[Symbol.iterator]();
+ !(s = (x = h.next()).done);
+ s = !0
+ ) {
+ var g = x.value;
+ this.emit("sending", g, l);
+ }
+ } catch (e) {
+ (c = !0), (d = e);
+ } finally {
+ try {
+ s || null == h.return || h.return();
+ } finally {
+ if (c) throw d;
+ }
+ }
+ this.options.uploadMultiple &&
+ this.emit("sendingmultiple", t, l),
+ this.submitRequest(l, null, t);
+ } else {
+ var b = new FormData();
+ if (this.options.params) {
+ var k = this.options.params;
+ for (var w in ("function" == typeof k &&
+ (k = k.call(
+ this,
+ t,
+ l,
+ t[0].upload.chunked ? this._getChunk(t[0], l) : null
+ )),
+ k)) {
+ var F = k[w];
+ if (Array.isArray(F))
+ for (var E = 0; E < F.length; E++) b.append(w, F[E]);
+ else b.append(w, F);
+ }
+ }
+ (s = !0), (c = !1), (d = void 0);
+ try {
+ var x;
+ for (
+ h = t[Symbol.iterator]();
+ !(s = (x = h.next()).done);
+ s = !0
+ ) {
+ g = x.value;
+ this.emit("sending", g, l, b);
+ }
+ } catch (e) {
+ (c = !0), (d = e);
+ } finally {
+ try {
+ s || null == h.return || h.return();
+ } finally {
+ if (c) throw d;
+ }
+ }
+ this.options.uploadMultiple &&
+ this.emit("sendingmultiple", t, l, b),
+ this._addFormElementData(b);
+ for (E = 0; E < i.length; E++) {
+ var z = i[E];
+ b.append(z.name, z.data, z.filename);
+ }
+ this.submitRequest(l, b, t);
+ }
+ },
+ },
+ {
+ key: "_transformFiles",
+ value: function (e, t) {
+ for (
+ var i = this,
+ n = function (n) {
+ i.options.transformFile.call(i, e[n], function (i) {
+ (r[n] = i), ++a === e.length && t(r);
+ });
+ },
+ r = [],
+ a = 0,
+ o = 0;
+ o < e.length;
+ o++
+ )
+ n(o);
+ },
+ },
+ {
+ key: "_addFormElementData",
+ value: function (e) {
+ var t = !0,
+ i = !1,
+ n = void 0;
+ if ("FORM" === this.element.tagName)
+ try {
+ for (
+ var r = this.element
+ .querySelectorAll("input, textarea, select, button")
+ [Symbol.iterator]();
+ !(t = (s = r.next()).done);
+ t = !0
+ ) {
+ var a = s.value,
+ o = a.getAttribute("name"),
+ l = a.getAttribute("type");
+ if ((l && (l = l.toLowerCase()), null != o))
+ if (
+ "SELECT" === a.tagName &&
+ a.hasAttribute("multiple")
+ ) {
+ (t = !0), (i = !1), (n = void 0);
+ try {
+ var s;
+ for (
+ r = a.options[Symbol.iterator]();
+ !(t = (s = r.next()).done);
+ t = !0
+ ) {
+ var u = s.value;
+ u.selected && e.append(o, u.value);
+ }
+ } catch (e) {
+ (i = !0), (n = e);
+ } finally {
+ try {
+ t || null == r.return || r.return();
+ } finally {
+ if (i) throw n;
+ }
+ }
+ } else
+ (!l ||
+ ("checkbox" !== l && "radio" !== l) ||
+ a.checked) &&
+ e.append(o, a.value);
+ }
+ } catch (e) {
+ (i = !0), (n = e);
+ } finally {
+ try {
+ t || null == r.return || r.return();
+ } finally {
+ if (i) throw n;
+ }
+ }
+ },
+ },
+ {
+ key: "_updateFilesUploadProgress",
+ value: function (e, t, i) {
+ var n = !0,
+ r = !1,
+ a = void 0;
+ if (e[0].upload.chunked) {
+ c = e[0];
+ var o = this._getChunk(c, t);
+ i
+ ? ((o.progress = (100 * i.loaded) / i.total),
+ (o.total = i.total),
+ (o.bytesSent = i.loaded))
+ : ((o.progress = 100), (o.bytesSent = o.total)),
+ (c.upload.progress = 0),
+ (c.upload.total = 0),
+ (c.upload.bytesSent = 0);
+ for (var l = 0; l < c.upload.totalChunkCount; l++)
+ c.upload.chunks[l] &&
+ void 0 !== c.upload.chunks[l].progress &&
+ ((c.upload.progress += c.upload.chunks[l].progress),
+ (c.upload.total += c.upload.chunks[l].total),
+ (c.upload.bytesSent += c.upload.chunks[l].bytesSent));
+ (c.upload.progress =
+ c.upload.progress / c.upload.totalChunkCount),
+ this.emit(
+ "uploadprogress",
+ c,
+ c.upload.progress,
+ c.upload.bytesSent
+ );
+ } else
+ try {
+ for (
+ var s, u = e[Symbol.iterator]();
+ !(n = (s = u.next()).done);
+ n = !0
+ ) {
+ var c;
+ ((c = s.value).upload.total &&
+ c.upload.bytesSent &&
+ c.upload.bytesSent == c.upload.total) ||
+ (i
+ ? ((c.upload.progress = (100 * i.loaded) / i.total),
+ (c.upload.total = i.total),
+ (c.upload.bytesSent = i.loaded))
+ : ((c.upload.progress = 100),
+ (c.upload.bytesSent = c.upload.total)),
+ this.emit(
+ "uploadprogress",
+ c,
+ c.upload.progress,
+ c.upload.bytesSent
+ ));
+ }
+ } catch (e) {
+ (r = !0), (a = e);
+ } finally {
+ try {
+ n || null == u.return || u.return();
+ } finally {
+ if (r) throw a;
+ }
+ }
+ },
+ },
+ {
+ key: "_finishedUploading",
+ value: function (e, t, i) {
+ var n;
+ if (e[0].status !== o.CANCELED && 4 === t.readyState) {
+ if (
+ "arraybuffer" !== t.responseType &&
+ "blob" !== t.responseType &&
+ ((n = t.responseText),
+ t.getResponseHeader("content-type") &&
+ ~t
+ .getResponseHeader("content-type")
+ .indexOf("application/json"))
+ )
+ try {
+ n = JSON.parse(n);
+ } catch (e) {
+ (i = e), (n = "Invalid JSON response from server.");
+ }
+ this._updateFilesUploadProgress(e, t),
+ 200 <= t.status && t.status < 300
+ ? e[0].upload.chunked
+ ? e[0].upload.finishedChunkUpload(
+ this._getChunk(e[0], t),
+ n
+ )
+ : this._finished(e, n, i)
+ : this._handleUploadError(e, t, n);
+ }
+ },
+ },
+ {
+ key: "_handleUploadError",
+ value: function (e, t, i) {
+ if (e[0].status !== o.CANCELED) {
+ if (e[0].upload.chunked && this.options.retryChunks) {
+ var n = this._getChunk(e[0], t);
+ if (n.retries++ < this.options.retryChunksLimit)
+ return void this._uploadData(e, [n.dataBlock]);
+ console.warn("Retried this chunk too often. Giving up.");
+ }
+ this._errorProcessing(
+ e,
+ i ||
+ this.options.dictResponseError.replace(
+ "{{statusCode}}",
+ t.status
+ ),
+ t
+ );
+ }
+ },
+ },
+ {
+ key: "submitRequest",
+ value: function (e, t, i) {
+ if (1 == e.readyState)
+ if (this.options.binaryBody)
+ if (i[0].upload.chunked) {
+ var n = this._getChunk(i[0], e);
+ e.send(n.dataBlock.data);
+ } else e.send(i[0]);
+ else e.send(t);
+ else
+ console.warn(
+ "Cannot send this request because the XMLHttpRequest.readyState is not OPENED."
+ );
+ },
+ },
+ {
+ key: "_finished",
+ value: function (e, t, i) {
+ var n = !0,
+ r = !1,
+ a = void 0;
+ try {
+ for (
+ var l, s = e[Symbol.iterator]();
+ !(n = (l = s.next()).done);
+ n = !0
+ ) {
+ var u = l.value;
+ (u.status = o.SUCCESS),
+ this.emit("success", u, t, i),
+ this.emit("complete", u);
+ }
+ } catch (e) {
+ (r = !0), (a = e);
+ } finally {
+ try {
+ n || null == s.return || s.return();
+ } finally {
+ if (r) throw a;
+ }
+ }
+ if (
+ (this.options.uploadMultiple &&
+ (this.emit("successmultiple", e, t, i),
+ this.emit("completemultiple", e)),
+ this.options.autoProcessQueue)
+ )
+ return this.processQueue();
+ },
+ },
+ {
+ key: "_errorProcessing",
+ value: function (e, t, i) {
+ var n = !0,
+ r = !1,
+ a = void 0;
+ try {
+ for (
+ var l, s = e[Symbol.iterator]();
+ !(n = (l = s.next()).done);
+ n = !0
+ ) {
+ var u = l.value;
+ (u.status = o.ERROR),
+ this.emit("error", u, t, i),
+ this.emit("complete", u);
+ }
+ } catch (e) {
+ (r = !0), (a = e);
+ } finally {
+ try {
+ n || null == s.return || s.return();
+ } finally {
+ if (r) throw a;
+ }
+ }
+ if (
+ (this.options.uploadMultiple &&
+ (this.emit("errormultiple", e, t, i),
+ this.emit("completemultiple", e)),
+ this.options.autoProcessQueue)
+ )
+ return this.processQueue();
+ },
+ },
+ ],
+ [
+ {
+ key: "initClass",
+ value: function () {
+ (this.prototype.Emitter = h),
+ (this.prototype.events = [
+ "drop",
+ "dragstart",
+ "dragend",
+ "dragenter",
+ "dragover",
+ "dragleave",
+ "addedfile",
+ "addedfiles",
+ "removedfile",
+ "thumbnail",
+ "error",
+ "errormultiple",
+ "processing",
+ "processingmultiple",
+ "uploadprogress",
+ "totaluploadprogress",
+ "sending",
+ "sendingmultiple",
+ "success",
+ "successmultiple",
+ "canceled",
+ "canceledmultiple",
+ "complete",
+ "completemultiple",
+ "reset",
+ "maxfilesexceeded",
+ "maxfilesreached",
+ "queuecomplete",
+ ]),
+ (this.prototype._thumbnailQueue = []),
+ (this.prototype._processingThumbnail = !1);
+ },
+ },
+ {
+ key: "uuidv4",
+ value: function () {
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
+ /[xy]/g,
+ function (e) {
+ var t = (16 * Math.random()) | 0;
+ return ("x" === e ? t : (3 & t) | 8).toString(16);
+ }
+ );
+ },
+ },
+ ]
+ ),
+ o
+ );
+ })(h);
+ f.initClass(),
+ (f.options = {}),
+ (f.optionsForElement = function (e) {
+ return e.getAttribute("id") ? f.options[v(e.getAttribute("id"))] : void 0;
+ }),
+ (f.instances = []),
+ (f.forElement = function (e) {
+ if (
+ ("string" == typeof e && (e = document.querySelector(e)),
+ null == (null != e ? e.dropzone : void 0))
+ )
+ throw new Error(
+ "No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone."
+ );
+ return e.dropzone;
+ }),
+ (f.discover = function () {
+ var e;
+ if (document.querySelectorAll) e = document.querySelectorAll(".dropzone");
+ else {
+ e = [];
+ var t = function (t) {
+ return (function () {
+ var i = [],
+ n = !0,
+ r = !1,
+ a = void 0;
+ try {
+ for (
+ var o, l = t[Symbol.iterator]();
+ !(n = (o = l.next()).done);
+ n = !0
+ ) {
+ var s = o.value;
+ /(^| )dropzone($| )/.test(s.className)
+ ? i.push(e.push(s))
+ : i.push(void 0);
+ }
+ } catch (e) {
+ (r = !0), (a = e);
+ } finally {
+ try {
+ n || null == l.return || l.return();
+ } finally {
+ if (r) throw a;
+ }
+ }
+ return i;
+ })();
+ };
+ t(document.getElementsByTagName("div")),
+ t(document.getElementsByTagName("form"));
+ }
+ return (function () {
+ var t = [],
+ i = !0,
+ n = !1,
+ r = void 0;
+ try {
+ for (
+ var a, o = e[Symbol.iterator]();
+ !(i = (a = o.next()).done);
+ i = !0
+ ) {
+ var l = a.value;
+ !1 !== f.optionsForElement(l) ? t.push(new f(l)) : t.push(void 0);
+ }
+ } catch (e) {
+ (n = !0), (r = e);
+ } finally {
+ try {
+ i || null == o.return || o.return();
+ } finally {
+ if (n) throw r;
+ }
+ }
+ return t;
+ })();
+ }),
+ (f.blockedBrowsers = [/opera.*(Macintosh|Windows Phone).*version\/12/i]),
+ (f.isBrowserSupported = function () {
+ var e = !0;
+ if (
+ window.File &&
+ window.FileReader &&
+ window.FileList &&
+ window.Blob &&
+ window.FormData &&
+ document.querySelector
+ )
+ if ("classList" in document.createElement("a")) {
+ void 0 !== f.blacklistedBrowsers &&
+ (f.blockedBrowsers = f.blacklistedBrowsers);
+ var t = !0,
+ i = !1,
+ n = void 0;
+ try {
+ for (
+ var r, a = f.blockedBrowsers[Symbol.iterator]();
+ !(t = (r = a.next()).done);
+ t = !0
+ ) {
+ r.value.test(navigator.userAgent) && (e = !1);
+ }
+ } catch (e) {
+ (i = !0), (n = e);
+ } finally {
+ try {
+ t || null == a.return || a.return();
+ } finally {
+ if (i) throw n;
+ }
+ }
+ } else e = !1;
+ else e = !1;
+ return e;
+ }),
+ (f.dataURItoBlob = function (e) {
+ for (
+ var t = atob(e.split(",")[1]),
+ i = e.split(",")[0].split(":")[1].split(";")[0],
+ n = new ArrayBuffer(t.length),
+ r = new Uint8Array(n),
+ a = 0,
+ o = t.length,
+ l = 0 <= o;
+ l ? a <= o : a >= o;
+ l ? a++ : a--
+ )
+ r[a] = t.charCodeAt(a);
+ return new Blob([n], { type: i });
+ });
+ var m = function (e, t) {
+ return e
+ .filter(function (e) {
+ return e !== t;
+ })
+ .map(function (e) {
+ return e;
+ });
+ },
+ v = function (e) {
+ return e.replace(/[\-_](\w)/g, function (e) {
+ return e.charAt(1).toUpperCase();
+ });
+ };
+ (f.createElement = function (e) {
+ var t = document.createElement("div");
+ return (t.innerHTML = e), t.childNodes[0];
+ }),
+ (f.elementInside = function (e, t) {
+ if (e === t) return !0;
+ for (; (e = e.parentNode); ) if (e === t) return !0;
+ return !1;
+ }),
+ (f.getElement = function (e, t) {
+ var i;
+ if (
+ ("string" == typeof e
+ ? (i = document.querySelector(e))
+ : null != e.nodeType && (i = e),
+ null == i)
+ )
+ throw new Error(
+ "Invalid `".concat(
+ t,
+ "` option provided. Please provide a CSS selector or a plain HTML element."
+ )
+ );
+ return i;
+ }),
+ (f.getElements = function (e, t) {
+ var i, n;
+ if (e instanceof Array) {
+ n = [];
+ try {
+ var r = !0,
+ a = !1,
+ o = void 0;
+ try {
+ for (
+ var l = e[Symbol.iterator]();
+ !(r = (s = l.next()).done);
+ r = !0
+ )
+ (i = s.value), n.push(this.getElement(i, t));
+ } catch (e) {
+ (a = !0), (o = e);
+ } finally {
+ try {
+ r || null == l.return || l.return();
+ } finally {
+ if (a) throw o;
+ }
+ }
+ } catch (e) {
+ n = null;
+ }
+ } else if ("string" == typeof e) {
+ n = [];
+ (r = !0), (a = !1), (o = void 0);
+ try {
+ var s;
+ for (
+ l = document.querySelectorAll(e)[Symbol.iterator]();
+ !(r = (s = l.next()).done);
+ r = !0
+ )
+ (i = s.value), n.push(i);
+ } catch (e) {
+ (a = !0), (o = e);
+ } finally {
+ try {
+ r || null == l.return || l.return();
+ } finally {
+ if (a) throw o;
+ }
+ }
+ } else null != e.nodeType && (n = [e]);
+ if (null == n || !n.length)
+ throw new Error(
+ "Invalid `".concat(
+ t,
+ "` option provided. Please provide a CSS selector, a plain HTML element or a list of those."
+ )
+ );
+ return n;
+ }),
+ (f.confirm = function (e, t, i) {
+ return window.confirm(e) ? t() : null != i ? i() : void 0;
+ }),
+ (f.isValidFile = function (e, t) {
+ if (!t) return !0;
+ t = t.split(",");
+ var i = e.type,
+ n = i.replace(/\/.*$/, ""),
+ r = !0,
+ a = !1,
+ o = void 0;
+ try {
+ for (
+ var l, s = t[Symbol.iterator]();
+ !(r = (l = s.next()).done);
+ r = !0
+ ) {
+ var u = l.value;
+ if ("." === (u = u.trim()).charAt(0)) {
+ if (
+ -1 !==
+ e.name
+ .toLowerCase()
+ .indexOf(u.toLowerCase(), e.name.length - u.length)
+ )
+ return !0;
+ } else if (/\/\*$/.test(u)) {
+ if (n === u.replace(/\/.*$/, "")) return !0;
+ } else if (i === u) return !0;
+ }
+ } catch (e) {
+ (a = !0), (o = e);
+ } finally {
+ try {
+ r || null == s.return || s.return();
+ } finally {
+ if (a) throw o;
+ }
+ }
+ return !1;
+ }),
+ "undefined" != typeof jQuery &&
+ null !== jQuery &&
+ (jQuery.fn.dropzone = function (e) {
+ return this.each(function () {
+ return new f(this, e);
+ });
+ }),
+ (f.ADDED = "added"),
+ (f.QUEUED = "queued"),
+ (f.ACCEPTED = f.QUEUED),
+ (f.UPLOADING = "uploading"),
+ (f.PROCESSING = f.UPLOADING),
+ (f.CANCELED = "canceled"),
+ (f.ERROR = "error"),
+ (f.SUCCESS = "success");
+ var y = function (e, t, i, n, r, a, o, l, s, u) {
+ var c = (function (e) {
+ e.naturalWidth;
+ var t = e.naturalHeight,
+ i = document.createElement("canvas");
+ (i.width = 1), (i.height = t);
+ var n = i.getContext("2d");
+ n.drawImage(e, 0, 0);
+ for (
+ var r = n.getImageData(1, 0, 1, t).data, a = 0, o = t, l = t;
+ l > a;
+
+ )
+ 0 === r[4 * (l - 1) + 3] ? (o = l) : (a = l), (l = (o + a) >> 1);
+ var s = l / t;
+ return 0 === s ? 1 : s;
+ })(t);
+ return e.drawImage(t, i, n, r, a, o, l, s, u / c);
+ },
+ g = (function () {
+ "use strict";
+ function e() {
+ i(this, e);
+ }
+ return (
+ r(e, null, [
+ {
+ key: "initClass",
+ value: function () {
+ this.KEY_STR =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+ },
+ },
+ {
+ key: "encode64",
+ value: function (e) {
+ for (
+ var t = "",
+ i = void 0,
+ n = void 0,
+ r = "",
+ a = void 0,
+ o = void 0,
+ l = void 0,
+ s = "",
+ u = 0;
+ (a = (i = e[u++]) >> 2),
+ (o = ((3 & i) << 4) | ((n = e[u++]) >> 4)),
+ (l = ((15 & n) << 2) | ((r = e[u++]) >> 6)),
+ (s = 63 & r),
+ isNaN(n) ? (l = s = 64) : isNaN(r) && (s = 64),
+ (t =
+ t +
+ this.KEY_STR.charAt(a) +
+ this.KEY_STR.charAt(o) +
+ this.KEY_STR.charAt(l) +
+ this.KEY_STR.charAt(s)),
+ (i = n = r = ""),
+ (a = o = l = s = ""),
+ u < e.length;
+
+ );
+ return t;
+ },
+ },
+ {
+ key: "restore",
+ value: function (e, t) {
+ if (!e.match("data:image/jpeg;base64,")) return t;
+ var i = this.decode64(e.replace("data:image/jpeg;base64,", "")),
+ n = this.slice2Segments(i),
+ r = this.exifManipulation(t, n);
+ return "data:image/jpeg;base64,".concat(this.encode64(r));
+ },
+ },
+ {
+ key: "exifManipulation",
+ value: function (e, t) {
+ var i = this.getExifArray(t),
+ n = this.insertExif(e, i);
+ return new Uint8Array(n);
+ },
+ },
+ {
+ key: "getExifArray",
+ value: function (e) {
+ for (var t = void 0, i = 0; i < e.length; ) {
+ if ((255 === (t = e[i])[0]) & (225 === t[1])) return t;
+ i++;
+ }
+ return [];
+ },
+ },
+ {
+ key: "insertExif",
+ value: function (e, t) {
+ var i = e.replace("data:image/jpeg;base64,", ""),
+ n = this.decode64(i),
+ r = n.indexOf(255, 3),
+ a = n.slice(0, r),
+ o = n.slice(r),
+ l = a;
+ return (l = (l = l.concat(t)).concat(o));
+ },
+ },
+ {
+ key: "slice2Segments",
+ value: function (e) {
+ for (var t = 0, i = []; ; ) {
+ if ((255 === e[t]) & (218 === e[t + 1])) break;
+ if ((255 === e[t]) & (216 === e[t + 1])) t += 2;
+ else {
+ var n = t + (256 * e[t + 2] + e[t + 3]) + 2,
+ r = e.slice(t, n);
+ i.push(r), (t = n);
+ }
+ if (t > e.length) break;
+ }
+ return i;
+ },
+ },
+ {
+ key: "decode64",
+ value: function (e) {
+ var t = void 0,
+ i = void 0,
+ n = "",
+ r = void 0,
+ a = void 0,
+ o = "",
+ l = 0,
+ s = [];
+ for (
+ /[^A-Za-z0-9\+\/\=]/g.exec(e) &&
+ console.warn(
+ "There were invalid base64 characters in the input text.\nValid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\nExpect errors in decoding."
+ ),
+ e = e.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+ (t =
+ (this.KEY_STR.indexOf(e.charAt(l++)) << 2) |
+ ((r = this.KEY_STR.indexOf(e.charAt(l++))) >> 4)),
+ (i =
+ ((15 & r) << 4) |
+ ((a = this.KEY_STR.indexOf(e.charAt(l++))) >> 2)),
+ (n =
+ ((3 & a) << 6) | (o = this.KEY_STR.indexOf(e.charAt(l++)))),
+ s.push(t),
+ 64 !== a && s.push(i),
+ 64 !== o && s.push(n),
+ (t = i = n = ""),
+ (r = a = o = ""),
+ l < e.length;
+
+ );
+ return s;
+ },
+ },
+ ]),
+ e
+ );
+ })();
+ g.initClass();
+ window.Dropzone = f;
+})();
//# sourceMappingURL=dropzone-min.js.map
diff --git a/src/ui/static/js/logs.js b/src/ui/static/js/logs.js
index b84219d8f..b401b09e6 100644
--- a/src/ui/static/js/logs.js
+++ b/src/ui/static/js/logs.js
@@ -1,12 +1,11 @@
import { Checkbox } from "./utils.js";
-//import AirDatepicker from "./air-datepicker/index.js";
+import Datepicker from "./datepicker/datepicker.js";
class LogsDropdown {
constructor(prefix = "logs") {
this.prefix = prefix;
this.container = document.querySelector("main");
this.initDropdown();
- this.logListContainer = document.querySelector(`[${this.prefix}-list]`);
}
initDropdown() {
@@ -163,10 +162,9 @@ class FetchLogs {
this.toDateInp = document.querySelector("input#to-date");
this.fromDate = "";
this.toDate = "";
-
this.isLiveUpdate = false;
this.updateDelay = 2000;
- this.lastUpdate = Math.round(Date.now() / 1000 - 86400);
+ this.lastUpdate = Date.now() - 86400000;
this.container = document.querySelector(`[${this.prefix}-settings]`);
this.logListContainer = document.querySelector(`[${this.prefix}-list]`);
this.submitSettings = document.querySelector("button#submit-settings");
@@ -175,8 +173,10 @@ class FetchLogs {
init() {
this.submitSettings.addEventListener("click", (e) => {
+ //remove prev logs
+ this.logListContainer.textContent = "";
//wait if live update previously
- if (this.isLiveUpdate) {
+ if (this.isLiveUpdate && !this.toDate) {
setTimeout(() => {
const isSettings = this.getSettings();
return isSettings ? this.getLogsFromToDate() : "";
@@ -186,18 +186,46 @@ class FetchLogs {
return isSettings ? this.getLogsFromToDate() : "";
}
});
+ //disabled/enabled filed logic
+ this.toDateInp.addEventListener("input", (e) => {
+ this.toDateInp.value
+ ? this.updateDelayInp.setAttribute("disabled", "")
+ : this.updateDelayInp.removeAttribute("disabled");
+ this.toDateInp.value
+ ? this.liveUpdateInp.setAttribute("disabled", "")
+ : this.liveUpdateInp.removeAttribute("disabled");
+ });
+
+ this.updateDelayInp.addEventListener("input", (e) => {
+ this.updateDelayInp.value
+ ? this.toDateInp.setAttribute("disabled", "")
+ : this.toDateInp.removeAttribute("disabled");
+ });
+
+ this.liveUpdateInp.addEventListener("input", (e) => {
+ this.liveUpdateInp.checked
+ ? this.toDateInp.setAttribute("disabled", "")
+ : this.toDateInp.removeAttribute("disabled");
+ });
}
getSettings() {
//get settings
+ //check valid instance name
this.instanceName = this.instance.textContent;
if (!this.instanceName || this.instanceName.trim() === "none") return false;
- this.formDate = this.fromDateInp.valueAsNumber
- ? this.fromDateInp.valueAsNumber
- : Math.round(Date.now() / 1000 - 86400);
- this.toDate = this.toDateInp.valueAsNumber
- ? this.toDateInp.valueAsNumber
- : "";
+ //if a date value exist, check if is a timestamp
+ if (this.fromDateInp.value && isNaN(Date.parse(this.fromDateInp.value)))
+ return false;
+ if (this.toDateInp.value && isNaN(Date.parse(this.toDateInp.value)))
+ return false;
+ //check valid date
+ this.fromDate = Date.parse(this.fromDateInp.value)
+ ? Date.parse(this.fromDateInp.value)
+ : Date.now() - 86400000;
+ this.toDate = Date.parse(this.toDateInp.value)
+ ? Date.parse(this.toDateInp.value)
+ : false;
this.updateDelay =
this.updateDelayInp.value * 1000 ? this.updateDelayInp.value : 2000;
this.isLiveUpdate = this.liveUpdateInp.checked;
@@ -214,9 +242,19 @@ class FetchLogs {
}
async getLogsFromToDate() {
- const response = await fetch(
- `${location.href}/${this.lastUpdate}?from_date=${this.fromDate}?to_date=${this.toDate}`
- );
+ console.log(this.fromDate, this.toDate);
+ let response;
+ if (this.toDate) {
+ response = await fetch(
+ `${location.href}/${this.instanceName}?from_date=${this.fromDate}&to_date=${this.toDate}`
+ );
+ }
+
+ if (!this.toDate) {
+ response = await fetch(
+ `${location.href}/${this.instanceName}?from_date=${this.fromDate}`
+ );
+ }
if (response.status === 200) {
const res = await response.json();
@@ -230,7 +268,7 @@ class FetchLogs {
async getLogsSinceLastUpdate() {
const response = await fetch(
- `${location.href}/${this.lastUpdate}` +
+ `${location.href}/${this.instanceName}` +
(this.lastUpdate ? `?last_update=${this.lastUpdate}` : "")
);
@@ -272,8 +310,8 @@ class FetchLogs {
setTimeout(() => {
this.goBottomList();
}, 100);
- //loop if true
- if (this.isLiveUpdate) {
+ //loop if no to date and live update true
+ if (this.isLiveUpdate && !this.toDate) {
setTimeout(() => {
this.getLogsSinceLastUpdate();
}, this.updateDelay);
@@ -286,7 +324,7 @@ class FilterLogs {
this.prefix = prefix;
this.container = document.querySelector(`[${this.prefix}-filter]`);
this.keyInp = document.querySelector("input#keyword");
- this.lastType = "";
+ this.lastType = "all";
this.initHandler();
}
@@ -329,7 +367,6 @@ class FilterLogs {
//filter type
this.setFilterType(logs);
this.setFilterKeyword(logs);
- this.setFilterDate(logs);
}
setFilterType(logs) {
@@ -350,17 +387,30 @@ class FilterLogs {
}
setFilterKeyword(logs) {
- const keyword = this.keyInp.value;
+ const keyword = this.keyInp.value.trim().toLowerCase();
if (!keyword) return;
for (let i = 0; i < logs.length; i++) {
const el = logs[i];
- const content = el.querySelector("[logs-content]").textContent;
+ const content = el
+ .querySelector("[logs-content]")
+ .textContent.trim()
+ .toLowerCase();
if (!content.includes(keyword)) el.classList.add("hidden");
}
}
}
+class LogsDate {
+ constructor(el, options = {}) {
+ this.datepicker = new Datepicker(el, options);
+ this.init();
+ this.container = document.querySelector("[logs-settings]");
+ }
+}
+
const setCheckbox = new Checkbox("[logs-settings]");
const dropdown = new LogsDropdown();
const setLogs = new FetchLogs();
const setFilter = new FilterLogs();
+const fromDatepicker = new LogsDate(document.querySelector("input#from-date"));
+const toDatepicker = new LogsDate(document.querySelector("input#to-date"));
diff --git a/src/ui/static/js/plugins.js b/src/ui/static/js/plugins.js
index a25a4a64a..476b6da53 100644
--- a/src/ui/static/js/plugins.js
+++ b/src/ui/static/js/plugins.js
@@ -1,30 +1,247 @@
-import AirDatepicker from "./air-datepicker/index.es.js";
-
class Upload {
- constructor(prefix) {
- this.prefix = prefix;
- this.uploadDOM = document.querySelector(`[${this.prefix}-upload-button]`);
- this.uploadTxt = document.querySelector(`[${this.prefix}-upload-text]`);
- this.uploadInp = document.querySelector(`[${this.prefix}-upload-input]`);
- this.dragNdrop = document.querySelector(`[${this.prefix}-drag-and-drop]`);
+ constructor() {
+ this.dropEl = document.querySelector("#dropzone");
+ this.drop = new Dropzone(this.dropEl, {
+ paramName: "file",
+ method: "post",
+ maxFiles: 100,
+ autoProcessQueue: false,
+ uploadMultiple: true,
+ parallelUploads: 100,
+ url: "#",
+ });
+ this.submitBtn = this.dropEl.querySelector('button[type="submit"]');
this.init();
}
init() {
- this.uploadDOM.addEventListener("click", (e) => {
- this.uploadInp.click();
- });
-
- this.uploadInp.addEventListener("change", (e) => {
- this.uploadTxt.textContent = "FILES : ";
- const files = this.uploadInp.files;
- for (let i = 0; i < files.length; i++) {
- const file = files[i];
- const spanEl = document.createElement("span");
- spanEl.textContent =
- i == files.length - 1 ? `${file.name};` : `${file.name}, `;
- this.uploadTxt.append(spanEl);
- }
+ this.submitBtn.addEventListener("click", (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ drop.processQueue();
});
}
}
+
+class PluginsDropdown {
+ constructor(prefix = "plugins") {
+ this.prefix = prefix;
+ this.container = document.querySelector("main");
+ this.initDropdown();
+ }
+
+ initDropdown() {
+ this.container.addEventListener("click", (e) => {
+ //SELECT BTN LOGIC
+ try {
+ if (
+ e.target
+ .closest("button")
+ .hasAttribute(`${this.prefix}-setting-select`) &&
+ !e.target.closest("button").hasAttribute(`disabled`)
+ ) {
+ this.toggleSelectBtn(e);
+ }
+ } catch (err) {}
+ //SELECT DROPDOWN BTN LOGIC
+ try {
+ if (
+ e.target
+ .closest("button")
+ .hasAttribute(`${this.prefix}-setting-select-dropdown-btn`)
+ ) {
+ const btn = e.target.closest("button");
+ const btnValue = btn.getAttribute("value");
+ const btnSetting = btn.getAttribute(
+ `${this.prefix}-setting-select-dropdown-btn`
+ );
+ //stop if same value to avoid new fetching
+ const isSameVal = this.isSameValue(btnSetting, btnValue);
+ if (isSameVal) return this.hideDropdown(btnSetting);
+ //else, add new value to custom
+ this.setSelectNewValue(btnSetting, btnValue);
+ //close dropdown and change style
+ this.hideDropdown(btnSetting);
+ this.changeDropBtnStyle(btnSetting, btn);
+ //show / hide filter
+ if (btnSetting === "instances") {
+ this.hideFilterOnLocal(btn.getAttribute("_type"));
+ }
+ }
+ } catch (err) {}
+ });
+ }
+
+ isSameValue(btnSetting, value) {
+ const selectCustom = document.querySelector(
+ `[${this.prefix}-setting-select-text="${btnSetting}"]`
+ );
+ const currVal = selectCustom.textContent;
+ return currVal === value ? true : false;
+ }
+
+ setSelectNewValue(btnSetting, value) {
+ const selectCustom = document.querySelector(
+ `[${this.prefix}-setting-select="${btnSetting}"]`
+ );
+ selectCustom.querySelector(
+ `[${this.prefix}-setting-select-text]`
+ ).textContent = value;
+ }
+
+ hideDropdown(btnSetting) {
+ //hide dropdown
+ const dropdownEl = document.querySelector(
+ `[${this.prefix}-setting-select-dropdown="${btnSetting}"]`
+ );
+ dropdownEl.classList.add("hidden");
+ dropdownEl.classList.remove("flex");
+ //svg effect
+ const dropdownChevron = document.querySelector(
+ `svg[${this.prefix}-setting-select="${btnSetting}"]`
+ );
+ dropdownChevron.classList.remove("rotate-180");
+ }
+
+ changeDropBtnStyle(btnSetting, selectedBtn) {
+ const dropdownEl = document.querySelector(
+ `[${this.prefix}-setting-select-dropdown="${btnSetting}"]`
+ );
+ //reset dropdown btns
+ const btnEls = dropdownEl.querySelectorAll("button");
+
+ btnEls.forEach((btn) => {
+ btn.classList.remove(
+ "dark:bg-primary",
+ "bg-primary",
+ "bg-primary",
+ "text-gray-300",
+ "text-gray-300"
+ );
+ btn.classList.add("bg-white", "dark:bg-slate-700", "text-gray-700");
+ });
+ //highlight clicked btn
+ selectedBtn.classList.remove(
+ "bg-white",
+ "dark:bg-slate-700",
+ "text-gray-700"
+ );
+ selectedBtn.classList.add("dark:bg-primary", "bg-primary", "text-gray-300");
+ }
+
+ toggleSelectBtn(e) {
+ const attribut = e.target
+ .closest("button")
+ .getAttribute(`${this.prefix}-setting-select`);
+ //toggle dropdown
+ const dropdownEl = document.querySelector(
+ `[${this.prefix}-setting-select-dropdown="${attribut}"]`
+ );
+ const dropdownChevron = document.querySelector(
+ `svg[${this.prefix}-setting-select="${attribut}"]`
+ );
+ dropdownEl.classList.toggle("hidden");
+ dropdownEl.classList.toggle("flex");
+ dropdownChevron.classList.toggle("rotate-180");
+ }
+
+ //hide date filter on local
+ hideFilterOnLocal(type) {
+ console.log(type);
+ if (type === "local") {
+ this.hideInp(`input#from-date`);
+ this.hideInp(`input#to-date`);
+ }
+
+ if (type !== "local") {
+ this.showInp(`input#from-date`);
+ this.showInp(`input#to-date`);
+ }
+ }
+
+ showInp(selector) {
+ document.querySelector(selector).closest("div").classList.add("flex");
+ document.querySelector(selector).closest("div").classList.remove("hidden");
+ }
+
+ hideInp(selector) {
+ document.querySelector(selector).closest("div").classList.add("hidden");
+ document.querySelector(selector).closest("div").classList.remove("flex");
+ }
+}
+
+class FilterPlugins {
+ constructor(prefix = "plugins") {
+ this.prefix = prefix;
+ this.container = document.querySelector(`[${this.prefix}-filter]`);
+ this.keyInp = document.querySelector("input#keyword");
+ this.lastType = "all";
+ this.initHandler();
+ }
+
+ initHandler() {
+ //TYPE HANDLER
+ this.container.addEventListener("click", (e) => {
+ try {
+ if (
+ e.target
+ .closest("button")
+ .getAttribute(`${this.prefix}-setting-select-dropdown-btn`) ===
+ "types"
+ ) {
+ const btn = e.target.closest("button");
+ const btnValue = btn.getAttribute("value");
+
+ this.lastType = btnValue;
+ console.log(this.lastType);
+ //run filter
+ this.filter();
+ }
+ } catch (err) {}
+ });
+ //KEYWORD HANDLER
+ this.keyInp.addEventListener("input", (e) => {
+ this.filter();
+ });
+ }
+
+ filter() {
+ const logs = document.querySelector(`[${this.prefix}-list]`).children;
+ if (logs.length === 0) return;
+ //reset
+ for (let i = 0; i < logs.length; i++) {
+ const el = logs[i];
+ el.classList.remove("hidden");
+ }
+ //filter type
+ this.setFilterType(logs);
+ this.setFilterKeyword(logs);
+ }
+
+ setFilterType(logs) {
+ if (this.lastType === "all") return;
+ for (let i = 0; i < logs.length; i++) {
+ const el = logs[i];
+ const type = el.getAttribute(`${this.prefix}-external`).trim();
+ if (type !== this.lastType) el.classList.add("hidden");
+ }
+ }
+
+ setFilterKeyword(logs) {
+ const keyword = this.keyInp.value.trim().toLowerCase();
+ if (!keyword) return;
+ for (let i = 0; i < logs.length; i++) {
+ const el = logs[i];
+ const content = el
+ .querySelector(`[${this.prefix}-content]`)
+ .textContent.trim()
+ .toLowerCase();
+
+ if (!content.includes(keyword)) el.classList.add("hidden");
+ }
+ }
+}
+
+const setUpload = new Upload();
+const setDropdown = new PluginsDropdown();
+const setFilter = new FilterPlugins();
diff --git a/src/ui/templates/global_config.html b/src/ui/templates/global_config.html
index a2d11980e..1c746f9c6 100644
--- a/src/ui/templates/global_config.html
+++ b/src/ui/templates/global_config.html
@@ -8,7 +8,7 @@ config["CONFIG"].get_config() %}
global-config-form
id="form-edit-global-configs"
method="POST"
- class="flex flex-col justify-between min-h-50-screen dark:brightness-110 col-span-12 break-words bg-white shadow-xl p-4 dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
+ class="flex flex-col justify-between overflow-hidden overflow-y-auto max-h-135 md:max-h-160 dark:brightness-110 col-span-12 break-words bg-white shadow-xl p-4 dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
>
diff --git a/src/ui/templates/head.html b/src/ui/templates/head.html
index 44ac21187..d6cf815f1 100644
--- a/src/ui/templates/head.html
+++ b/src/ui/templates/head.html
@@ -34,14 +34,24 @@
{% elif current_endpoint == "services" %}
{% elif current_endpoint == "plugins" %}
-
-
+
+
{% elif current_endpoint == "cache" %}
{% elif current_endpoint == "logs" %}
-
+
+
+
{% endif %}
diff --git a/src/ui/templates/home.html b/src/ui/templates/home.html
index c93a6757f..a0f030b58 100644
--- a/src/ui/templates/home.html
+++ b/src/ui/templates/home.html
@@ -66,7 +66,7 @@
{{ instances_number }}
1 / {{ instances_number }}{{instance_health_count}} / {{ instances_number }}
is working
@@ -104,10 +104,18 @@
{{ services_number }}
- last created is
service-1{{services_ui_count}}
+ ui,
+ {{services_scheduler_count}}
+ scheduler,
+ {{services_autoconf_count}}
+ autoconf
@@ -145,7 +153,7 @@
1
0{{plugins_errors}}
error
diff --git a/src/ui/templates/jobs.html b/src/ui/templates/jobs.html
index d49fa6bd2..21095dfd2 100644
--- a/src/ui/templates/jobs.html
+++ b/src/ui/templates/jobs.html
@@ -1,60 +1,58 @@
{% extends "base.html" %} {% block content %} {% set current_endpoint =
url_for(request.endpoint)[1:].split("/")[-1].strip() %}
+
+
+
INFO
+
+
+ JOBS TOTAL
+
+
+ {{jobs_total}}
+
+
+
+
+ JOBS ERROS
+
+
+ {{jobs_errors}}
+
+
+
+
+ AUTO UPDATE
+
+
+ 2 mins
+
+
+
+
+
FILTER
-
-
-
-
-
-
- Update delay (in seconds)
-
-
-
-
-
+
@@ -64,34 +62,34 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
type="text"
id="keyword"
name="keyword"
- class="col-span-12 sm:col-span-6 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
+ class="dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
placeholder="key words"
pattern="(.*?)"
required
/>
-
-
+
+
- Select types
+ Sort by
+
+
+
+
+ Select success
+
+
+
+
+
+
-
+
+
+
+
+ Select reload
+
+
+ all
+
+
+
+
+
+
+
+ all
+
+
+ false
+
+
+ true
+
+
+
+
+
-
LOGS
+
{{current_endpoint}}
-
+
-
- {% for job in jobs %}
+