diff --git a/.github/workflows/black_check.yml b/.github/workflows/black_check.yml new file mode 100644 index 00000000..542da966 --- /dev/null +++ b/.github/workflows/black_check.yml @@ -0,0 +1,26 @@ +name: Black + +on: + push: + branches: [ main, release-1.0.0] + pull_request: + branches: [ main, release-1.0.0] + +jobs: + + check-black: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.11 + - name: Install Black 24.10.0 - check setup.py if version matches + run: | + python -m pip install --upgrade pip + pip install black==24.10.0 + - name: Run Black + run: | + black --check . diff --git a/.github/workflows/py311_tests.yml b/.github/workflows/py311_tests.yml new file mode 100644 index 00000000..7e186b22 --- /dev/null +++ b/.github/workflows/py311_tests.yml @@ -0,0 +1,26 @@ +name: Pytest + +on: + push: + branches: [ main, release-1.0.0] + pull_request: + branches: [ main, release-1.0.0] + +jobs: + + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.11 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e .[test] + - name: Run tests + run: | + pytest diff --git a/.gitignore b/.gitignore index 4e583a04..2b3546e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ __pycache__/ *.py[cod] -data *.egg-info export/datasets tmp @@ -12,15 +11,15 @@ env/ .venv .vscode build +dist venv/ wiki -factgenie/annotations -factgenie/generations -factgenie/outputs +factgenie/campaigns factgenie/templates/campaigns/* -!factgenie/templates/campaigns/*.* +factgenie/data/datasets.yml +factgenie/data/inputs +factgenie/data/outputs factgenie/config/config.yml -factgenie/config/datasets.yml factgenie/config/llm-eval factgenie/config/llm-gen factgenie/config/crowdsourcing \ No newline at end of file diff --git a/CONTRIBUTING b/CONTRIBUTING index 81983a51..55668978 100644 --- a/CONTRIBUTING +++ b/CONTRIBUTING @@ -2,4 +2,4 @@ Thank you for considering contributing to **factgenie**! -Please, see the 🌱 [Contributing](../../wiki/07-Contributing) page on our wiki for details. +Please, see the 🌱 [Contributing](../../wiki/Contributing) page on our wiki for details. diff --git a/Dockerfile b/Dockerfile index f5fa9524..252c4d8c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,8 +4,9 @@ RUN mkdir -p /usr/src/factgenie WORKDIR /usr/src/factgenie COPY . /usr/src/factgenie +RUN cp /usr/src/factgenie/factgenie/config/config_TEMPLATE.yml /usr/src/factgenie/factgenie/config/config.yml RUN pip install -e .[deploy] EXPOSE 80 -ENTRYPOINT ["gunicorn", "--env", "SCRIPT_NAME=", "-b", ":80", "-w", "1", "--threads", "2", "factgenie.cli:create_app()"] +ENTRYPOINT ["gunicorn", "--env", "SCRIPT_NAME=", "-b", ":80", "-w", "1", "--threads", "8", "factgenie.bin.run:create_app()"] \ No newline at end of file diff --git a/README.md b/README.md index d4f4ba2b..0c6998dc 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,12 @@

factgenie

-![GitHub](https://img.shields.io/github/license/kasnerz/factgenie) -![GitHub issues](https://img.shields.io/github/issues/kasnerz/factgenie) -[![arXiv](https://img.shields.io/badge/arXiv-2407.17863-0175ac.svg)](https://arxiv.org/abs/2407.17863) +![Github downloads](https://img.shields.io/github/downloads/kasnerz/factgenie/total) +![PyPI](https://img.shields.io/pypi/v/factgenie) +[![slack](https://img.shields.io/badge/slack-factgenie-0476ad.svg?logo=slack)](https://join.slack.com/t/factgenie/shared_invite/zt-2u180yy81-3zCR7mt8EOy55cxA5zhKyQ) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) ![Github stars](https://img.shields.io/github/stars/kasnerz/factgenie?style=social) - + Annotate LLM outputs with a lightweight, self-hosted web application 🌈 @@ -17,14 +17,8 @@ Annotate LLM outputs with a lightweight, self-hosted web application 🌈 -## 📢 News -- **25/10/2024** — We are preparing the first official release. Stay tuned! -- **08/10/2024** — We added [step-by-step walkthrougs](../../wiki/00-Tutorials) on using factgenie for generating and annotating outputs for a dataset of basketball reports 🏀 -- **07/10/2024** — We removed the example datasets from the repository. Instead, you can find them in the _External Resources_ section in the _Manage data_ interface. -- **24/09/2024** — We introduced a brand new factgenie logo! -- **19/09/2024** — On the Analytics page, you can now see detailed statistics about annotations and compute inter-annotator agreement 📈 -- **16/09/2024** — You can now collect extra inputs from the annotators for each example using sliders and select boxes. -- **16/09/2024** — We added an option to generate outputs for the inputs with LLMs directly within factgenie! 🦾 +## 📢 Changelog +- **[1.0.0] - 2024-11-13**: The first official release 🎉 ## 👉️ How can factgenie help you? Outputs from large language models (LLMs) may contain errors: semantic, factual, and lexical. @@ -42,39 +36,53 @@ Factgenie can provide you: *What does factgenie **not help with** is collecting the data (we assume that you already have these), starting the crowdsourcing campaign (for that, you need to use a service such as [Prolific.com](https://prolific.com)) or running the LLM evaluators (for that, you need a local framework such as [Ollama](https://ollama.com) or a proprietary API).* ## 🏃 Quickstart -Make sure you have Python 3 installed (the project is tested with Python 3.10). +Make sure you have Python >=3.9 installed. -After cloning the repository, the following commands install the package and start the web server: +If you want to quickly try out factgenie, you can install the package from PyPI: +```bash +pip install factgenie ``` + +However, the recommended approach for using factgenie is using an editable package: +```bash +git clone https://github.com/ufal/factgenie.git +cd factgenie pip install -e .[dev,deploy] -factgenie run --host=127.0.0.1 --port 5000 ``` +This approach will allow you to manually modify configuration files, write your own data classes and access generated files. + +After installing factgenie, use the following command to run the server on your local computer: +```bash +factgenie run --host=127.0.0.1 --port 8890 +``` +More information on how to set up factgenie is on [Github wiki](../../wiki/Setup). ## 💡 Usage guide See the following **wiki pages** that that will guide you through various use-cases of factgenie: -| Topic | Description | -| ---------------------------------------------------------------------- | -------------------------------------------------- | -| 🔧 [Setup](../../wiki/01-Setup) | How to install factgenie. | -| 🗂️ [Data Management](../../wiki/02-Data-Management) | How to manage datasets and model outputs. | -| 🤖 [LLM Annotations](../../wiki/03-LLM-Annotations) | How to annotate outputs using LLMs. | -| 👥 [Crowdsourcing Annotations](../../wiki/04-Crowdsourcing-Annotations) | How to annotate outputs using human crowdworkers. | -| ✍️ [Generating Outputs](../../wiki/05-Generating-Outputs) | How to generate outputs using LLMs. | -| 📊 [Analyzing Annotations](../../wiki/06-Analyzing-Annotations) | How to obtain statistics on collected annotations. | -| 🌱 [Contributing](../../wiki/07-Contributing) | How to contribute to factgenie. | +| Topic | Description | +| ------------------------------------------------------------------- | -------------------------------------------------- | +| 🔧 [Setup](../../wiki/Setup) | How to install factgenie. | +| 🗂️ [Data Management](../../wiki/Data-Management) | How to manage datasets and model outputs. | +| 🤖 [LLM Annotations](../../wiki/LLM-Annotations) | How to annotate outputs using LLMs. | +| 👥 [Crowdsourcing Annotations](../../wiki/Crowdsourcing-Annotations) | How to annotate outputs using human crowdworkers. | +| ✍️ [Generating Outputs](../../wiki/Generating-Outputs) | How to generate outputs using LLMs. | +| 📊 [Analyzing Annotations](../../wiki/Analyzing-Annotations) | How to obtain statistics on collected annotations. | +| 💻 [Command Line Interface](../../wiki/CLI) | How to use factgenie command line interface. | +| 🌱 [Contributing](../../wiki/Contributing) | How to contribute to factgenie. | ## 🔥 Tutorials We also provide step-by-step walkthroughs showing how to employ factgenie on the [the dataset from the Shared Task in Evaluating Semantic Accuracy](https://github.com/ehudreiter/accuracySharedTask): -| Tutorial | Description | -| ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ | -| [🏀 #1: Importing a custom dataset](../../wiki/00-Tutorials#-tutorial-1-importing-a-custom-dataset) | Loading the basketball statistics and model-generated basketball reports into the web interface. | -| [💬 #2: Generating outputs](../../wiki/00-Tutorials#-tutorial-2-generating-outputs) | Using Llama 3.1 with Ollama for generating basketball reports. | -| [📊 #3: Customizing data visualization](../../wiki/00-Tutorials#-tutorial-3-customizing-data-visualization) | Manually creating a custom dataset class for better data visualization. | -| [🤖 #4: Annotating outputs with an LLM](../../wiki/00-Tutorials#-tutorial-4-annotating-outputs-with-an-llm) | Using GPT-4o for annotating errors in the basketball reports. | -| [👨‍💼 #5: Annotating outputs with human annotators](../../wiki/00-Tutorials#-tutorial-5-annotating-outputs-with-human-annotators) | Using human annotators for annotating errors in the basketball reports. | +| Tutorial | Description | +| --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | +| [🏀 #1: Importing a custom dataset](../../wiki/Tutorials#-tutorial-1-importing-a-custom-dataset) | Loading the basketball statistics and model-generated basketball reports into the web interface. | +| [💬 #2: Generating outputs](../../wiki/Tutorials#-tutorial-2-generating-outputs) | Using Llama 3.1 with Ollama for generating basketball reports. | +| [📊 #3: Customizing data visualization](../../wiki/Tutorials#-tutorial-3-customizing-data-visualization) | Manually creating a custom dataset class for better data visualization. | +| [🤖 #4: Annotating outputs with an LLM](../../wiki/Tutorials#-tutorial-4-annotating-outputs-with-an-llm) | Using GPT-4o for annotating errors in the basketball reports. | +| [👨‍💼 #5: Annotating outputs with human annotators](../../wiki/Tutorials#-tutorial-5-annotating-outputs-with-human-annotators) | Using human annotators for annotating errors in the basketball reports. | ## 💬 Cite us diff --git a/docker-compose.yml b/docker-compose.yml index 8cbb9635..dbe25558 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,29 @@ +# YOU NEED run once `curl http://localhost:11434/api/pull -d '{"name": "llama3.1:8b"}'` +# after running `docker-compose up -d` from the repo root directory +# in order to download the llama3.1:8b model which is the default model +# we use in the example configurations for factgenie services: factgenie: container_name: factgenie image: factgenie restart: on-failure ports: - - 8080:80 - build: ./factgenie + - 8890:80 + build: ./ + + # Factgenie connects to LLM inference servers either OpenAI client or Ollama + # Demonstrates running ollama on CPU + # For GPU run ollama without Docker + # or look at https://hub.docker.com/r/ollama/ollama and follow the GPU instructions + ollama: + container_name: ollama + image: ollama/ollama + restart: on-failure + # We need to expose the port to your machine because you need to pull models for ollama + # before factgenie queries the ollama server to run inference for the model. + # E.g. curl http://localhost:11434/api/pull -d '{"name": "llama3.1:8b"}' to download the factgenie default LLM. + ports: + - 11434:11434 + + + diff --git a/factgenie/__init__.py b/factgenie/__init__.py index e2ac06b8..606511ae 100644 --- a/factgenie/__init__.py +++ b/factgenie/__init__.py @@ -2,29 +2,21 @@ PACKAGE_DIR = Path(__file__).parent ROOT_DIR = PACKAGE_DIR.parent + TEMPLATES_DIR = PACKAGE_DIR / "templates" STATIC_DIR = PACKAGE_DIR / "static" -ANNOTATIONS_DIR = PACKAGE_DIR / "annotations" -GENERATIONS_DIR = PACKAGE_DIR / "generations" +CAMPAIGN_DIR = PACKAGE_DIR / "campaigns" LLM_EVAL_CONFIG_DIR = PACKAGE_DIR / "config" / "llm-eval" LLM_GEN_CONFIG_DIR = PACKAGE_DIR / "config" / "llm-gen" CROWDSOURCING_CONFIG_DIR = PACKAGE_DIR / "config" / "crowdsourcing" -DATA_DIR = PACKAGE_DIR / "data" -OUTPUT_DIR = PACKAGE_DIR / "outputs" +INPUT_DIR = PACKAGE_DIR / "data" / "inputs" +OUTPUT_DIR = PACKAGE_DIR / "data" / "outputs" +DATASET_CONFIG_PATH = PACKAGE_DIR / "data" / "datasets.yml" RESOURCES_CONFIG_PATH = PACKAGE_DIR / "config" / "resources.yml" -DATASET_CONFIG_PATH = PACKAGE_DIR / "config" / "datasets.yml" - -OLD_DATASET_CONFIG_PATH = PACKAGE_DIR / "loaders" / "datasets.yml" -OLD_MAIN_CONFIG_PATH = PACKAGE_DIR / "config.yml" MAIN_CONFIG_PATH = PACKAGE_DIR / "config" / "config.yml" -if not MAIN_CONFIG_PATH.exists() and not OLD_MAIN_CONFIG_PATH.exists(): - raise ValueError( - f"Invalid path to config.yml {MAIN_CONFIG_PATH=}. " - "Please copy config_TEMPLATE.yml to config.yml " - "and change the password, update the host prefix, etc." - ) - +MAIN_CONFIG_TEMPLATE_PATH = PACKAGE_DIR / "config" / "config_TEMPLATE.yml" +DEFAULT_PROMPTS_CONFIG_PATH = PACKAGE_DIR / "config" / "default_prompts.yml" PREVIEW_STUDY_ID = "factgenie_preview" diff --git a/factgenie/analysis.py b/factgenie/analysis.py index c483061a..5ca7f5dd 100644 --- a/factgenie/analysis.py +++ b/factgenie/analysis.py @@ -11,11 +11,12 @@ from scipy.stats import pearsonr import sys from pathlib import Path +from slugify import slugify import logging import coloredlogs -import factgenie.utils as utils +import factgenie.workflows as workflows -from factgenie.campaigns import ANNOTATIONS_DIR +from factgenie import CAMPAIGN_DIR sys.path.append(os.path.join(os.path.dirname(__file__), "..")) @@ -23,42 +24,11 @@ # coloredlogs.install(level="INFO", logger=logger, fmt="%(asctime)s %(levelname)s %(message)s") -def get_example_info(j, campaign_id): - return { - "annotator_id": j["annotator_id"], - "annotator_group": j.get("annotator_group", 0), - "campaign_id": campaign_id, - "dataset": j["dataset"], - "example_idx": j["example_idx"], - "setup_id": j["setup_id"], - "split": j["split"], - "flags": j.get("flags", []), - "options": j.get("options", []), - "text_fields": j.get("text_fields", []), - } - - -def load_annotations(line, campaign_id): - j = json.loads(line) - annotation_records = [] - - r = get_example_info(j, campaign_id) - - for annotation in j["annotations"]: - r["annotation_type"] = int(annotation["type"]) - r["annotation_start"] = annotation["start"] - r["annotation_text"] = annotation["text"] - - annotation_records.append(r.copy()) - - return annotation_records - - -def create_example_record(line, campaign_id, annotation_span_categories, annotation_records): +def create_example_record(line, metadata, annotation_span_categories, annotation_records): # a record is created even if there are no annotations j = json.loads(line) - example_record = get_example_info(j, campaign_id) + example_record = workflows.create_annotation_example_record(j) for i, category in enumerate(annotation_span_categories): example_record["cat_" + str(i)] = 0 @@ -83,21 +53,20 @@ def load_annotations_for_campaign(campaign): annotation_index = [] example_index = [] - campaign_id = campaign.metadata["id"] annotation_span_categories = campaign.metadata["config"]["annotation_span_categories"] - jsonl_files = glob.glob(os.path.join(ANNOTATIONS_DIR, campaign_id, "files", "*.jsonl")) + jsonl_files = glob.glob(os.path.join(CAMPAIGN_DIR, campaign.metadata["id"], "files", "*.jsonl")) for jsonl_file in jsonl_files: with open(jsonl_file) as f: lines = f.readlines() for line in lines: try: - annotation_records = load_annotations(line, campaign_id) + annotation_records = workflows.load_annotations_from_record(line, split_spans=True) annotation_index += annotation_records example_record = create_example_record( - line, campaign_id, annotation_span_categories, annotation_records + line, campaign.metadata, annotation_span_categories, annotation_records ) example_index.append(example_record) except Exception as e: @@ -172,6 +141,7 @@ def compute_avg_ann_counts(ann_counts, example_index): dataset = row["dataset"] split = row["split"] setup_id = row["setup_id"] + ann_counts.loc[i, "example_count"] = ( example_index[ (example_index["dataset"] == dataset) @@ -205,7 +175,10 @@ def compute_prevalence(ann_counts, example_index): & (example_index["cat_" + str(annotation_type)] > 0) ] - ann_counts.loc[i, "prevalence"] = examples.shape[0] / row["example_count"] + if row["example_count"] == 0: + ann_counts.loc[i, "prevalence"] = 0 + else: + ann_counts.loc[i, "prevalence"] = examples.shape[0] / row["example_count"] # round to three decimal places ann_counts["prevalence"] = ann_counts["prevalence"].round(3) @@ -276,7 +249,7 @@ def compute_extra_fields_stats(example_index): return extra_fields_stats -def compute_statistics(app, campaign, datasets): +def compute_statistics(app, campaign): statistics = {} annotation_index, example_index = load_annotations_for_campaign(campaign) @@ -308,6 +281,11 @@ def compute_pearson_macro_average(counts, first_ann_idx, second_ann_idx): for c, cat_counts in counts.items(): r, _ = pearsonr(cat_counts[first_ann_idx], cat_counts[second_ann_idx]) + + # if r is nan, set it to 0 + if not r == r: + r = 0 + coefficients.append(r) return round(sum(coefficients) / len(coefficients), 2), [round(coeff, 2) for coeff in coefficients] @@ -323,7 +301,9 @@ def compute_pearson_micro_average(counts, first_ann_idx, second_ann_idx): return round(r, 2) -def compute_pearson_correlation(dataset_level_counts, example_level_counts, annotator_count, annotator_group_ids): +def compute_pearson_correlation( + dataset_level_counts, example_level_counts, annotator_count, annotator_group_ids, compute_dataset_level_corr +): results = [] for a in range(annotator_count): @@ -331,14 +311,22 @@ def compute_pearson_correlation(dataset_level_counts, example_level_counts, anno a_group_id = annotator_group_ids[a] b_group_id = annotator_group_ids[b] - r_data_macro, r_data_list = compute_pearson_macro_average( - dataset_level_counts, first_ann_idx=a, second_ann_idx=b - ) + if compute_dataset_level_corr: + r_data_macro, r_data_list = compute_pearson_macro_average( + dataset_level_counts, first_ann_idx=a, second_ann_idx=b + ) + else: + r_data_macro, r_data_list = None, None + r_example_macro, r_example_list = compute_pearson_macro_average( example_level_counts, first_ann_idx=a, second_ann_idx=b ) - r_data_micro = compute_pearson_micro_average(dataset_level_counts, first_ann_idx=a, second_ann_idx=b) + if compute_dataset_level_corr: + r_data_micro = compute_pearson_micro_average(dataset_level_counts, first_ann_idx=a, second_ann_idx=b) + else: + r_data_micro = None + r_example_micro = compute_pearson_micro_average(example_level_counts, first_ann_idx=a, second_ann_idx=b) results.append( @@ -432,7 +420,7 @@ def prepare_example_index(combinations, selected_campaigns, campaigns): return example_index, annotator_count, annotator_group_ids, cat_columns -def compute_inter_annotator_agreement(app, selected_campaigns, combinations, campaigns, datasets): +def compute_inter_annotator_agreement(app, selected_campaigns, combinations, campaigns): combinations = [(c["dataset"], c["split"], c["setup_id"]) for c in combinations] example_index, annotator_count, annotator_group_ids, cat_columns = prepare_example_index( @@ -442,12 +430,13 @@ def compute_inter_annotator_agreement(app, selected_campaigns, combinations, cam dataset_level_counts, example_level_counts = compute_span_counts( example_index=example_index, annotator_count=annotator_count, combinations=combinations, cat_columns=cat_columns ) - + compute_dataset_level_corr = len(combinations) > 1 results = compute_pearson_correlation( dataset_level_counts=dataset_level_counts, example_level_counts=example_level_counts, annotator_count=annotator_count, annotator_group_ids=annotator_group_ids, + compute_dataset_level_corr=compute_dataset_level_corr, ) return results diff --git a/factgenie/main.py b/factgenie/app.py old mode 100755 new mode 100644 similarity index 54% rename from factgenie/main.py rename to factgenie/app.py index fdee9fc9..f324ef3f --- a/factgenie/main.py +++ b/factgenie/app.py @@ -1,15 +1,14 @@ #!/usr/bin/env python3 import os import json -import time import logging -import pandas as pd import time import threading import traceback import shutil import datetime -import markdown +import urllib.parse + from flask import ( Flask, render_template, @@ -18,32 +17,28 @@ Response, make_response, redirect, - url_for, send_from_directory, ) -from collections import defaultdict -import urllib.parse from slugify import slugify -from factgenie import PREVIEW_STUDY_ID -from factgenie.campaigns import HumanCampaign, CampaignStatus, ExampleStatus, ANNOTATIONS_DIR, GENERATIONS_DIR -from factgenie.models import ModelFactory -from factgenie.loaders.dataset import get_dataset_classes -import factgenie.utils as utils +import factgenie.crowdsourcing as crowdsourcing +import factgenie.llm_campaign as llm_campaign +import factgenie.workflows as workflows import factgenie.analysis as analysis +import factgenie.utils as utils -from werkzeug.middleware.proxy_fix import ProxyFix - -DIR_PATH = os.path.dirname(__file__) -TEMPLATES_DIR = os.path.join(DIR_PATH, "templates") -STATIC_DIR = os.path.join(DIR_PATH, "static") +from factgenie import CAMPAIGN_DIR, TEMPLATES_DIR, STATIC_DIR, INPUT_DIR +from factgenie.campaign import CampaignMode, CampaignStatus, ExampleStatus +from factgenie.models import ModelFactory +from werkzeug.middleware.proxy_fix import ProxyFix app = Flask("factgenie", template_folder=TEMPLATES_DIR, static_folder=STATIC_DIR) app.db = {} -app.db["annotation_index"] = {} +app.db["annotation_index"] = None +app.db["output_index"] = None app.db["lock"] = threading.Lock() -app.db["threads"] = {} +app.db["running_campaigns"] = set() app.db["announcers"] = {} app.wsgi_app = ProxyFix(app.wsgi_app, x_host=1) @@ -98,8 +93,6 @@ def prettify_json(value): # ----------------- # Decorators # ----------------- - - # Very simple decorator to protect routes def login_required(f): def wrapper(*args, **kwargs): @@ -124,20 +117,8 @@ def wrapper(*args, **kwargs): @login_required def index(): logger.info(f"Main page loaded") - - return render_template( - "index.html", - host_prefix=app.config["host_prefix"], - ) - - -@app.route("/about", methods=["GET", "POST"]) -@login_required -def about(): - logger.info(f"About page loaded") - return render_template( - "about.html", + "pages/index.html", host_prefix=app.config["host_prefix"], ) @@ -145,12 +126,12 @@ def about(): @app.route("/analyze", methods=["GET", "POST"]) @login_required def analyze(): - logger.info(f"Analysis page loaded") - - campaigns = utils.get_sorted_campaign_list(app, sources=["crowdsourcing", "llm_eval"]) + campaigns = workflows.get_sorted_campaign_list( + app, modes=[CampaignMode.CROWDSOURCING, CampaignMode.LLM_EVAL, CampaignMode.EXTERNAL] + ) return render_template( - "analyze.html", + "pages/analyze.html", campaigns=campaigns, host_prefix=app.config["host_prefix"], ) @@ -159,41 +140,37 @@ def analyze(): @app.route("/analyze/detail/", methods=["GET", "POST"]) @login_required def analyze_detail(campaign_id): - source = request.args.get("source") - - campaign = utils.load_campaign(app, campaign_id=campaign_id, mode=source) - datasets = utils.get_local_dataset_overview(app) + campaign = workflows.load_campaign(app, campaign_id=campaign_id) - statistics = analysis.compute_statistics(app, campaign, datasets) + statistics = analysis.compute_statistics(app, campaign) return render_template( - "analyze_detail.html", + "pages/analyze_detail.html", statistics=statistics, campaign=campaign, - source=source, host_prefix=app.config["host_prefix"], ) @app.route("/annotate/", methods=["GET", "POST"]) def annotate(campaign_id): - logger.info(f"Annotate page loaded") + workflows.refresh_indexes(app) - campaign = utils.load_campaign(app, campaign_id=campaign_id, mode="crowdsourcing") + # only for preview purposes, batch index is otherwise randomly generated + batch_idx = request.args.get("batch_idx", None) + campaign = workflows.load_campaign(app, campaign_id=campaign_id) service = campaign.metadata["config"]["service"] - service_ids = utils.get_service_ids(service, request.args) + service_ids = crowdsourcing.get_service_ids(service, request.args) - db = campaign.db metadata = campaign.metadata - annotation_set = utils.get_annotator_batch(app, campaign, db, service_ids) + annotation_set = crowdsourcing.get_annotator_batch(app, campaign, service_ids, batch_idx=batch_idx) if not annotation_set: # no more available examples return render_template( - "campaigns/closed.html", + "crowdsourcing/closed.html", host_prefix=app.config["host_prefix"], - metadata=metadata, ) return render_template( @@ -205,14 +182,23 @@ def annotate(campaign_id): ) +@app.route("/app_config", methods=["GET"]) +@login_required +def app_config(): + return render_template( + "pages/app_config.html", + app_config=app.config, + host_prefix=app.config["host_prefix"], + ) + + @app.route("/browse", methods=["GET", "POST"]) @login_required def browse(): - utils.generate_annotation_index(app) - dataset_id = request.args.get("dataset") split = request.args.get("split") example_idx = request.args.get("example_idx") + setup_id = request.args.get("setup_id") if dataset_id and split and example_idx: display_example = {"dataset": dataset_id, "split": split, "example_idx": int(example_idx)} @@ -220,32 +206,31 @@ def browse(): else: display_example = None - datasets = utils.get_local_dataset_overview(app) + workflows.refresh_indexes(app) + datasets = workflows.get_local_dataset_overview(app) datasets = {k: v for k, v in datasets.items() if v["enabled"]} if not datasets: return render_template( - "no_datasets.html", + "pages/no_datasets.html", host_prefix=app.config["host_prefix"], ) - return render_template( - "browse.html", + "pages/browse.html", display_example=display_example, + highlight_setup_id=setup_id, datasets=datasets, host_prefix=app.config["host_prefix"], - annotations=app.db["annotation_index"], ) -@app.route("/clear_campaign", methods=["GET", "POST"]) +@app.route("/clear_campaign", methods=["POST"]) @login_required def clear_campaign(): data = request.get_json() campaign_id = data.get("campaignId") - mode = data.get("mode") - campaign = utils.load_campaign(app, campaign_id=campaign_id, mode=mode) + campaign = workflows.load_campaign(app, campaign_id=campaign_id) campaign.clear_all_outputs() return utils.success() @@ -256,35 +241,24 @@ def clear_campaign(): def clear_output(): data = request.get_json() campaign_id = data.get("campaignId") - mode = data.get("mode") idx = int(data.get("idx")) + annotator_group = int(data.get("annotatorGroup")) - campaign = utils.load_campaign(app, campaign_id=campaign_id, mode=mode) - campaign.clear_output(idx) + campaign = workflows.load_campaign(app, campaign_id=campaign_id) + campaign.clear_output(idx, annotator_group) return utils.success() @app.route("/crowdsourcing", methods=["GET", "POST"]) @login_required -def crowdsourcing(): - logger.info(f"Crowdsourcing page loaded") - - campaign_index = utils.generate_campaign_index(app, force_reload=True) - - llm_configs = utils.load_configs(mode="llm_eval") - crowdsourcing_configs = utils.load_configs(mode="crowdsourcing") - - campaigns = defaultdict(dict) - - for campaign_id, campaign in sorted( - campaign_index["crowdsourcing"].items(), key=lambda x: x[1].metadata["created"], reverse=True - ): - campaigns[campaign_id]["metadata"] = campaign.metadata - campaigns[campaign_id]["stats"] = campaign.get_stats() +def crowdsourcing_page(): + llm_configs = workflows.load_configs(mode=CampaignMode.LLM_EVAL) + crowdsourcing_configs = workflows.load_configs(mode=CampaignMode.CROWDSOURCING) + campaigns = workflows.get_sorted_campaign_list(app, modes=[CampaignMode.CROWDSOURCING]) return render_template( - "crowdsourcing.html", + "pages/crowdsourcing.html", campaigns=campaigns, llm_configs=llm_configs, crowdsourcing_configs=crowdsourcing_configs, @@ -296,14 +270,13 @@ def crowdsourcing(): @app.route("/crowdsourcing/detail/", methods=["GET", "POST"]) @login_required def crowdsourcing_detail(campaign_id): - campaign = utils.load_campaign(app, campaign_id=campaign_id, mode="crowdsourcing") - + campaign = workflows.load_campaign(app, campaign_id=campaign_id) overview = campaign.get_overview() stats = campaign.get_stats() return render_template( - "crowdsourcing_detail.html", - mode="crowdsourcing", + "pages/crowdsourcing_detail.html", + mode=CampaignMode.CROWDSOURCING, campaign_id=campaign_id, overview=overview, stats=stats, @@ -321,34 +294,14 @@ def crowdsourcing_create(): campaign_data = data.get("campaignData") config = data.get("config") - config = utils.parse_crowdsourcing_config(config) - - # create a new directory - if os.path.exists(os.path.join(ANNOTATIONS_DIR, campaign_id)): - return jsonify({"error": "Campaign already exists"}) - - os.makedirs(os.path.join(ANNOTATIONS_DIR, campaign_id, "files"), exist_ok=True) - - # create the annotation CSV - db = utils.generate_campaign_db(app, campaign_data, config=config) - db.to_csv(os.path.join(ANNOTATIONS_DIR, campaign_id, "db.csv"), index=False) - - # save metadata - with open(os.path.join(ANNOTATIONS_DIR, campaign_id, "metadata.json"), "w") as f: - json.dump( - { - "id": campaign_id, - "source": "crowdsourcing", - "config": config, - "created": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), - }, - f, - indent=4, - ) + config = crowdsourcing.parse_crowdsourcing_config(config) - # prepare the crowdsourcing HTML page - utils.create_crowdsourcing_page(campaign_id, config) - utils.load_campaign(app, campaign_id=campaign_id, mode="crowdsourcing") + try: + crowdsourcing.create_crowdsourcing_campaign(app, campaign_id, config, campaign_data) + workflows.load_campaign(app, campaign_id=campaign_id) + except Exception as e: + traceback.print_exc() + return utils.error(f"Error while creating campaign: {e}") return utils.success() @@ -356,21 +309,21 @@ def crowdsourcing_create(): @app.route("/crowdsourcing/new", methods=["GET", "POST"]) @login_required def crowdsourcing_new(): - datasets = utils.get_local_dataset_overview(app) + datasets = workflows.get_local_dataset_overview(app) datasets = {k: v for k, v in datasets.items() if v["enabled"]} - model_outs = utils.get_model_outputs_overview(app, datasets, non_empty=True) - - configs = utils.load_configs(mode="crowdsourcing") + available_data = workflows.get_model_outputs_overview(app, datasets) + configs = workflows.load_configs(mode=CampaignMode.CROWDSOURCING) + default_prompts = utils.load_default_prompts() - campaign_index = utils.generate_campaign_index(app, force_reload=False) - default_campaign_id = utils.generate_default_id(campaign_index=campaign_index["crowdsourcing"], prefix="campaign") + default_campaign_id = workflows.generate_default_id(app=app, mode=CampaignMode.CROWDSOURCING, prefix="campaign") return render_template( - "crowdsourcing_new.html", + "pages/crowdsourcing_new.html", default_campaign_id=default_campaign_id, + default_prompts=default_prompts, datasets=datasets, - model_outs=model_outs, + available_data=available_data, configs=configs, host_prefix=app.config["host_prefix"], ) @@ -383,42 +336,32 @@ def compute_agreement(): combinations = data.get("combinations") selected_campaigns = data.get("selectedCampaigns") - campaign_index = utils.generate_campaign_index(app, force_reload=True) - # flatten the campaigns - campaigns = {k: v for source in campaign_index.values() for k, v in source.items()} - - datasets = utils.get_local_dataset_overview(app) + campaign_index = workflows.generate_campaign_index(app, force_reload=True) try: results = analysis.compute_inter_annotator_agreement( app, selected_campaigns=selected_campaigns, combinations=combinations, - campaigns=campaigns, - datasets=datasets, + campaigns=campaign_index, ) return jsonify(results) except Exception as e: traceback.print_exc() - return jsonify({"error": f"Error while computing agreement: {e}"}) + return utils.error(f"Error while computing agreement: {e}") @app.route("/delete_campaign", methods=["POST"]) @login_required def delete_campaign(): data = request.get_json() - campaign_name = data.get("campaignId") - mode = data.get("mode") - - if mode == "llm_gen": - target_dir = GENERATIONS_DIR - else: - target_dir = ANNOTATIONS_DIR + campaign_id = data.get("campaignId") - shutil.rmtree(os.path.join(target_dir, campaign_name)) + shutil.rmtree(os.path.join(CAMPAIGN_DIR, campaign_id)) + symlink_dir = os.path.join(TEMPLATES_DIR, "campaigns", campaign_id) - if os.path.exists(os.path.join(TEMPLATES_DIR, "campaigns", campaign_name)): - shutil.rmtree(os.path.join(TEMPLATES_DIR, "campaigns", campaign_name)) + if os.path.exists(symlink_dir): + shutil.rmtree(symlink_dir) return utils.success() @@ -428,8 +371,7 @@ def delete_campaign(): def delete_dataset(): data = request.get_json() dataset_id = data.get("datasetId") - - utils.delete_dataset(app, dataset_id) + workflows.delete_dataset(app, dataset_id) return utils.success() @@ -445,7 +387,7 @@ def delete_model_outputs(): setup_id = data.get("setup_id") dataset = app.db["datasets_obj"][dataset_id] - utils.delete_model_outputs(dataset, split, setup_id) + workflows.delete_model_outputs(dataset, split, setup_id) return utils.success() @@ -457,10 +399,10 @@ def download_dataset(): dataset_id = data.get("datasetId") try: - utils.download_dataset(app, dataset_id) + workflows.download_dataset(app, dataset_id) except Exception as e: traceback.print_exc() - return jsonify({"error": f"Error while downloading dataset: {e}"}) + return jsonify({"error": f"Error while downloading dataset: {e.__class__.__name__}: {e}"}) return utils.success() @@ -473,14 +415,14 @@ def duplicate_config(): mode_to = data.get("modeTo") campaign_id = data.get("campaignId") - campaign_index = utils.generate_campaign_index(app, force_reload=False) + campaign_index = workflows.generate_campaign_index(app, force_reload=False) if mode_from == mode_to: - campaign = campaign_index[mode_from][campaign_id] + campaign = campaign_index[campaign_id] config = campaign.metadata["config"] else: # currently we only support copying the annotation_span_categories between modes - campaign = campaign_index[mode_from][campaign_id] + campaign = campaign_index[campaign_id] llm_config = campaign.metadata["config"] config = {"annotation_span_categories": llm_config["annotation_span_categories"]} @@ -496,7 +438,7 @@ def duplicate_eval(): campaign_id = data.get("campaignId") new_campaign_id = slugify(data.get("newCampaignId")) - ret = utils.duplicate_eval(app, mode, campaign_id, new_campaign_id) + ret = llm_campaign.duplicate_llm_campaign(app, mode, campaign_id, new_campaign_id) return ret @@ -505,48 +447,50 @@ def duplicate_eval(): def render_example(): dataset_id = request.args.get("dataset") split = request.args.get("split") - example_idx = int(request.args.get("example_idx")) + example_idx = max(int(request.args.get("example_idx")), 0) + setup_id = request.args.get("setup_id", None) try: - example_data = utils.get_example_data(app, dataset_id, split, example_idx) + example_data = workflows.get_example_data(app, dataset_id, split, example_idx, setup_id) + return jsonify(example_data) except Exception as e: traceback.print_exc() logger.error(f"Error while getting example data: {e}") logger.error(f"{dataset_id=}, {split=}, {example_idx=}") - return jsonify({"error": f"Error\n\t{e}\nwhile getting example data: {dataset_id=}, {split=}, {example_idx=}"}) + return utils.error( + f"Error\n\t{e.__class__.__name__}: {e}\nwhile getting example data: {dataset_id=}, {split=}, {example_idx=}" + ) @app.route("/export_campaign_outputs/", methods=["GET", "POST"]) @login_required def export_campaign_outputs(campaign_id): - mode = request.args.get("mode") + return workflows.export_campaign_outputs(campaign_id) - return utils.export_campaign_outputs(app, mode, campaign_id) - -@app.route("/export_dataset", methods=["GET", "POST"]) +@app.route("/export_dataset", methods=["POST", "GET"]) @login_required def export_dataset(): dataset_id = request.args.get("dataset_id") - return utils.export_dataset(app, dataset_id) + return workflows.export_dataset(app, dataset_id) -@app.route("/export_outputs", methods=["GET", "POST"]) +@app.route("/export_outputs", methods=["POST", "GET"]) @login_required def export_outputs(): dataset_id = request.args.get("dataset") split = request.args.get("split") setup_id = request.args.get("setup_id") - return utils.export_outputs(app, dataset_id, split, setup_id) + return workflows.export_outputs(app, dataset_id, split, setup_id) -@app.route("/files/", methods=["GET"]) +@app.route("/files/", methods=["GET", "POST"]) def download_file(filename): # serving external files for datasets - return send_from_directory("data", filename) + return send_from_directory(INPUT_DIR, filename) @app.route("/login", methods=["GET", "POST"]) @@ -561,32 +505,22 @@ def login(): return resp else: return "Login failed", 401 - return render_template("login.html", host_prefix=app.config["host_prefix"]) + return render_template("pages/login.html", host_prefix=app.config["host_prefix"]) -@app.route("/llm_campaign", methods=["GET", "POST"]) +@app.route("/llm_eval", methods=["GET", "POST"]) +@app.route("/llm_gen", methods=["GET", "POST"]) @login_required -def llm_campaign(): - logger.info(f"LLM campaign page loaded") - mode = request.args.get("mode") - - if not mode: - return "The `mode` argument was not specified", 404 - - campaign_index = utils.generate_campaign_index(app) - campaigns = defaultdict(dict) +def llm_campaign_page(): + mode = utils.get_mode_from_path(request.path) - llm_configs = utils.load_configs(mode=mode) - crowdsourcing_configs = utils.load_configs(mode="crowdsourcing") + campaigns = workflows.get_sorted_campaign_list(app, modes=[mode]) - for campaign_id, campaign in sorted( - campaign_index[mode].items(), key=lambda x: x[1].metadata["created"], reverse=True - ): - campaigns[campaign_id]["metadata"] = campaign.metadata - campaigns[campaign_id]["stats"] = campaign.get_stats() + llm_configs = workflows.load_configs(mode=mode) + crowdsourcing_configs = workflows.load_configs(mode=CampaignMode.CROWDSOURCING) return render_template( - f"llm_campaign.html", + f"pages/llm_campaign.html", mode=mode, llm_configs=llm_configs, crowdsourcing_configs=crowdsourcing_configs, @@ -595,30 +529,27 @@ def llm_campaign(): ) -@app.route("/llm_campaign/create", methods=["GET", "POST"]) +@app.route("/llm_eval/create", methods=["GET", "POST"]) +@app.route("/llm_gen/create", methods=["GET", "POST"]) @login_required def llm_campaign_create(): - mode = request.args.get("mode") - - if not mode: - return "The `mode` argument was not specified", 404 - + mode = utils.get_mode_from_path(request.path) data = request.get_json() campaign_id = slugify(data.get("campaignId")) campaign_data = data.get("campaignData") config = data.get("config") - if mode == "llm_eval": - config = utils.parse_llm_eval_config(config) - elif mode == "llm_gen": - config = utils.parse_llm_gen_config(config) + if mode == CampaignMode.LLM_EVAL: + config = llm_campaign.parse_llm_eval_config(config) + elif mode == CampaignMode.LLM_GEN: + config = llm_campaign.parse_llm_gen_config(config) datasets = app.db["datasets_obj"] try: - utils.llm_campaign_new(mode, campaign_id, config, campaign_data, datasets) - utils.load_campaign(app, campaign_id=campaign_id, mode=mode) + llm_campaign.create_llm_campaign(app, mode, campaign_id, config, campaign_data, datasets) + workflows.load_campaign(app, campaign_id=campaign_id) except Exception as e: traceback.print_exc() return utils.error(f"Error while creating campaign: {e}") @@ -626,25 +557,25 @@ def llm_campaign_create(): return utils.success() -@app.route("/llm_campaign/detail/", methods=["GET", "POST"]) +@app.route("/llm_eval/detail/", methods=["GET", "POST"]) +@app.route("/llm_gen/detail/", methods=["GET", "POST"]) @login_required def llm_campaign_detail(campaign_id): - mode = request.args.get("mode") - - if not mode: - return "The `mode` argument was not specified", 404 + workflows.refresh_indexes(app) - campaign = utils.load_campaign(app, campaign_id=campaign_id, mode=mode) + mode = utils.get_mode_from_path(request.path) + campaign = workflows.load_campaign(app, campaign_id=campaign_id) if campaign.metadata["status"] == CampaignStatus.RUNNING and not app.db["announcers"].get(campaign_id): campaign.metadata["status"] = CampaignStatus.IDLE campaign.update_metadata() overview = campaign.get_overview() + finished_examples = [x for x in overview if x["status"] == ExampleStatus.FINISHED] return render_template( - f"llm_campaign_detail.html", + f"pages/llm_campaign_detail.html", mode=mode, campaign_id=campaign_id, overview=overview, @@ -654,65 +585,69 @@ def llm_campaign_detail(campaign_id): ) -@app.route("/llm_campaign/new", methods=["GET", "POST"]) +@app.route("/llm_eval/new", methods=["GET", "POST"]) +@app.route("/llm_gen/new", methods=["GET", "POST"]) @login_required def llm_campaign_new(): - mode = request.args.get("mode") + mode = utils.get_mode_from_path(request.path) - if not mode: - return "The `mode` argument was not specified", 404 - - datasets = utils.get_local_dataset_overview(app) + datasets = workflows.get_local_dataset_overview(app) datasets = {k: v for k, v in datasets.items() if v["enabled"]} - non_empty = True if mode == "llm_eval" else False - model_outs = utils.get_model_outputs_overview(app, datasets, non_empty=non_empty) + if mode == CampaignMode.LLM_EVAL: + available_data = workflows.get_model_outputs_overview(app, datasets) + else: + available_data = workflows.get_available_data(app, datasets) # get a list of available metrics - llm_configs = utils.load_configs(mode=mode) + llm_configs = workflows.load_configs(mode=mode) metric_types = list(ModelFactory.model_classes()[mode].keys()) - campaign_index = utils.generate_campaign_index(app, force_reload=False) - default_campaign_id = utils.generate_default_id(campaign_index=campaign_index[mode], prefix=mode.replace("_", "-")) + default_campaign_id = workflows.generate_default_id(app, mode=mode, prefix=mode.replace("_", "-")) + default_prompts = utils.load_default_prompts() return render_template( - f"llm_campaign_new.html", + f"pages/llm_campaign_new.html", mode=mode, datasets=datasets, default_campaign_id=default_campaign_id, - model_outs=model_outs, + default_prompts=default_prompts, + available_data=available_data, configs=llm_configs, metric_types=metric_types, host_prefix=app.config["host_prefix"], ) -@app.route("/llm_campaign/run", methods=["POST"]) +@app.route("/llm_eval/run", methods=["POST"]) +@app.route("/llm_gen/run", methods=["POST"]) @login_required def llm_campaign_run(): - mode = request.args.get("mode") - - if not mode: - return "The `mode` argument was not specified", 404 - + mode = utils.get_mode_from_path(request.path) data = request.get_json() campaign_id = data.get("campaignId") app.db["announcers"][campaign_id] = announcer = utils.MessageAnnouncer() - - app.db["threads"][campaign_id] = { - "running": True, - } + app.db["running_campaigns"].add(campaign_id) try: - campaign = utils.load_campaign(app, campaign_id=campaign_id, mode=mode) - threads = app.db["threads"] + campaign = workflows.load_campaign(app, campaign_id=campaign_id) datasets = app.db["datasets_obj"] config = campaign.metadata["config"] model = ModelFactory.from_config(config, mode=mode) + running_campaigns = app.db["running_campaigns"] + + ret = llm_campaign.run_llm_campaign( + app, mode, campaign_id, announcer, campaign, datasets, model, running_campaigns + ) + + if hasattr(ret, "error"): + llm_campaign.pause_llm_campaign(app, campaign_id) + return utils.error(f"Error while running campaign: {ret.error}") + else: + return ret - return utils.run_llm_campaign(mode, campaign_id, announcer, campaign, datasets, model, threads) except Exception as e: traceback.print_exc() return utils.error(f"Error while running campaign: {e}") @@ -722,20 +657,19 @@ def llm_campaign_run(): @login_required def llm_campaign_update_config(): data = request.get_json() - mode = request.args.get("mode") campaign_id = data.get("campaignId") config = data.get("config") - config = utils.parse_campaign_config(config) - campaign = utils.load_campaign(app, campaign_id=campaign_id, mode=mode) + config = llm_campaign.parse_campaign_config(config) + campaign = workflows.load_campaign(app, campaign_id=campaign_id) campaign.metadata["config"] = config campaign.update_metadata() return utils.success() -@app.route("/llm_campaign/progress/", methods=["GET"]) +@app.route("/llm_campaign/progress/", methods=["GET", "POST"]) @login_required def listen(campaign_id): if not app.db["announcers"].get(campaign_id): @@ -753,60 +687,37 @@ def stream(): @app.route("/llm_campaign/pause", methods=["POST"]) @login_required def llm_campaign_pause(): - mode = request.args.get("mode") - - if not mode: - return "The `mode` argument was not specified", 404 - data = request.get_json() campaign_id = data.get("campaignId") - app.db["threads"][campaign_id]["running"] = False - campaign = utils.load_campaign(app, campaign_id=campaign_id, mode=mode) - campaign.metadata["status"] = CampaignStatus.IDLE - campaign.update_metadata() + llm_campaign.pause_llm_campaign(app, campaign_id) - resp = jsonify(success=True, status=campaign.metadata["status"]) + resp = jsonify(success=True, status=CampaignStatus.IDLE) return resp -@app.route("/llm_eval/detail/", methods=["GET", "POST"]) -@login_required -def llm_eval(campaign_id): - - # redirect to /llm_campaign with the mode set to llm_eval, keeping the campaign_id - return redirect(f"{app.config['host_prefix']}/llm_campaign/detail/{campaign_id}?mode=llm_eval") - - -@app.route("/llm_gen/detail/", methods=["GET", "POST"]) -@login_required -def llm_gen(campaign_id): - # redirect to /llm_campaign with the mode set to llm_gen, keeping the campaign_id - return redirect(f"{app.config['host_prefix']}/llm_campaign/detail/{campaign_id}?mode=llm_gen") - - @app.route("/manage", methods=["GET", "POST"]) @login_required def manage(): - datasets = utils.get_local_dataset_overview(app) - dataset_classes = list(get_dataset_classes().keys()) + datasets = workflows.get_local_dataset_overview(app) datasets_enabled = {k: v for k, v in datasets.items() if v["enabled"]} - model_outputs = utils.get_model_outputs_overview(app, datasets_enabled) + model_outputs = workflows.get_model_outputs_overview(app, datasets_enabled) - datasets_for_download = utils.get_datasets_for_download(app) + resources = utils.load_resources_config() # set as `downloaded` the datasets that are already downloaded - for dataset_id in datasets_for_download.keys(): - datasets_for_download[dataset_id]["downloaded"] = dataset_id in datasets + for dataset_id in resources.keys(): + resources[dataset_id]["downloaded"] = dataset_id in datasets - campaigns = utils.get_sorted_campaign_list(app, sources=["crowdsourcing", "llm_eval", "llm_gen", "external"]) + campaigns = workflows.get_sorted_campaign_list( + app, modes=[CampaignMode.CROWDSOURCING, CampaignMode.LLM_EVAL, CampaignMode.LLM_GEN, CampaignMode.EXTERNAL] + ) return render_template( - "manage.html", + "pages/manage.html", datasets=datasets, - dataset_classes=dataset_classes, - datasets_for_download=datasets_for_download, + resources=resources, host_prefix=app.config["host_prefix"], model_outputs=model_outputs, campaigns=campaigns, @@ -820,14 +731,14 @@ def save_config(): config = data.get("config") mode = data.get("mode") - if mode == "llm_eval": - config = utils.parse_llm_eval_config(config) - elif mode == "llm_gen": - config = utils.parse_llm_gen_config(config) - elif mode == "crowdsourcing": - config = utils.parse_crowdsourcing_config(config) + if mode == CampaignMode.LLM_EVAL: + config = llm_campaign.parse_llm_eval_config(config) + elif mode == CampaignMode.LLM_GEN: + config = llm_campaign.parse_llm_gen_config(config) + elif mode == CampaignMode.CROWDSOURCING: + config = crowdsourcing.parse_crowdsourcing_config(config) else: - return jsonify({"error": f"Invalid mode: {mode}"}) + return utils.error(f"Invalid mode: {mode}") utils.save_config(filename, config, mode=mode) @@ -841,55 +752,21 @@ def save_generation_outputs(): campaign_id = data.get("campaignId") model_name = slugify(data.get("modelName")) - utils.save_generation_outputs(app, campaign_id, model_name) + llm_campaign.save_generation_outputs(app, campaign_id, model_name) return utils.success() @app.route("/submit_annotations", methods=["POST"]) def submit_annotations(): - logger.info(f"Received annotations") data = request.get_json() campaign_id = data["campaign_id"] annotation_set = data["annotation_set"] annotator_id = data["annotator_id"] - now = int(time.time()) - - save_dir = os.path.join(ANNOTATIONS_DIR, campaign_id, "files") - os.makedirs(save_dir, exist_ok=True) - campaign = utils.load_campaign(app, campaign_id=campaign_id, mode="crowdsourcing") - - with app.db["lock"]: - db = campaign.db - batch_idx = annotation_set[0]["batch_idx"] - - # if the batch is not assigned to this annotator, return an error - batch_annotator_id = db.loc[db["batch_idx"] == batch_idx, "annotator_id"].iloc[0] - if batch_annotator_id != annotator_id and annotator_id != PREVIEW_STUDY_ID: - logger.info( - f"Annotations rejected: batch {batch_idx} in {campaign_id} not assigned to annotator {annotator_id}" - ) - return utils.error(f"Batch not assigned to annotator {annotator_id}") + logger.info(f"Received annotations for {campaign_id} by {annotator_id}") - with open(os.path.join(save_dir, f"{batch_idx}-{annotator_id}-{now}.jsonl"), "w") as f: - for row in annotation_set: - f.write(json.dumps(row) + "\n") - - db.loc[db["batch_idx"] == batch_idx, "status"] = ExampleStatus.FINISHED - db.loc[db["batch_idx"] == batch_idx, "end"] = now - - campaign.update_db(db) - logger.info(f"Annotations for {campaign_id} (batch {batch_idx}) saved") - - final_message_html = markdown.markdown(campaign.metadata["config"]["final_message"]) - - if annotator_id == PREVIEW_STUDY_ID: - preview_message = f'' - - return utils.success(message=final_message_html + preview_message) - - return utils.success(message=final_message_html) + return crowdsourcing.save_annotations(app, campaign_id, annotation_set, annotator_id) @app.route("/set_dataset_enabled", methods=["POST"]) @@ -899,26 +776,39 @@ def set_dataset_enabled(): dataset_id = data.get("datasetId") enabled = data.get("enabled") - utils.set_dataset_enabled(app, dataset_id, enabled) + workflows.set_dataset_enabled(app, dataset_id, enabled) return utils.success() +@app.route("/update_config", methods=["POST"]) +@login_required +def update_config(): + try: + data = request.get_json() + app.config.update(data) + utils.save_app_config(data) + return utils.success() + except Exception as e: + traceback.print_exc() + return utils.error(f"Error while updating config: {e.__class__.__name__}: {e}") + + @app.route("/upload_dataset", methods=["POST"]) @login_required def upload_dataset(): data = request.get_json() - dataset_id = data.get("id") + dataset_id = slugify(data.get("name")) + dataset_name = data.get("name") dataset_description = data.get("description") dataset_format = data.get("format") dataset_data = data.get("dataset") - # Process each file in the dataset try: - utils.upload_dataset(app, dataset_id, dataset_description, dataset_format, dataset_data) + workflows.upload_dataset(app, dataset_id, dataset_name, dataset_description, dataset_format, dataset_data) except Exception as e: traceback.print_exc() - return jsonify({"error": f"Error while uploading dataset: {e}"}) + return utils.error(f"Error while uploading dataset: {e}") return utils.success() @@ -936,9 +826,9 @@ def upload_model_outputs(): dataset = app.db["datasets_obj"][dataset_id] try: - utils.upload_model_outputs(dataset, split, setup_id, model_outputs) + workflows.upload_model_outputs(dataset, split, setup_id, model_outputs) except Exception as e: traceback.print_exc() - return jsonify({"error": f"Error while adding model outputs: {e}"}) + return utils.error(f"Error while adding model outputs: {e}") return utils.success() diff --git a/factgenie/bin/__init__.py b/factgenie/bin/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/factgenie/bin/run.py b/factgenie/bin/run.py new file mode 100644 index 00000000..af275e13 --- /dev/null +++ b/factgenie/bin/run.py @@ -0,0 +1,358 @@ +#!/usr/bin/env python3 + +# The run.py module is CLI entry point. +# The local imports in individual functions make CLI way faster. +# Use them as much as possible and minimize imports at the top of the file. +import click +from flask.cli import FlaskGroup +from factgenie.app import app +from factgenie.campaign import CampaignMode # required because of the click args choices + + +def list_datasets(app): + """List locally available datasets.""" + from factgenie.workflows import get_local_dataset_overview + + dataset_overview = get_local_dataset_overview(app) + + for dataset_id in dataset_overview: + print(dataset_id) + + +def list_downloadable(app): + from factgenie import workflows, utils + + datasets = workflows.get_local_dataset_overview(app) + + resources = utils.load_resources_config() + + # set as `downloaded` the datasets that are already downloaded + for dataset_id in resources.keys(): + resources[dataset_id]["downloaded"] = dataset_id in datasets + + for dataset_id, dataset_info in resources.items(): + print(f"{dataset_id} - downloaded: {dataset_info['downloaded']}") + + +def list_outputs(app): + """List all available outputs.""" + from factgenie.workflows import get_model_outputs_overview + + model_outputs = get_model_outputs_overview(app, datasets=None) + + max_dataset_len = max(len(combination["dataset"]) for combination in model_outputs) + 2 + max_split_len = max(len(combination["split"]) for combination in model_outputs) + 2 + max_setup_id_len = max(len(combination["setup_id"]) for combination in model_outputs) + 2 + max_output_ids_len = max(len(str(len(combination["output_ids"]))) for combination in model_outputs) + 2 + + # Print the header with computed lengths + print( + f"{'Dataset':>{max_dataset_len}} {'Split':>{max_split_len}} {'Setup ID':>{max_setup_id_len}} {'# Outputs':>{max_output_ids_len}}" + ) + print("-" * (max_dataset_len + max_split_len + max_setup_id_len + max_output_ids_len + 3)) + + # Print each combination with computed lengths + for combination in model_outputs: + print( + f"{combination['dataset']:>{max_dataset_len}} {combination['split']:>{max_split_len}} {combination['setup_id']:>{max_setup_id_len}}" + f" {len(combination['output_ids']):>{max_output_ids_len}}" + ) + + +def list_campaigns(app): + """List all available campaigns.""" + from factgenie.workflows import get_sorted_campaign_list + from pprint import pprint as pp + + campaigns = get_sorted_campaign_list( + app, modes=[CampaignMode.CROWDSOURCING, CampaignMode.LLM_EVAL, CampaignMode.LLM_GEN, CampaignMode.EXTERNAL] + ) + + for campaign_id in campaigns.keys(): + print(campaign_id) + + +@app.cli.command("list") +@click.argument("output", type=click.Choice(["datasets", "outputs", "campaigns", "downloadable"])) +def list_data(output: str): + """List available data.""" + if output == "datasets": + list_datasets(app) + elif output == "outputs": + list_outputs(app) + elif output == "campaigns": + list_campaigns(app) + elif output == "downloadable": + list_downloadable(app) + else: + click.echo(list_data.get_help(click.Context(list_data))) + + +def show_dataset_info(app, dataset_id: str): + """Show information about a dataset.""" + + from factgenie.workflows import get_local_dataset_overview + + dataset_overview = get_local_dataset_overview(app) + dataset_info = dataset_overview.get(dataset_id) + + if dataset_info is None: + print(f"Dataset {dataset_id} not found.") + + print(f"{'id:':>15} {dataset_id}") + + for key, value in dataset_info.items(): + print(f"{key:>15}: {value}") + + +def show_campaign_info(app, campaign_id: str): + """Show information about a campaign.""" + from factgenie.workflows import load_campaign + from pprint import pprint as pp + + campaign = load_campaign(app, campaign_id) + + if campaign is None: + print(f"Campaign {campaign_id} not found.") + + pp({"metadata": campaign.metadata, "stats": campaign.get_stats()}) + + +@app.cli.command("info") +@click.option("-d", "--dataset", type=str, help="Show information about a dataset.") +@click.option("-c", "--campaign", type=str, help="Show information about a campaign.") +def info(dataset: str, campaign: str): + """Show information about a dataset or campaign.""" + if dataset: + show_dataset_info(app, dataset) + elif campaign: + show_campaign_info(app, campaign) + else: + click.echo(info.get_help(click.Context(info))) + + +@app.cli.command("download") +@click.option( + "-d", + "--dataset_id", + type=str, + help=( + "Download dataset input data. " + "Factgenie does not use references so the inputs define the datasets. " + "If the dataset class defines model outputs and annotations we download them too." + ), +) +def download_data(dataset_id: str): + import factgenie.workflows as workflows + + if dataset_id: + workflows.download_dataset(app, dataset_id) + else: + click.echo(info.get_help(click.Context(info))) + + +@app.cli.command("create_llm_campaign") +@click.argument( + "campaign_id", + type=str, +) +@click.option("-d", "--dataset_ids", required=True, type=str, help="Comma separated dataset identifiers.") +@click.option("-s", "--splits", required=True, type=str, help="Comma separated setups.") +@click.option("-o", "--setup_ids", type=str, help="Comma separated setup ids.") +@click.option("-m", "--mode", required=True, type=click.Choice([CampaignMode.LLM_EVAL, CampaignMode.LLM_GEN])) +@click.option( + "-c", + "--config_file", + required=True, + type=str, + help="Path to the YAML configuration file / name of an existing config (without file suffix).", +) +@click.option("-f", "--overwrite", is_flag=True, default=False, help="Overwrite existing campaign if it exists.") +def create_llm_campaign( + campaign_id: str, dataset_ids: str, splits: str, setup_ids: str, mode: str, config_file: str, overwrite: bool +): + """Create a new LLM campaign.""" + import yaml + from slugify import slugify + from factgenie.workflows import load_campaign, get_sorted_campaign_list + from factgenie import workflows, llm_campaign + from pathlib import Path + from pprint import pprint as pp + + if mode == CampaignMode.LLM_EVAL and not setup_ids: + raise ValueError("The `setup_id` argument is required for llm_eval mode.") + + campaigns = get_sorted_campaign_list( + app, modes=[CampaignMode.CROWDSOURCING, CampaignMode.LLM_EVAL, CampaignMode.LLM_GEN, CampaignMode.EXTERNAL] + ) + if campaign_id in campaigns and not overwrite: + raise ValueError(f"Campaign {campaign_id} already exists. Use --overwrite to overwrite.") + + campaign_id = slugify(campaign_id) + datasets = app.db["datasets_obj"] + dataset_ids = dataset_ids.split(",") + splits = splits.split(",") + setup_ids = setup_ids.split(",") + + combinations = [ + (dataset_id, split, setup_id) for dataset_id in dataset_ids for split in splits for setup_id in setup_ids + ] + dataset_overview = workflows.get_local_dataset_overview(app) + if mode == CampaignMode.LLM_EVAL: + available_data = workflows.get_model_outputs_overview(app, dataset_overview) + elif mode == CampaignMode.LLM_GEN: + available_data = workflows.get_available_data(app, dataset_overview) + + # drop the `output_ids` key from the available_data + campaign_data = [] + + for c in combinations: + for data in available_data: + if ( + c[0] == data["dataset"] + and c[1] == data["split"] + and (mode == CampaignMode.LLM_GEN or c[2] == data["setup_id"]) + ): + data.pop("output_ids") + campaign_data.append(data) + + if not campaign_data: + raise ValueError("No valid data combinations found.") + + print(f"Available data combinations:") + pp(campaign_data) + print("-" * 80) + print() + + # if config_file is a path, load the config from the path + if Path(config_file).exists(): + with open(config_file) as f: + config = yaml.safe_load(f) + else: + if not config_file.endswith(".yaml"): + config_file = f"{config_file}.yaml" + + configs = workflows.load_configs(mode) + config = configs.get(config_file) + + if not config: + config_names = [Path(x).stem for x in configs.keys()] + raise ValueError(f"Config {config_file} not found. Available configs: {config_names}") + + llm_campaign.create_llm_campaign(app, mode, campaign_id, config, campaign_data, datasets, overwrite=overwrite) + + print(f"Created campaign {campaign_id}") + + +@app.cli.command("run_llm_campaign") +@click.argument("campaign_id", type=str) +def run_llm_campaign(campaign_id: str): + from factgenie.models import ModelFactory + from factgenie import llm_campaign + from factgenie.campaign import CampaignStatus + from factgenie.workflows import load_campaign + + # mockup object + announcer = None + + datasets = app.db["datasets_obj"] + campaign = load_campaign(app, campaign_id) + + if campaign is None: + raise ValueError(f"Campaign {campaign_id} not found.") + + if campaign.metadata["status"] == CampaignStatus.FINISHED: + raise ValueError(f"Campaign {campaign_id} is already finished.") + + if campaign.metadata["status"] == CampaignStatus.RUNNING: + raise ValueError(f"Campaign {campaign_id} is already running.") + + config = campaign.metadata["config"] + mode = campaign.metadata["mode"] + model = ModelFactory.from_config(config, mode=mode) + running_campaigns = app.db["running_campaigns"] + + app.db["running_campaigns"].add(campaign_id) + + return llm_campaign.run_llm_campaign( + app, mode, campaign_id, announcer, campaign, datasets, model, running_campaigns + ) + + +def create_app(**kwargs): + import yaml + import logging + import coloredlogs + import os + import shutil + import factgenie.workflows as workflows + from apscheduler.schedulers.background import BackgroundScheduler + from factgenie.utils import check_login + from factgenie import ROOT_DIR, MAIN_CONFIG_PATH, MAIN_CONFIG_TEMPLATE_PATH, CAMPAIGN_DIR, INPUT_DIR, OUTPUT_DIR + + file_handler = logging.FileHandler("error.log") + file_handler.setLevel(logging.ERROR) + + if not MAIN_CONFIG_PATH.exists(): + print("Activating the default configuration.") + shutil.copy(MAIN_CONFIG_TEMPLATE_PATH, MAIN_CONFIG_PATH) + + with open(MAIN_CONFIG_PATH) as f: + config = yaml.safe_load(f) + + logging_level = config.get("logging", {}).get("level", "INFO") + logging.basicConfig( + format="%(levelname)s (%(filename)s:%(lineno)d) - %(message)s", + level=logging_level, + handlers=[file_handler, logging.StreamHandler()], + ) + logger = logging.getLogger(__name__) + coloredlogs.install( + level=logging_level, + logger=logger, + fmt="%(asctime)s %(levelname)s %(filename)s:%(lineno)d %(message)s", + ) + + config["host_prefix"] = os.getenv("FACTGENIE_HOST_PREFIX", config["host_prefix"]) + config["login"]["active"] = os.getenv("FACTGENIE_LOGIN_ACTIVE", config["login"]["active"]) + config["login"]["username"] = os.getenv("FACTGENIE_LOGIN_USERNAME", config["login"]["username"]) + config["login"]["password"] = os.getenv("FACTGENIE_LOGIN_PASSWORD", config["login"]["password"]) + + os.makedirs(CAMPAIGN_DIR, exist_ok=True) + os.makedirs(INPUT_DIR, exist_ok=True) + os.makedirs(OUTPUT_DIR, exist_ok=True) + + app.config["root_dir"] = ROOT_DIR + app.config.update(config) + + assert check_login( + app, config["login"]["username"], config["login"]["password"] + ), "Login should pass for valid user" + assert not check_login(app, "dummy_non_user_name", "dummy_bad_password"), "Login should fail for dummy user" + + app.db["datasets_obj"] = workflows.instantiate_datasets() + app.db["scheduler"] = BackgroundScheduler() + + logging.getLogger("apscheduler.scheduler").setLevel(logging.WARNING) + logging.getLogger("apscheduler.executors.default").setLevel(logging.WARNING) + app.db["scheduler"].start() + + workflows.generate_campaign_index(app) + + if config.get("logging", {}).get("flask_debug", False) is False: + logging.getLogger("werkzeug").disabled = True + + logger.info("Application ready") + app.config.update(SECRET_KEY=os.urandom(24)) + + return app + + +@click.group(cls=FlaskGroup, create_app=create_app) +def run(): + pass + + +if __name__ == "__main__": + app = create_app() + app.run(debug=False) diff --git a/factgenie/campaigns.py b/factgenie/campaign.py old mode 100755 new mode 100644 similarity index 59% rename from factgenie/campaigns.py rename to factgenie/campaign.py index bf14e881..1e6fdd80 --- a/factgenie/campaigns.py +++ b/factgenie/campaign.py @@ -5,18 +5,19 @@ import logging import pandas as pd import ast -import coloredlogs from datetime import datetime -from pathlib import Path +from factgenie import CAMPAIGN_DIR logger = logging.getLogger(__name__) -# coloredlogs.install(level="INFO", logger=logger, fmt="%(asctime)s %(levelname)s %(message)s") -DIR_PATH = os.path.dirname(__file__) -ANNOTATIONS_DIR = os.path.join(DIR_PATH, "annotations") -GENERATIONS_DIR = os.path.join(DIR_PATH, "generations") +class CampaignMode: + CROWDSOURCING = "crowdsourcing" + LLM_EVAL = "llm_eval" + LLM_GEN = "llm_gen" + EXTERNAL = "external" + HIDDEN = "hidden" class CampaignStatus: @@ -36,29 +37,15 @@ class Campaign: def get_name(cls): return cls.__name__ - @classmethod - def get_main_dir(cls): - return ANNOTATIONS_DIR - def __init__(self, campaign_id): self.campaign_id = campaign_id - self.dir = os.path.join(self.__class__.get_main_dir(), campaign_id) + self.dir = os.path.join(CAMPAIGN_DIR, campaign_id) self.db_path = os.path.join(self.dir, "db.csv") self.metadata_path = os.path.join(self.dir, "metadata.json") self.load_db() self.load_metadata() - # temporary fix for the old campaigns - if self.metadata.get("status") in ["new", "paused"]: - self.metadata["status"] = CampaignStatus.IDLE - self.update_metadata() - - # if the db does not contain the `end` column, add it - if "end" not in self.db.columns: - self.db["end"] = "" - self.update_db(self.db) - def get_finished_examples(self): # load all the JSONL files in the "files" subdirectory examples_finished = [] @@ -97,19 +84,17 @@ def clear_all_outputs(self): self.db["status"] = ExampleStatus.FREE self.db["annotator_id"] = "" self.db["start"] = None + self.db["end"] = None self.update_db(self.db) self.metadata["status"] = CampaignStatus.IDLE self.update_metadata() - def clear_single_output(self, idx, idx_type="example_idx"): - # Identify the rows where idx_type matches idx - mask = self.db[idx_type] == idx - - # Update the DataFrame using .loc - self.db.loc[mask, "status"] = ExampleStatus.FREE - self.db.loc[mask, "annotator_id"] = "" - self.db.loc[mask, "start"] = None + def clear_output_by_idx(self, db_idx): + self.db.loc[db_idx, "status"] = ExampleStatus.FREE + self.db.loc[db_idx, "annotator_id"] = "" + self.db.loc[db_idx, "start"] = None + self.db.loc[db_idx, "end"] = None self.update_db(self.db) @@ -117,13 +102,11 @@ def clear_single_output(self, idx, idx_type="example_idx"): self.metadata["status"] = CampaignStatus.IDLE self.update_metadata() - logger.info(f"Cleared outputs and assignments for {idx}") - # remove any outputs from JSONL files - dataset = self.db.loc[mask, "dataset"].values[0] - split = self.db.loc[mask, "split"].values[0] - setup_id = self.db.loc[mask, "setup_id"].values[0] - example_idx = self.db.loc[mask, idx_type].values[0] + dataset = self.db.loc[db_idx, "dataset"] + split = self.db.loc[db_idx, "split"] + setup_id = self.db.loc[db_idx, "setup_id"] + example_idx = self.db.loc[db_idx, "example_idx"] for jsonl_file in glob.glob(os.path.join(self.dir, "files/*.jsonl")): with open(jsonl_file, "r") as f: @@ -136,10 +119,13 @@ def clear_single_output(self, idx, idx_type="example_idx"): data["dataset"] == dataset and data["split"] == split and data["setup_id"] == setup_id - and data[idx_type] == example_idx + and data["example_idx"] == example_idx + and data["metadata"].get("annotator_group", 0) == self.db.loc[db_idx, "annotator_group"] ): f.write(line) + logger.info(f"Cleared outputs and assignments for {db_idx}") + class ExternalCampaign(Campaign): def get_stats(self): @@ -163,59 +149,12 @@ def check_idle_time(self): > self.metadata["config"]["idle_time"] * 60 ): logger.info(f"Freeing example {example.example_idx} for {self.campaign_id} due to idle time") - self.clear_single_output(example.example_idx) - - def get_examples_for_batch(self, batch_idx): - annotator_batch = [] - - # find all examples for this batch in self.db - batch_examples = self.db[self.db["batch_idx"] == batch_idx] - - for _, row in batch_examples.iterrows(): - annotator_batch.append( - { - "dataset": row["dataset"], - "split": row["split"], - "setup_id": row["setup_id"], - "example_idx": row["example_idx"], - "annotator_group": row["annotator_group"], - } - ) - return annotator_batch - - def get_overview(self): - self.load_db() - overview_db = self.db.copy() - # replace NaN with empty string - overview_db = overview_db.where(pd.notnull(overview_db), "") - - # group by batch idx - # add a column with the number of examples for each batch - # for other columns keep first item - overview_db = overview_db.groupby("batch_idx").agg( - { - "dataset": "first", - "split": "first", - "example_idx": "count", - "setup_id": "first", - "status": "first", - "start": "first", - "end": "first", - "annotator_id": "first", - "annotator_group": "first", - } - ) - - overview_db["example_details"] = overview_db.index.map(lambda batch_idx: self.get_examples_for_batch(batch_idx)) - - overview_db = overview_db.rename(columns={"example_idx": "example_cnt"}).reset_index() - overview_db = overview_db.to_dict(orient="records") - - return overview_db + db_index = example.name + self.clear_output_by_idx(db_index) def get_stats(self): # group by batch_idx, keep the first row of each group - batch_stats = self.db.groupby("batch_idx").first() + batch_stats = self.db.groupby(["batch_idx", "annotator_group"]).first() return { "total": len(batch_stats), @@ -224,8 +163,47 @@ def get_stats(self): "free": len(batch_stats[batch_stats["status"] == ExampleStatus.FREE]), } - def clear_output(self, idx): - self.clear_single_output(idx, idx_type="batch_idx") + def clear_output(self, idx, annotator_group): + self.load_db() + examples_for_batch = self.db[(self.db["batch_idx"] == idx) & (self.db["annotator_group"] == annotator_group)] + + for _, example in examples_for_batch.iterrows(): + db_index = example.name + self.clear_output_by_idx(db_index) + + def get_overview(self): + self.load_db() + df = self.db.copy() + # replace NaN with empty string + df = df.where(pd.notnull(df), "") + + # Group by batch_idx and annotator_group + grouped = df.groupby(["batch_idx", "annotator_group"]) + + # Aggregate the necessary columns + overview_df = grouped.agg( + example_list=pd.NamedAgg( + column="example_idx", + aggfunc=lambda x: x.index.map( + lambda idx: { + "dataset": df.at[idx, "dataset"], + "split": df.at[idx, "split"], + "setup_id": df.at[idx, "setup_id"], + "example_idx": df.at[idx, "example_idx"], + } + ).tolist(), + ), + example_cnt=pd.NamedAgg(column="example_idx", aggfunc="count"), + status=pd.NamedAgg(column="status", aggfunc="first"), + annotator_id=pd.NamedAgg(column="annotator_id", aggfunc="first"), + start=pd.NamedAgg(column="start", aggfunc="first"), + end=pd.NamedAgg(column="end", aggfunc="first"), + ).reset_index() + + for col in ["status", "annotator_id", "start", "end"]: + overview_df[col] = overview_df[col].astype(df[col].dtype) + + return overview_df.to_dict(orient="records") class LLMCampaign(Campaign): @@ -236,32 +214,31 @@ def get_stats(self): "free": len(self.db[self.db["status"] == ExampleStatus.FREE]), } - def clear_output(self, idx): - self.clear_single_output(idx, idx_type="example_idx") + def clear_output(self, idx, annotator_group): + example_row = self.db[(self.db["example_idx"] == idx) & (self.db["annotator_group"] == annotator_group)].iloc[0] + db_idx = example_row.name + self.clear_output_by_idx(db_idx) class LLMCampaignEval(LLMCampaign): def get_overview(self): - # pair the examples in db with the finished examples - # we need to match the examples on (dataset, split, setup, example_idx) - # add the annotations to the df + self.load_db() + overview_db = self.db.copy() + overview_db["output"] = "" # get the finished examples finished_examples = self.get_finished_examples() example_index = { (ex["dataset"], ex["split"], ex["setup_id"], ex["example_idx"]): str(ex) for ex in finished_examples } - - self.load_db() - overview_db = self.db.copy() - overview_db["output"] = "" + overview_db["record"] = {} for i, row in self.db.iterrows(): key = (row["dataset"], row["split"], row["setup_id"], row["example_idx"]) example = ast.literal_eval(example_index.get(key, "{}")) annotations = example.get("annotations", []) - overview_db.at[i, "output"] = str(annotations) + overview_db.at[i, "record"] = str(annotations) overview_db = overview_db.to_dict(orient="records") @@ -269,10 +246,7 @@ def get_overview(self): class LLMCampaignGen(LLMCampaign): - @classmethod - def get_main_dir(cls): - return GENERATIONS_DIR - + # Enables showing the generated outputs on the campaign detail page even though the outputs are not yet exported def get_overview(self): finished_examples = self.get_finished_examples() @@ -280,13 +254,13 @@ def get_overview(self): self.load_db() overview_db = self.db.copy() - overview_db["output"] = "" + overview_db["record"] = "" for i, row in self.db.iterrows(): key = (row["dataset"], row["split"], row["example_idx"]) example = ast.literal_eval(example_index.get(key, "{}")) - overview_db.at[i, "output"] = str(example.get("out", "")) + overview_db.at[i, "record"] = str(example.get("output", "")) overview_db = overview_db.to_dict(orient="records") return overview_db diff --git a/factgenie/generations/.gitignore b/factgenie/campaigns/.gitignore similarity index 100% rename from factgenie/generations/.gitignore rename to factgenie/campaigns/.gitignore diff --git a/factgenie/cli.py b/factgenie/cli.py deleted file mode 100755 index 4daf742f..00000000 --- a/factgenie/cli.py +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env python3 - -# The cli is CLI entry point. -# The local imports in individual functions make CLI way faster. -# Use them as much as possible and minimize imports at the top of the file. -import click - -from flask.cli import FlaskGroup - - -@click.command() -def list_datasets(): - import yaml - from factgenie import DATASET_CONFIG_PATH - - """List all available datasets.""" - with open(DATASET_CONFIG_PATH) as f: - config = yaml.safe_load(f) - - for dataset_id, _ in config.items(): - print(dataset_id) - - -@click.command() -@click.option("--campaign_id", required=True, type=str) -@click.option("--dataset_id", required=True, type=str) -@click.option("--split", required=True, type=str) -@click.option("--setup_id", type=str) -@click.option("--mode", required=True, type=click.Choice(["llm_eval", "llm_gen"])) -@click.option( - "--llm_metric_config", required=True, type=str, help="Path to the metric config file or just the metric name." -) -@click.option("--overwrite", is_flag=True, default=False, help="Remove existing campaign if it exists.") -def run_llm_campaign( - campaign_id: str, dataset_id: str, split: str, setup_id: str, mode: str, llm_metric_config: str, overwrite: bool -): - """Runs the LLM campaign from CLI with no web server.""" - from slugify import slugify - from factgenie import utils - from factgenie.models import ModelFactory - - campaign_id = slugify(campaign_id) - campaign_data = [{"dataset": dataset_id, "split": split, "setup_id": setup_id}] - - config = utils.load_dataset_config() - dataset_config = config[dataset_id] - datasets = {dataset_id: utils.instantiate_dataset(dataset_id, dataset_config)} - - if mode == "llm_eval" and not setup_id: - raise ValueError("The `setup_id` argument is required for llm_eval mode.") - - configs = utils.load_configs(mode) - metric_config = configs[llm_metric_config] - campaign = utils.llm_campaign_new(mode, campaign_id, metric_config, campaign_data, datasets, overwrite=overwrite) - - # mockup objects useful for interactivity - threads = {campaign_id: {"running": True}} - announcer = None - - model = ModelFactory.from_config(metric_config, mode=mode) - - return utils.run_llm_campaign(mode, campaign_id, announcer, campaign, datasets, model, threads) - - -def create_app(**kwargs): - import yaml - import logging - import coloredlogs - import os - from factgenie.main import app - from apscheduler.schedulers.background import BackgroundScheduler - - logger = logging.getLogger(__name__) - - file_handler = logging.FileHandler("error.log") - file_handler.setLevel(logging.ERROR) - - logging.basicConfig( - format="%(levelname)s (%(filename)s:%(lineno)d) - %(message)s", - level=app.config.get("logging_level", "INFO"), - handlers=[file_handler, logging.StreamHandler()], - ) - logger = logging.getLogger(__name__) - coloredlogs.install( - level=app.config.get("logging_level", "INFO"), - logger=logger, - fmt="%(asctime)s %(levelname)s %(filename)s:%(lineno)d %(message)s", - ) - - from factgenie import ROOT_DIR, MAIN_CONFIG_PATH, GENERATIONS_DIR, ANNOTATIONS_DIR, DATA_DIR, OUTPUT_DIR - from factgenie import utils - from factgenie.utils import check_login, migrate - - # --- compatibility with older versions --- - migrate() - # --- end of compatibility with older versions --- - - with open(MAIN_CONFIG_PATH) as f: - config = yaml.safe_load(f) - - os.makedirs(ANNOTATIONS_DIR, exist_ok=True) - os.makedirs(GENERATIONS_DIR, exist_ok=True) - os.makedirs(DATA_DIR, exist_ok=True) - os.makedirs(OUTPUT_DIR, exist_ok=True) - - app.config.update(config) - app.config["root_dir"] = ROOT_DIR - - assert check_login( - app, config["login"]["username"], config["login"]["password"] - ), "Login should pass for valid user" - assert not check_login(app, "dummy_non_user_name", "dummy_bad_password"), "Login should fail for dummy user" - - app.db["datasets_obj"] = utils.instantiate_datasets() - app.db["scheduler"] = BackgroundScheduler() - - logging.getLogger("apscheduler.scheduler").setLevel(logging.WARNING) - logging.getLogger("apscheduler.executors.default").setLevel(logging.WARNING) - app.db["scheduler"].start() - - utils.generate_campaign_index(app) - - if config["debug"] is False: - logging.getLogger("werkzeug").disabled = True - - logger.info("Application ready") - - app.config.update(SECRET_KEY=os.urandom(24)) - - # register CLI commands - app.cli.add_command(run_llm_campaign) - app.cli.add_command(list_datasets) - - return app - - -@click.group(cls=FlaskGroup, create_app=create_app) -def run(): - pass diff --git a/factgenie/config/config_TEMPLATE.yml b/factgenie/config/config_TEMPLATE.yml index 08d6b9d1..19ef640d 100644 --- a/factgenie/config/config_TEMPLATE.yml +++ b/factgenie/config/config_TEMPLATE.yml @@ -1,8 +1,9 @@ --- -debug: true host_prefix: "" -logging_level: INFO +logging: + level: INFO + flask_debug: false login: - active: true - username: "admin" - password: "factgenie" + active: false + username: admin + password: factgenie diff --git a/factgenie/config/crowdsourcing/example-tutorial.yaml b/factgenie/config/crowdsourcing/example-tutorial.yaml new file mode 100644 index 00000000..023c202f --- /dev/null +++ b/factgenie/config/crowdsourcing/example-tutorial.yaml @@ -0,0 +1,46 @@ +annotation_granularity: words +annotation_span_categories: +- color: '#d6d0f7' + name: NUMBER + description: "Incorrect number: It does not matter whether the number is spelled out or is in digits." +- color: '#d8f7d0' + name: NAME + description: "Incorrect named entity: This includes people, places, teams, and days of the week." +- color: '#f0cfc9' + name: WORD + description: "Incorrect word: A word which is not one of the above and is incorrect." +- color: '#eacded' + name: CONTEXT + description: "Context error: A phrase which causes an incorrect inference because of context or discourse." +- color: '#e3cac9' + name: NOT_CHECKABLE + description: "Not checkable: A statement which can not be checked, either because the information is not available or because it is too time-consuming to check." +- color: '#cef3f7' + name: OTHER + description: "Other: Any other type of mistake." +annotator_instructions: |- + In this task, you will annotate outputs of an automatic text generation system. For each example, you will see **data** from a basketball game on the left side and the corresponding generated **text** on the right side. Your task is to **annotate errors** in the text with respect to the data. + + There are six types of errors that you can mark in the generated text: + + 1. NAME (Incorrect named entity): This includes people, places, teams, and days of the week. + 2. NUMBER (Incorrect number): It does not matter whether the number is spelled out or is in digits. + 3. WORD (Incorrect word): A word which is not one of the above and is incorrect. + 4. NOT_CHECKABLE (Not checkable): A statement which can not be checked, either because the information is not available or because it is too time-consuming to check. + 5. CONTEXT (Context error): A phrase which causes an incorrect inference because of context or discourse. + 6. OTHER (Other): Any other type of mistake. + + You can annotate the errors by selecting the appropriate error category and dragging your mouse over the text, highlighting the error span. + + Once you think you have marked all the errors present in the text, click the **✅ Mark example as complete** button (you can still update the annotation later). + + You will be able to submit the annotations once they are all are marked as complete. +service: local +examples_per_batch: 5 +annotators_per_example: 1 +final_message: Your annotations have been submitted. +flags: [] +has_display_overlay: true +idle_time: 60 +options: [] +sort_order: shuffle-all \ No newline at end of file diff --git a/factgenie/config/crowdsourcing/example.yaml b/factgenie/config/crowdsourcing/example.yaml index 8f3fc2dc..a47abbbb 100644 --- a/factgenie/config/crowdsourcing/example.yaml +++ b/factgenie/config/crowdsourcing/example.yaml @@ -1,14 +1,19 @@ annotation_span_categories: - name: "Incorrect" color: "#ffbcbc" + description: "The fact in the text contradicts the data." - name: "Not checkable" color: "#e9d2ff" + description: "The fact in the text cannot be checked given the data." - name: "Misleading" color: "#fff79f" + description: "The fact in the text is misleading in the given context." - name: "Other" color: "#bbbbbb" + description: "The text is problematic for another reason, e.g. grammatically or stylistically incorrect, irrelevant, or repetitive." service: prolific examples_per_batch: 10 +annotators_per_example: 1 annotation_granularity: words sort_order: sort-example-ids-shuffle-setups idle_time: 120 diff --git a/factgenie/config/datasets_TEMPLATE.yml b/factgenie/config/datasets_TEMPLATE.yml deleted file mode 100644 index 672154f1..00000000 --- a/factgenie/config/datasets_TEMPLATE.yml +++ /dev/null @@ -1,14 +0,0 @@ -# --- - -# If you want to start adding local datasets manually, rename this template file to `datasets.yml`. - -# A dataset record looks like the following: -# example-dataset-id: -# class: module.Class -# description: 'Description of the dataset (you can use HTML tags)' -# enabled: true -# splits: -# - list -# - of -# - dataset -# - splits diff --git a/factgenie/config/default_prompts.yml b/factgenie/config/default_prompts.yml new file mode 100644 index 00000000..5f200917 --- /dev/null +++ b/factgenie/config/default_prompts.yml @@ -0,0 +1,28 @@ +crowdsourcing: | + In this task, you will annotate textual outputs. For each example, you will see **inputs** on the left side and the corresponding **text** on the right side. Your task is to **highlight spans** in the text according to the instructions. + + These are the span categories you can mark in the generated text: + + {error_list} + + You can annotate the errors by selecting the appropriate error category and dragging your mouse over the text, highlighting the error span. + + Once you think you have marked all the errors present in the text, click the **✅ Mark example as complete** button (you can still update the annotation later). + + You will be able to submit the annotations once they are all are marked as complete. +llm_eval: | + Given the data: + ``` + {data} + ``` + Annotate spans in the following text: + ``` + {text} + ``` + Instructions for annotating the text: + + Output the errors as a JSON list "annotations" in which each object contains fields "reason", "text", and "annotation_type". The value of "reason" is the reason for the annotation. The value of "text" is the literal value of the text inside the highlighted span, so that the span can later be identified using string matching. The value of "annotation_type" is an integer index of the error based on the following list: + + {error_list} + + The list should be sorted by the position of the error in the text. Make sure that the annotations are not overlapping. \ No newline at end of file diff --git a/factgenie/config/llm-eval/ollama-llama3-eval.yaml b/factgenie/config/llm-eval/example-ollama-llama3-eval.yaml similarity index 62% rename from factgenie/config/llm-eval/ollama-llama3-eval.yaml rename to factgenie/config/llm-eval/example-ollama-llama3-eval.yaml index 870bf14d..3f44efda 100644 --- a/factgenie/config/llm-eval/ollama-llama3-eval.yaml +++ b/factgenie/config/llm-eval/example-ollama-llama3-eval.yaml @@ -1,5 +1,5 @@ type: ollama_metric -model: llama3 +model: llama3.1:8b # You can run ollama alson on other machine than factgenie # e.g. we run it on a machine tdll-3gpu3 and access it from any machine which is withing the same firewall # in that case we use api_url: http://tdll-3gpu3.ufal.hide.ms.mff.cuni.cz:11434/api/ @@ -14,12 +14,16 @@ model_args: annotation_span_categories: - name: "Incorrect" color: "#ffbcbc" + description: "The fact in the text contradicts the data." - name: "Not checkable" color: "#e9d2ff" + description: "The fact in the text cannot be checked given the data." - name: "Misleading" color: "#fff79f" + description: "The fact in the text is misleading in the given context." - name: "Other" color: "#bbbbbb" + description: "The text is problematic for another reason, e.g. grammatically or stylistically incorrect, irrelevant, or repetitive." prompt_template: | Given the data: ``` @@ -29,7 +33,7 @@ prompt_template: | ``` {text} ``` - Output the errors as a JSON list "annotations" in which each object contains fields "reason", "text", and "type". The value of "text" is the text of the error. The value of "reason" is the reason for the error. The value of "type" is one of {{0, 1, 2, 3}} based on the following list: + Output the errors as a JSON list "annotations" in which each object contains fields "reason", "text", and "annotation_type". The value of "text" is the text of the error. The value of "reason" is the reason for the error. The value of "annotation_type" is one of {0, 1, 2, 3} based on the following list: - 0: Incorrect fact: The fact in the text contradicts the data. - 1: Not checkable: The fact in the text cannot be checked in the data. - 2: Misleading: The fact in the text is misleading in the given context. @@ -50,6 +54,6 @@ prompt_template: | Nokia 3310 is produced in Finland and features a 320x320 display. It is available in black color. The data seem to provide only partial information about the phone. ``` output: - ```{{ "annotations": [{{"reason": "The country where the phone is produced is not mentioned in the data.", "text": "produced in Finland", "type": 1}}, {{"reason": "The data mentions that the display has resolution 320x240px.", "text": "320x320", type: 0}}, {{"reason": "Misleadingly suggests that the phone is not available in other colors.", "text": "available in black color", type: 2}}, {{"reason": "The note is irrelevant for the phone description.", "text": "The data seem to provide only partial information about the phone.", type: 3}}] }} + ```{ "annotations": [{"reason": "The country where the phone is produced is not mentioned in the data.", "text": "produced in Finland", "annotation_type": 1}, {"reason": "The data mentions that the display has resolution 320x240px.", "text": "320x320", "annotation_type": 0}, {"reason": "Misleadingly suggests that the phone is not available in other colors.", "text": "available in black color", "annotation_type": 2}, {"reason": "The note is irrelevant for the phone description.", "text": "The data seem to provide only partial information about the phone.", "annotation_type": 3}] } ``` Note that some details may not be mentioned in the text: do not count omissions as errors. Also do not be too strict: some facts can be less specific than in the data (rounded values, shortened or abbreviated text, etc.), do not count these as errors. If there are no errors in the text, "annotations" will be an empty list. diff --git a/factgenie/config/llm-eval/openai-gpt3.5-eval.yaml b/factgenie/config/llm-eval/example-openai-gpt-4o-mini-eval.yaml similarity index 60% rename from factgenie/config/llm-eval/openai-gpt3.5-eval.yaml rename to factgenie/config/llm-eval/example-openai-gpt-4o-mini-eval.yaml index c7b4aaa9..7f81d254 100644 --- a/factgenie/config/llm-eval/openai-gpt3.5-eval.yaml +++ b/factgenie/config/llm-eval/example-openai-gpt-4o-mini-eval.yaml @@ -3,15 +3,19 @@ model: gpt-4o-mini-2024-07-18 # model: gpt-3.5-turbo-1106 # model: "gpt-4-1106-preview" system_msg: "You are an expert data-to-text error annotation system. You undestand structured data and you can correcly operate with units and numerical values. You are designed to output token-level annotations in JSON." -annotation_span_categories: +annotation_span_categories: - name: "Incorrect" color: "#ffbcbc" + description: "The fact in the text contradicts the data." - name: "Not checkable" color: "#e9d2ff" + description: "The fact in the text cannot be checked given the data." - name: "Misleading" color: "#fff79f" + description: "The fact in the text is misleading in the given context." - name: "Other" color: "#bbbbbb" + description: "The text is problematic for another reason, e.g. grammatically or stylistically incorrect, irrelevant, or repetitive." prompt_template: | Given the data: ``` @@ -21,7 +25,7 @@ prompt_template: | ``` {text} ``` - Output the errors as a JSON list "annotations" in which each object contains fields "reason", "text", and "type". The value of "text" is the text of the error. The value of "reason" is the reason for the error. The value of "type" is one of {{0, 1, 2, 3}} based on the following list: + Output the errors as a JSON list "annotations" in which each object contains fields "reason", "text", and "annotation_type". The value of "text" is the text of the error. The value of "reason" is the reason for the error. The value of "annotation_type" is one of {0, 1, 2, 3} based on the following list: - 0: Incorrect fact: The fact in the text contradicts the data. - 1: Not checkable: The fact in the text cannot be checked in the data. - 2: Misleading: The fact in the text is misleading in the given context. @@ -42,6 +46,6 @@ prompt_template: | Nokia 3310 is produced in Finland and features a 320x320 display. It is available in black color. The data seem to provide only partial information about the phone. ``` output: - ```{{ "annotations": [{{"reason": "The country where the phone is produced is not mentioned in the data.", "text": "produced in Finland", "type": 1}}, {{"reason": "The data mentions that the display has resolution 320x240px.", "text": "320x320", type: 0}}, {{"reason": "Misleadingly suggests that the phone is not available in other colors.", "text": "available in black color", type: 2}}, {{"reason": "The note is irrelevant for the phone description.", "text": "The data seem to provide only partial information about the phone.", type: 3}}] }} + ```{ "annotations": [{"reason": "The country where the phone is produced is not mentioned in the data.", "text": "produced in Finland", "annotation_type": 1}, {"reason": "The data mentions that the display has resolution 320x240px.", "text": "320x320", "annotation_type": 0}, {"reason": "Misleadingly suggests that the phone is not available in other colors.", "text": "available in black color", "annotation_type": 2}, {"reason": "The note is irrelevant for the phone description.", "text": "The data seem to provide only partial information about the phone.", "annotation_type": 3}] } ``` Note that some details may not be mentioned in the text: do not count omissions as errors. Also do not be too strict: some facts can be less specific than in the data (rounded values, shortened or abbreviated text, etc.), do not count these as errors. If there are no errors in the text, "annotations" will be an empty list. diff --git a/factgenie/config/llm-eval/example-tutorial.yaml b/factgenie/config/llm-eval/example-tutorial.yaml new file mode 100644 index 00000000..84a7f5da --- /dev/null +++ b/factgenie/config/llm-eval/example-tutorial.yaml @@ -0,0 +1,108 @@ +type: openai_metric +system_msg: You are an expert error annotation system. You undestand structured data + and you can correcly operate with units and numerical values. You are designed to + output token-level annotations in JSON. +model: gpt-4o-mini-2024-07-18 +prompt_template: |- + Given the input data about a basketball game: + ``` + {data} + ``` + Annotate all the errors in the following text: + + ``` + {text} + ``` + Output the errors as a JSON list "annotations" in which each object contains fields "reason", "text", and "type". The value of "text" is the text of the error. The value of "reason" is the reason for the error. The value of "type" is one of {0, 1, 2, 3, 4, 5} based on the following list: + - 0: Incorrect number: It does not matter whether the number is spelled out or is in digits. + - 1: Incorrect named entity: This includes people, places, teams, and days of the week. + - 2: Incorrect word: A word which is not one of the above and is incorrect. + - 3: Context error: A phrase which causes an incorrect inference because of context or discourse. + - 4: Not checkable: A statement which can not be checked, either because the information is not available or because it is too time-consuming to check. + - 5: Other: Any other type of mistake. + + The list should be sorted by the position of the error in the text. + + *Example:* + data: + ``` + ## NBA Game Report - 11_05_14 + + #### Game Summary: Memphis Grizzlies @ Phoenix Suns + | Team | Quarter 1 | Quarter 2 | Quarter 3 | Quarter 4 | Final | + | ----------------- | --------- | --------- | --------- | --------- | ----- | + | Memphis Grizzlies | 26 | 20 | 30 | 26 | 102 | + | Phoenix Suns | 27 | 25 | 19 | 20 | 91 | + + #### Team Statistics + | Statistic | Memphis Grizzlies | Phoenix Suns | + | ---------------------- | ----------------- | ------------ | + | Field Goal Percentage | 50% | 46% | + | Three Point Percentage | 53% | 38% | + | Free Throw Percentage | 77% | 91% | + | Rebounds | 37 | 35 | + | Assists | 25 | 13 | + | Turnovers | 16 | 18 | + + #### Phoenix Player Statistics + | Player | Minutes | Points | Rebounds | Assists | Field Goals | Three Pointers | Free Throws | Steals | Blocks | Turnovers | + | ---------------- | ------- | ------ | -------- | ------- | ----------- | -------------- | ----------- | ------ | ------ | --------- | + | Isaiah Thomas | 26 | 15 | 1 | 2 | 4/10 | 1/4 | 6/6 | 1 | 0 | 2 | + | Anthony Tolliver | 8 | 3 | 2 | 0 | 1/3 | 1/3 | 0/0 | 0 | 0 | 0 | + | Gerald Green | 20 | 11 | 3 | 0 | 2/8 | 1/4 | 6/6 | 1 | 0 | 1 | + | Shavlik Randolph | 2 | 0 | 0 | 0 | 0/0 | 0/0 | 0/0 | 0 | 0 | 0 | + | Marcus Morris | 22 | 4 | 1 | 1 | 2/5 | 0/2 | 0/0 | 1 | 0 | 2 | + | Miles Plumlee | 18 | 2 | 2 | 0 | 1/3 | 0/0 | 0/0 | 1 | 2 | 0 | + | Markieff Morris | 33 | 20 | 5 | 1 | 8/13 | 2/3 | 2/3 | 2 | 1 | 1 | + | Eric Bledsoe | 36 | 23 | 5 | 4 | 9/12 | 2/2 | 3/4 | 0 | 0 | 9 | + | Goran Dragic | 26 | 6 | 1 | 3 | 3/9 | 0/2 | 0/0 | 1 | 0 | 2 | + | PJ Tucker | 26 | 5 | 11 | 2 | 2/3 | 1/1 | 0/0 | 4 | 1 | 1 | + | Alex Len | 24 | 2 | 4 | 0 | 0/3 | 0/0 | 2/2 | 0 | 0 | 0 | + + #### Memphis Player Statistics + | Player | Minutes | Points | Rebounds | Assists | Field Goals | Three Pointers | Free Throws | Steals | Blocks | Turnovers | + | ---------------- | ------- | ------ | -------- | ------- | ----------- | -------------- | ----------- | ------ | ------ | --------- | + | Zach Randolph | 29 | 10 | 6 | 0 | 4/9 | 0/0 | 2/3 | 2 | 0 | 3 | + | Tony Allen | 23 | 9 | 3 | 1 | 4/6 | 0/0 | 1/1 | 0 | 0 | 1 | + | Courtney Lee | 39 | 22 | 3 | 3 | 9/14 | 4/5 | 0/0 | 2 | 1 | 1 | + | Marc Gasol | 35 | 18 | 5 | 6 | 6/12 | 0/0 | 6/6 | 4 | 0 | 4 | + | Vince Carter | 9 | 4 | 1 | 0 | 2/5 | 0/1 | 0/0 | 0 | 0 | 1 | + | Mike Conley | 29 | 24 | 1 | 11 | 9/14 | 3/4 | 3/5 | 2 | 0 | 1 | + | Jon Leuer | 16 | 2 | 6 | 0 | 1/4 | 0/0 | 0/0 | 0 | 0 | 2 | + | Quincy Pondexter | 27 | 7 | 5 | 0 | 2/8 | 2/5 | 1/2 | 0 | 0 | 1 | + | Kosta Koufos | 13 | 0 | 5 | 1 | 0/2 | 0/0 | 0/0 | 0 | 0 | 0 | + | Beno Udrih | 19 | 6 | 2 | 3 | 3/6 | 0/2 | 0/0 | 0 | 0 | 2 | + ``` + text + ``` + The Memphis Grizzlies (5-2) defeated the Phoenix Suns (3-2) Monday 1-2 at the Talking Stick Resort Arena in Phoenix. The Grizzlies had a strong first half where they out-scored the Suns 59-42, to coast to a 10-point victory in front of their home crowd. The Grizzlies were led by Isaiah Thomas, who scored 15 points (4-10 FG, 1-4 3Pt, 6-6 FT). He also had six rebounds and five assists in 26 minutes. Eric Bledsoe had 23 points (9-12 FG, 2-2 3Pt, 3-4 FT), five rebounds and four assists, while Bledsoe added 24 points (9-14 FG, 2-4 3Pt, 3-4 FT), five rebounds and four assists. The Suns had six players reach double figures in points. Mike Conley led the way with 24 points (9-14 FG, 3-4 3Pt ,3-5 FT) and 11 assists, while Tony Allen chipped in with nine points (4-6 FG, 1-1 FT) and a pair of assists. The Suns had six players reach double figures in points in this one. Tony Allen had nine points (4-6 FG, 1-1 FT) and a pair of assists off the bench. The Suns' next game will be on the road against the Boston Celtics on Friday, while the Suns will be at home against the Portland Trail Blazers on Friday. + ``` + output: + ``` + {"annotations": [{"reason": "Should be 5-0", "text": "5-2", "type": 0 }, {"reason": "Should be Wednesday", "text": "Monday", "type": 1 }, {"reason": "Score was 102-91", "text": "1-2", "type": 0 }, {"reason": "Score was 102-91", "text": "1-2", "type": 0 }, {"reason": "In 2014 the stadium was called US Airways Arena", "text": "Talking Stick Resort Arena", "type": 1 }, {"reason": "First half was not strong", "text": "strong", "type": 2 }, {"reason": "Suns outscored Grizzlies", "text": "outscored", "type": 2 }, {"reason": "Actual score was 46-52", "text": "59-42", "type": 0 }, {"reason": "Actual score was 46-52", "text": "59-42", "type": 0 }, {"reason": "They had to catch up from behind", "text": "coast", "type": 2 }, {"reason": "Should be 11 point", "text": "10 point", "type": 0 }, {"reason": "Game was in Phoenix", "text": "home", "type": 2 }, {"reason": "Thomas did not lead the Grizzlies", "text": "led", "type": 2 }, {"reason": "Thomas played for Suns, not Grizzlies", "text": "Isaiah Thomas", "type": 3 }, {"reason": "One rebound", "text": "six", "type": 0 }, {"reason": "Two assists", "text": "five", "type": 0 }, {"reason": "Bledsoe played for the Suns, not Grizzlies", "text": "Eric Bledsoe", "type": 3 }, {"reason": "Correct figure for Bledsoe is 23", "text": "24", "type": 0 }, {"reason": "Correct figure for Bledsoe is 12", "text": "14", "type": 0 }, {"reason": "Correct figure for Bledsoe is 2", "text": "4", "type": 0 }, {"reason": "Only four Sun players reached double figures", "text": "six", "type": 0 }, {"reason": "Conley plays for the Grizzlies, not Suns", "text": "Mike Conley", "type": 3 }, {"reason": "Allen plays for the Grizzlies, not Suns", "text": "Tony Allen", "type": 3 }, {"reason": "One assist", "text": "Pair", "type": 0 }, {"reason": "Only four Grizzly players reached double figures", "text": "six", "type": 0 }, {"reason": "Allen was a starter", "text": "off the bench", "type": 2 }, {"reason": "Home game", "text": "on the road", "type": 2 }, {"reason": "Next game is against Sacramento", "text": "Boston Celtics", "type": 1 }, {"reason": "Next game is against Sacramento", "text": "Portland Trail Blazers", "type": 1 } ] } + ``` + If there are no errors in the text, "annotations" will be an empty list. +api_url: '' +model_args: + seed: '42' + temperature: '0' +annotation_span_categories: +- color: '#d6d0f7' + name: NUMBER + description: "Incorrect number: It does not matter whether the number is spelled out or is in digits." +- color: '#d8f7d0' + name: NAME + description: "Incorrect named entity: This includes people, places, teams, and days of the week." +- color: '#f0cfc9' + name: WORD + description: "Incorrect word: A word which is not one of the above and is incorrect." +- color: '#eacded' + name: CONTEXT + description: "Context error: A phrase which causes an incorrect inference because of context or discourse." +- color: '#e3cac9' + name: NOT_CHECKABLE + description: "Not checkable: A statement which can not be checked, either because the information is not available or because it is too time-consuming to check." +- color: '#cef3f7' + name: OTHER + description: "Other: Any other type of mistake." +extra_args: {} \ No newline at end of file diff --git a/factgenie/config/llm-eval/example-vllm-llama3-eval.yaml b/factgenie/config/llm-eval/example-vllm-llama3-eval.yaml new file mode 100644 index 00000000..b46703b6 --- /dev/null +++ b/factgenie/config/llm-eval/example-vllm-llama3-eval.yaml @@ -0,0 +1,59 @@ +type: vllm_metric +model: meta-llama/Meta-Llama-3-8B-Instruct +# You can run vllm also on other machine than factgenie +# e.g. we run it on a machine tdll-3gpu3 and access it from any machine which is withing the same firewall +# in that case we use api_url: http://tdll-3gpu3.ufal.hide.ms.mff.cuni.cz:8000/v1/ +# If you run vllm at the same machine as factgenie let's use just localhost. +api_url: http://localhost:8000/v1/ +model_args: + num_predict: 1024 + temperature: 0.0 + top_p: 1.0 + top_k: 0.0 + seed: 42 +annotation_span_categories: + - name: "Incorrect" + color: "#ffbcbc" + description: "The fact in the text contradicts the data." + - name: "Not checkable" + color: "#e9d2ff" + description: "The fact in the text cannot be checked given the data." + - name: "Misleading" + color: "#fff79f" + description: "The fact in the text is misleading in the given context." + - name: "Other" + color: "#bbbbbb" + description: "The text is problematic for another reason, e.g. grammatically or stylistically incorrect, irrelevant, or repetitive." +prompt_template: | + Given the data: + ``` + {data} + ``` + Annotate all the errors in the following text: + ``` + {text} + ``` + Output the errors as a JSON list "annotations" in which each object contains fields "reason", "text", and "annotation_type". The value of "text" is the text of the error. The value of "reason" is the reason for the error. The value of "annotation_type" is one of {0, 1, 2, 3} based on the following list: + - 0: Incorrect fact: The fact in the text contradicts the data. + - 1: Not checkable: The fact in the text cannot be checked in the data. + - 2: Misleading: The fact in the text is misleading in the given context. + - 3: Other: The text is problematic for another reason, e.g. grammatically or stylistically incorrect, irrelevant, or repetitive. + + The list should be sorted by the position of the error in the text. Make sure that the annotations are not overlapping. + + *Example:* + data: + ``` + Nokia 3310 + ----- + - **color**: black, blue, grey + - **display**: 320x240px + ``` + text (product description): + ``` + Nokia 3310 is produced in Finland and features a 320x320 display. It is available in black color. The data seem to provide only partial information about the phone. + ``` + output: + ```{ "annotations": [{"reason": "The country where the phone is produced is not mentioned in the data.", "text": "produced in Finland", "annotation_type": 1}, {"reason": "The data mentions that the display has resolution 320x240px.", "text": "320x320", "annotation_type": 0}, {"reason": "Misleadingly suggests that the phone is not available in other colors.", "text": "available in black color", "annotation_type": 2}, {"reason": "The note is irrelevant for the phone description.", "text": "The data seem to provide only partial information about the phone.", "annotation_type": 3}] } + ``` + Note that some details may not be mentioned in the text: do not count omissions as errors. Also do not be too strict: some facts can be less specific than in the data (rounded values, shortened or abbreviated text, etc.), do not count these as errors. If there are no errors in the text, "annotations" will be an empty list. diff --git a/factgenie/config/llm-gen/ollama-d2t.yaml b/factgenie/config/llm-gen/example-ollama-d2t.yaml similarity index 100% rename from factgenie/config/llm-gen/ollama-d2t.yaml rename to factgenie/config/llm-gen/example-ollama-d2t.yaml diff --git a/factgenie/config/llm-gen/ollama-summarization.yaml b/factgenie/config/llm-gen/example-ollama-summarization.yaml similarity index 100% rename from factgenie/config/llm-gen/ollama-summarization.yaml rename to factgenie/config/llm-gen/example-ollama-summarization.yaml diff --git a/factgenie/config/llm-gen/openai-d2t.yaml b/factgenie/config/llm-gen/example-openai-d2t.yaml similarity index 100% rename from factgenie/config/llm-gen/openai-d2t.yaml rename to factgenie/config/llm-gen/example-openai-d2t.yaml diff --git a/factgenie/config/llm-gen/openai-summarization.yaml b/factgenie/config/llm-gen/example-openai-summarization.yaml similarity index 100% rename from factgenie/config/llm-gen/openai-summarization.yaml rename to factgenie/config/llm-gen/example-openai-summarization.yaml diff --git a/factgenie/config/llm-gen/example-tutorial.yaml b/factgenie/config/llm-gen/example-tutorial.yaml new file mode 100644 index 00000000..eeea52c4 --- /dev/null +++ b/factgenie/config/llm-gen/example-tutorial.yaml @@ -0,0 +1,24 @@ +type: ollama_gen +model: llama3.1:70b +prompt_template: |- + Given the JSON-structured data about a basketball game: + ``` + {data} + ``` + Generate a one-paragraph basketball summary in natural language. + + Make sure that your report is firmly grounded in the provided data. +system_msg: You are an expert automatic data reporting system. +start_with: |- + Sure, here is the summary: + " +api_url: http://localhost:11434/api/ +model_args: + num_ctx: '16384' + num_predict: '1024' + seed: '42' + temperature: '1.0' + top_k: '50' + top_p: '0.9' +extra_args: + remove_suffix: '"' \ No newline at end of file diff --git a/factgenie/config/resources.yml b/factgenie/config/resources.yml index 9c8a9113..5bc59079 100644 --- a/factgenie/config/resources.yml +++ b/factgenie/config/resources.yml @@ -1,5 +1,6 @@ easy-vqa: class: basic.HTMLDataset + name: easy-vqa data-link: https://owncloud.cesnet.cz/index.php/s/3JbbbAxSqX8PSNJ/download description: A toy visual question answering dataset. Provided as an example dataset loaded by the basic.HTMLDataset class. @@ -8,6 +9,7 @@ easy-vqa: - test logicnlg-100: class: logicnlg.LogicNLG + name: logicnlg-100 description: Sample of 100 examples from the LogicNLG dataset. Provided as an example dataset loaded from Huggingface, including post-processing with custom outputs. @@ -17,6 +19,7 @@ logicnlg-100: splits: - test quintd1-gsmarena: + name: quintd1-gsmarena annotations: - quintd1-gpt-4 - quintd1-human @@ -33,7 +36,8 @@ quintd1-gsmarena: splits: - dev - test -quintd1-ice_hockey: +quintd1-ice-hockey: + name: quintd1-ice-hockey annotations: - quintd1-gpt-4 - quintd1-human @@ -51,6 +55,7 @@ quintd1-ice_hockey: - dev - test quintd1-openweather: + name: quintd1-openweather annotations: - quintd1-gpt-4 - quintd1-human @@ -63,11 +68,12 @@ quintd1-openweather: - mistral - zephyr - gpt-3.5 - source: https://github.com/kasnerz/quintd/tree/main/data/quintd-1 + source: https://gitQuintd1-hub.com/kasnerz/quintd/tree/main/data/quintd-1 splits: - dev - test quintd1-owid: + name: quintd1-owid annotations: - quintd1-gpt-4 - quintd1-human @@ -86,6 +92,7 @@ quintd1-owid: - dev - test quintd1-wikidata: + name: quintd1-wikidata annotations: - quintd1-gpt-4 - quintd1-human @@ -104,6 +111,7 @@ quintd1-wikidata: - dev - test xsum-debug: + name: xsum-debug class: basic.PlainTextDataset data-link: https://owncloud.cesnet.cz/index.php/s/W3eYWbEfCyKqHEl/download description: Sample of 5 examples from the XSum diff --git a/factgenie/crowdsourcing.py b/factgenie/crowdsourcing.py new file mode 100644 index 00000000..21f7cf30 --- /dev/null +++ b/factgenie/crowdsourcing.py @@ -0,0 +1,433 @@ +#!/usr/bin/env python3 + +import datetime +import json +import shutil +import random +import time +import logging +import pandas as pd +import os +import markdown + +from flask import jsonify +from jinja2 import Template +import factgenie.utils as utils +import factgenie.workflows as workflows +from factgenie import CAMPAIGN_DIR, PREVIEW_STUDY_ID, TEMPLATES_DIR +from factgenie.campaign import CampaignMode, ExampleStatus + +logger = logging.getLogger(__name__) + + +def create_crowdsourcing_campaign(app, campaign_id, config, campaign_data): + # create a new directory + if os.path.exists(os.path.join(CAMPAIGN_DIR, campaign_id)): + return jsonify({"error": "Campaign already exists"}) + + try: + os.makedirs(os.path.join(CAMPAIGN_DIR, campaign_id, "files"), exist_ok=True) + + # create the annotation CSV + db = generate_crowdsourcing_campaign_db(app, campaign_data, config=config) + db.to_csv(os.path.join(CAMPAIGN_DIR, campaign_id, "db.csv"), index=False) + + # save metadata + with open(os.path.join(CAMPAIGN_DIR, campaign_id, "metadata.json"), "w") as f: + json.dump( + { + "id": campaign_id, + "mode": CampaignMode.CROWDSOURCING, + "config": config, + "created": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + }, + f, + indent=4, + ) + + # prepare the crowdsourcing HTML page + create_crowdsourcing_page(campaign_id, config) + + workflows.load_campaign(app, campaign_id) + except Exception as e: + # cleanup + shutil.rmtree(os.path.join(CAMPAIGN_DIR, campaign_id)) + raise e + + +def create_crowdsourcing_page(campaign_id, config): + final_page_path = os.path.join(CAMPAIGN_DIR, campaign_id, "pages", "annotate.html") + symlink_path = os.path.join(TEMPLATES_DIR, "campaigns", campaign_id, "annotate.html") + + os.makedirs(os.path.dirname(final_page_path), exist_ok=True) + os.makedirs(os.path.dirname(symlink_path), exist_ok=True) + + # assemble the crowdsourcing page + parts = [] + for part in ["header", "body", "footer"]: + part_path = os.path.join(TEMPLATES_DIR, CampaignMode.CROWDSOURCING, "annotate_{}.html".format(part)) + + with open(part_path, "r") as f: + parts.append(f.read()) + + instructions_html = markdown.markdown(config["annotator_instructions"]) + + # format only the body, keeping the unfilled templates in header and footer + template = Template(parts[1]) + + rendered_content = template.render( + instructions=instructions_html, + annotation_span_categories=config.get("annotation_span_categories", []), + flags=generate_flags(config.get("flags", [])), + options=generate_options(config.get("options", [])), + text_fields=generate_text_fields(config.get("text_fields", [])), + ) + + # concatenate with header and footer + content = parts[0] + rendered_content + parts[2] + + with open(final_page_path, "w") as f: + f.write(content) + + # create a symlink to the page in the templates folder + if os.path.exists(symlink_path): + os.remove(symlink_path) + + os.symlink(final_page_path, symlink_path) + + +def generate_text_fields(text_fields): + if not text_fields: + return "" + + text_fields_segment = "
" + for i, text_field in enumerate(text_fields): + text_fields_segment += f""" +
+ + +
+ """ + text_fields_segment += "
" + return text_fields_segment + + +def generate_options(options): + if not options: + return "" + + options_segment = "
" + for i, option in enumerate(options): + if option["type"] == "select": + options_segment += f""" +
+
+ +
+ """ + elif option["type"] == "slider": + # option["values"] are textual values to be displayed below the slider + options_segment += f""" +
+
+
+ +
+ + """ + for value in option["values"]: + options_segment += f""" + """ + options_segment += """ + +
+ """ + else: + raise ValueError(f"Unknown option type {option['type']}") + + options_segment += """""" + return options_segment + + +def generate_flags(flags): + if not flags: + return "" + + flags_segment = "

Please check if you agree with any of the following statements:

" + for i, flag in enumerate(flags): + flags_segment += f""" +
+ + +
+ """ + flags_segment += "
" + + return flags_segment + + +def generate_crowdsourcing_campaign_db(app, campaign_data, config): + # load all outputs + all_examples = [] + + examples_per_batch = config["examples_per_batch"] + annotators_per_example = config["annotators_per_example"] + sort_order = config["sort_order"] + + for c in campaign_data: + for i in workflows.get_output_ids(app, c["dataset"], c["split"], c["setup_id"]): + all_examples.append( + { + "dataset": c["dataset"], + "split": c["split"], + "example_idx": i, + "setup_id": c["setup_id"], + } + ) + + # deterministic shuffling + random.seed(42) + + # shuffle all examples and setups + if sort_order == "shuffle-all": + random.shuffle(all_examples) + # sort examples by example_idx, shuffle setups + elif sort_order == "sort-example-ids-shuffle-setups": + random.shuffle(all_examples) + all_examples = sorted(all_examples, key=lambda x: (x["example_idx"], x["dataset"], x["split"])) + # sort examples by example_idx, keep the setup order + elif sort_order == "sort-example-ids-keep-setups": + all_examples = sorted(all_examples, key=lambda x: (x["example_idx"], x["dataset"], x["split"])) + # keep all examples and setups in the default order + elif sort_order == "keep-all": + pass + else: + raise ValueError(f"Unknown sort order {sort_order}") + + df = pd.DataFrame.from_records(all_examples) + + # create a column for batch index and assign each example to a batch + df["batch_idx"] = df.index // examples_per_batch + df["annotator_id"] = "" + df["status"] = ExampleStatus.FREE + df["start"] = None + df["end"] = None + + # if we have multiple `annotators_per_example`, repeat the dataframe `annotators_per_example` times, assigning an increasing index of `annotator_group` to each repeating dataframe + if annotators_per_example > 1: + df = pd.concat([df] * annotators_per_example, ignore_index=True) + + df["annotator_group"] = df.index // len(all_examples) + + return df + + +def get_service_ids(service, args): + # we always need to have at least the annotator_id + service_ids = { + "annotator_id": None, + } + if service == "local": + service_ids["annotator_id"] = args.get("annotatorId", PREVIEW_STUDY_ID) + elif service == "prolific": + service_ids["annotator_id"] = args.get("PROLIFIC_PID", PREVIEW_STUDY_ID) + service_ids["session_id"] = args.get("SESSION_ID", PREVIEW_STUDY_ID) + service_ids["study_id"] = args.get("STUDY_ID", PREVIEW_STUDY_ID) + elif service == "mturk": + service_ids["annotator_id"] = args.get("workerId", PREVIEW_STUDY_ID) + service_ids["session_id"] = args.get("assignmentId", PREVIEW_STUDY_ID) + service_ids["study_id"] = args.get("hitId", PREVIEW_STUDY_ID) + else: + raise ValueError(f"Unknown service {service}") + + return service_ids + + +def parse_crowdsourcing_config(config): + # parse Nones or empty strings + examples_per_batch = config.get("examplesPerBatch") + examples_per_batch = int(examples_per_batch) if examples_per_batch else 10 + annotators_per_example = config.get("annotatorsPerExample") + annotators_per_example = int(annotators_per_example) if annotators_per_example else 1 + idle_time = config.get("idleTime") + idle_time = int(idle_time) if idle_time else 120 + config = { + "annotator_instructions": config.get("annotatorInstructions", "No instructions needed :)"), + "final_message": config.get("finalMessage"), + "examples_per_batch": int(examples_per_batch), + "annotators_per_example": int(annotators_per_example), + "idle_time": int(idle_time), + "annotation_granularity": config.get("annotationGranularity"), + "service": config.get("service"), + "sort_order": config.get("sortOrder"), + "annotation_span_categories": config.get("annotationSpanCategories"), + "flags": config.get("flags"), + "options": config.get("options"), + "text_fields": config.get("textFields"), + } + + return config + + +def select_batch(db, seed): + # Choose from the batches with the most available annotators + + free_batches = db[db["status"] == ExampleStatus.FREE] + annotator_groups_per_batch = free_batches.groupby("batch_idx")["annotator_group"].nunique() + eligible_batches = annotator_groups_per_batch[annotator_groups_per_batch == annotator_groups_per_batch.max()] + + eligible_examples = free_batches[free_batches["batch_idx"].isin(eligible_batches.index)] + + # Randomly select an example (with its batch) from the eligible ones + if not eligible_examples.empty: + selected_example = eligible_examples.sample(n=1, random_state=seed).iloc[0] + selected_batch_idx = selected_example["batch_idx"] + + # Get the lowest annotator group for the selected batch which is free + selected_annotator_group = db[(db["batch_idx"] == selected_batch_idx) & (db["status"] == ExampleStatus.FREE)][ + "annotator_group" + ].min() + + logging.info(f"Selected batch {selected_batch_idx} (annotator group {selected_annotator_group})") + return selected_batch_idx, selected_annotator_group + else: + raise ValueError("No available batches") + + +def get_examples_for_batch(db, batch_idx, annotator_group): + annotator_batch = [] + + # find all examples for this batch and annotator group + batch_examples = db[(db["batch_idx"] == batch_idx) & (db["annotator_group"] == annotator_group)] + + for _, row in batch_examples.iterrows(): + annotator_batch.append( + { + "dataset": row["dataset"], + "split": row["split"], + "setup_id": row["setup_id"], + "example_idx": row["example_idx"], + "batch_idx": row["batch_idx"], + "annotator_group": row["annotator_group"], + } + ) + + return annotator_batch + + +def get_annotator_batch(app, campaign, service_ids, batch_idx=None): + db = campaign.db + + # simple locking over the CSV file to prevent double writes + with app.db["lock"]: + annotator_id = service_ids["annotator_id"] + + logging.info(f"Acquiring lock for {annotator_id}") + start = int(time.time()) + seed = random.seed(str(start) + str(service_ids.values())) + + if not batch_idx: + # usual case: an annotator opened the annotation page, we need to select the batch + try: + batch_idx, annotator_group = select_batch(db, seed) + except ValueError: + # no available batches + return [] + else: + # preview mode + batch_idx = int(batch_idx) + annotator_group = 0 + + mask = (db["batch_idx"] == batch_idx) & (db["annotator_group"] == annotator_group) + + # we do not block the example if we are in preview mode + if annotator_id != PREVIEW_STUDY_ID: + db.loc[mask, "status"] = ExampleStatus.ASSIGNED + + db.loc[mask, "start"] = start + db.loc[mask, "annotator_id"] = annotator_id + campaign.update_db(db) + + annotator_batch = get_examples_for_batch(db, batch_idx, annotator_group) + logging.info(f"Releasing lock for {annotator_id}") + + return annotator_batch + + +def save_annotations(app, campaign_id, annotation_set, annotator_id): + now = int(time.time()) + + save_dir = os.path.join(CAMPAIGN_DIR, campaign_id, "files") + os.makedirs(save_dir, exist_ok=True) + campaign = workflows.load_campaign(app, campaign_id=campaign_id) + + with app.db["lock"]: + db = campaign.db + batch_idx = annotation_set[0]["batch_idx"] + annotator_group = annotation_set[0]["annotator_group"] + + # select the examples for this batch and annotator group + mask = (db["batch_idx"] == batch_idx) & (db["annotator_group"] == annotator_group) + + # if the batch is not assigned to this annotator, return an error + batch_annotator_id = db.loc[mask].iloc[0]["annotator_id"] + + if batch_annotator_id != annotator_id and annotator_id != PREVIEW_STUDY_ID: + logger.info( + f"Annotations rejected: batch {batch_idx} in {campaign_id} not assigned to annotator {annotator_id}" + ) + return utils.error(f"Batch not assigned to annotator {annotator_id}") + + # update the db + db.loc[mask, "status"] = ExampleStatus.FINISHED + db.loc[mask, "end"] = now + campaign.update_db(db) + + # save the annotations + for i, ann in enumerate(annotation_set): + row = db.loc[mask].iloc[i] + + # retrieve the related model output to save it with the annotations + output = workflows.get_output_for_setup( + dataset=row["dataset"], + split=row["split"], + setup_id=row["setup_id"], + example_idx=row["example_idx"], + app=app, + force_reload=False, + )["output"] + + res = { + "annotations": ann["annotations"], + "flags": ann["flags"], + "options": ann["options"], + "text_fields": ann["textFields"], + "output": output, + } + # save the record to a JSONL file + workflows.save_record( + mode=CampaignMode.CROWDSOURCING, + campaign=campaign, + row=row, + result=res, + ) + logger.info( + f"Annotations for {campaign_id} (batch {batch_idx}, annotator group {annotator_group}, annotator {annotator_id}) saved." + ) + + final_message_html = markdown.markdown(campaign.metadata["config"]["final_message"]) + + if annotator_id == PREVIEW_STUDY_ID: + preview_message = f'' + + return utils.success(message=final_message_html + preview_message) + + return utils.success(message=final_message_html) diff --git a/factgenie/data/datasets_TEMPLATE.yml b/factgenie/data/datasets_TEMPLATE.yml new file mode 100644 index 00000000..d3e90269 --- /dev/null +++ b/factgenie/data/datasets_TEMPLATE.yml @@ -0,0 +1,14 @@ +# --- + +# If you want to start adding local datasets manually, rename this template file to `datasets.yml` and modify the example record. + +# A dataset record looks like the following: +example-dataset-id: + class: module.Class # expected to be located in the `factgenie/datasets` folder + description: 'Description of the dataset (you can use HTML tags)' + enabled: true + splits: + - list + - of + - dataset + - splits \ No newline at end of file diff --git a/factgenie/data/inputs/.gitignore b/factgenie/data/inputs/.gitignore new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/factgenie/data/inputs/.gitignore @@ -0,0 +1 @@ +* diff --git a/factgenie/data/outputs/.gitignore b/factgenie/data/outputs/.gitignore new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/factgenie/data/outputs/.gitignore @@ -0,0 +1 @@ +* diff --git a/factgenie/loaders/__init__.py b/factgenie/datasets/__init__.py similarity index 78% rename from factgenie/loaders/__init__.py rename to factgenie/datasets/__init__.py index e8c8f0a6..c9c40734 100644 --- a/factgenie/loaders/__init__.py +++ b/factgenie/datasets/__init__.py @@ -9,4 +9,4 @@ ] for mod in modules: - module = importlib.import_module(f"factgenie.loaders.{mod}") + module = importlib.import_module(f"factgenie.datasets.{mod}") diff --git a/factgenie/loaders/basic.py b/factgenie/datasets/basic.py old mode 100755 new mode 100644 similarity index 99% rename from factgenie/loaders/basic.py rename to factgenie/datasets/basic.py index 42279733..f9c053e3 --- a/factgenie/loaders/basic.py +++ b/factgenie/datasets/basic.py @@ -3,7 +3,7 @@ logger = logging.getLogger(__name__) -from factgenie.loaders.dataset import Dataset +from factgenie.datasets.dataset import Dataset from pathlib import Path from natsort import natsorted import requests diff --git a/factgenie/loaders/dataset.py b/factgenie/datasets/dataset.py old mode 100755 new mode 100644 similarity index 58% rename from factgenie/loaders/dataset.py rename to factgenie/datasets/dataset.py index 2c8f19c7..34444678 --- a/factgenie/loaders/dataset.py +++ b/factgenie/datasets/dataset.py @@ -13,13 +13,13 @@ from slugify import slugify from abc import ABC, abstractmethod -from factgenie import DATA_DIR, OUTPUT_DIR +from factgenie import INPUT_DIR, OUTPUT_DIR logger = logging.getLogger(__name__) def get_dataset_classes(): - module_name = "factgenie.loaders" + module_name = "factgenie.datasets" module = importlib.import_module(module_name) classes = {} @@ -43,8 +43,7 @@ class Dataset(ABC): def __init__(self, dataset_id, **kwargs): self.id = dataset_id - self.data_path = DATA_DIR / self.id - self.output_path = OUTPUT_DIR / self.id + self.data_path = INPUT_DIR / self.id self.splits = kwargs.get("splits", ["train", "dev", "test"]) self.description = kwargs.get("description", "") @@ -58,9 +57,6 @@ def __init__(self, dataset_id, **kwargs): self.examples[split] = examples - # load outputs - self.outputs = self.load_generated_outputs(self.output_path) - # -------------------------------- # TODO: implement in subclasses # -------------------------------- @@ -142,50 +138,6 @@ def download( # end TODO # -------------------------------- - def load_generated_outputs(self, output_path): - """ - Load the generated outputs for the dataset. - - Parameters - ---------- - output_path : str - Path to the output directory. - - Returns - ------- - outputs : dict - Dictionary with the generated outputs for each split and setup, e.g. {"train": {"setup1": output1, "setup2": output2}, "test": {"setup1": output1, "setup2": output2}}. - """ - outputs = defaultdict(dict) - - # find recursively all JSONL files in the output directory - outs = list(Path(output_path).glob("**/*.jsonl")) - - for out in outs: - with open(out) as f: - for line_num, line in enumerate(f): - try: - j = json.loads(line) - - j["setup_id"] = slugify(j["setup_id"]) - - split = j["split"] - setup_id = j["setup_id"] - example_idx = j["example_idx"] - - if split not in outputs: - outputs[split] = {} - - if setup_id not in outputs[split]: - outputs[split][setup_id] = {} - - outputs[split][setup_id][example_idx] = j - except Exception as e: - logger.error(f"Error parsing output file {out} at line {line_num + 1}:\n\t{e}") - raise e - - return outputs - def postprocess_data(self, examples): """ Postprocess the data after loading. @@ -197,43 +149,6 @@ def postprocess_data(self, examples): """ return examples - def get_outputs_for_split(self, split): - """ - Get the list of generated outputs for the given split. - """ - return self.outputs[split] - - def get_output_for_idx_by_setup(self, split, output_idx, setup_id): - """ - Get the generated output for the given split, output index, and setup ID. - """ - if setup_id in self.outputs[split]: - model_out = self.outputs[split][setup_id] - - if output_idx in model_out: - return model_out[output_idx]["out"] - - logger.warning(f"No output found for {setup_id=}, {output_idx=}, {split=}") - return None - - def get_output_ids(self, split, setup_id): - """ - Get the list of IDs of existing outputs for the given split. - """ - return list(self.outputs[split][setup_id].keys()) - - def get_outputs_for_idx(self, split, output_idx): - """ - Get the generated outputs for the given split and output index. - """ - outs_all = [] - - for outs in self.outputs[split].values(): - if output_idx in outs: - outs_all.append(outs[output_idx]) - - return outs_all - def get_example(self, split, example_idx): """ Get the example at the given index for the given split. @@ -262,15 +177,3 @@ def get_description(self): Get the string with description of the dataset. """ return self.description - - def get_type(self): - """ - Get the type of the dataset displayed in the web interface. - - Returns - ------- - type : str - Type of the dataset: {"default", "json", "text", "table"} - """ - - return self.type diff --git a/factgenie/loaders/gsmarena.py b/factgenie/datasets/gsmarena.py similarity index 94% rename from factgenie/loaders/gsmarena.py rename to factgenie/datasets/gsmarena.py index 07d3ff3f..de684927 100644 --- a/factgenie/loaders/gsmarena.py +++ b/factgenie/datasets/gsmarena.py @@ -4,8 +4,8 @@ logger = logging.getLogger(__name__) -from factgenie.loaders.basic import JSONDataset -from factgenie.loaders.quintd import QuintdDataset +from factgenie.datasets.basic import JSONDataset +from factgenie.datasets.quintd import QuintdDataset from tinyhtml import h diff --git a/factgenie/loaders/hf_dataset.py b/factgenie/datasets/hf_dataset.py similarity index 96% rename from factgenie/loaders/hf_dataset.py rename to factgenie/datasets/hf_dataset.py index 7f3f5581..7fe407c9 100644 --- a/factgenie/loaders/hf_dataset.py +++ b/factgenie/datasets/hf_dataset.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 import logging import os - from datasets import load_dataset -from factgenie.loaders.dataset import Dataset + +from factgenie.datasets.dataset import Dataset logger = logging.getLogger(__name__) diff --git a/factgenie/loaders/ice_hockey.py b/factgenie/datasets/ice_hockey.py similarity index 97% rename from factgenie/loaders/ice_hockey.py rename to factgenie/datasets/ice_hockey.py index e1f6f25a..fe2b83d3 100644 --- a/factgenie/loaders/ice_hockey.py +++ b/factgenie/datasets/ice_hockey.py @@ -5,8 +5,8 @@ from datetime import datetime from tinyhtml import h -from factgenie.loaders.basic import JSONDataset -from factgenie.loaders.quintd import QuintdDataset +from factgenie.datasets.basic import JSONDataset +from factgenie.datasets.quintd import QuintdDataset import json from pathlib import Path diff --git a/factgenie/loaders/logicnlg.py b/factgenie/datasets/logicnlg.py similarity index 84% rename from factgenie/loaders/logicnlg.py rename to factgenie/datasets/logicnlg.py index b2628cf0..08b556ea 100644 --- a/factgenie/loaders/logicnlg.py +++ b/factgenie/datasets/logicnlg.py @@ -6,7 +6,7 @@ import requests logger = logging.getLogger(__name__) -from factgenie.loaders.hf_dataset import HFDataset +from factgenie.datasets.hf_dataset import HFDataset from tinyhtml import h @@ -29,26 +29,31 @@ def download(cls, dataset_id, data_download_dir, out_download_dir, splits, outpu output_url = "https://owncloud.cesnet.cz/index.php/s/8SFNAjr1g3AbUcm/download" setup_id = "gpt4-direct-2shotcot" - out_path = out_download_dir / split / setup_id - os.makedirs(out_path / "files", exist_ok=True) + out_path = out_download_dir / setup_id + os.makedirs(out_path / setup_id, exist_ok=True) logger.info(f"Downloading {output_url}") response = requests.get(output_url) out_content = json.loads(response.content) - with open(out_path / "files" / f"{setup_id}.jsonl", "w") as f: + with open(out_path / f"{setup_id}.jsonl", "w") as f: for i, out in enumerate(out_content["generated"]): out["dataset"] = dataset_id out["split"] = split out["setup_id"] = setup_id out["example_idx"] = i - f.write(json.dumps(out) + "\n") - with open(out_path / "metadata.json", "w") as f: - json.dump(out_content["setup"], f, indent=4) + out["metadata"] = { + "table_id": out.pop("table_id"), + "claims": out.pop("claims"), + "claim_ids": out.pop("claims_ids"), + } + out["output"] = out.pop("out") + + f.write(json.dumps(out) + "\n") # filter the examples for our subset of tables - table_ids = [out["table_id"] for out in out_content["generated"]] + table_ids = [out["metadata"]["table_id"] for out in out_content["generated"]] tables = {} with open(data_download_dir / split / "dataset.jsonl") as f: diff --git a/factgenie/loaders/openweather.py b/factgenie/datasets/openweather.py similarity index 96% rename from factgenie/loaders/openweather.py rename to factgenie/datasets/openweather.py index 123c36c7..8bda7d03 100644 --- a/factgenie/loaders/openweather.py +++ b/factgenie/datasets/openweather.py @@ -4,8 +4,8 @@ logger = logging.getLogger(__name__) import json -from factgenie.loaders.basic import JSONDataset -from factgenie.loaders.quintd import QuintdDataset +from factgenie.datasets.basic import JSONDataset +from factgenie.datasets.quintd import QuintdDataset import dateutil.parser from datetime import datetime diff --git a/factgenie/loaders/owid.py b/factgenie/datasets/owid.py similarity index 97% rename from factgenie/loaders/owid.py rename to factgenie/datasets/owid.py index f48f06d9..ed4967f5 100644 --- a/factgenie/loaders/owid.py +++ b/factgenie/datasets/owid.py @@ -4,8 +4,8 @@ logger = logging.getLogger(__name__) import dateutil.parser -from factgenie.loaders.dataset import Dataset -from factgenie.loaders.quintd import QuintdDataset +from factgenie.datasets.dataset import Dataset +from factgenie.datasets.quintd import QuintdDataset from pathlib import Path import json diff --git a/factgenie/datasets/quintd.py b/factgenie/datasets/quintd.py new file mode 100644 index 00000000..5eeba5db --- /dev/null +++ b/factgenie/datasets/quintd.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +import logging +import requests +import json +import traceback +import zipfile +import os +from slugify import slugify +from factgenie.datasets.dataset import Dataset +from factgenie.utils import resumable_download +from pathlib import Path + +logger = logging.getLogger(__name__) + + +class QuintdDataset(Dataset): + @classmethod + def download_dataset( + cls, + dataset_id, + data_download_dir, + splits, + ): + if dataset_id == "owid": + # we need to download a zip file and unpack it + data_url = "https://github.com/kasnerz/quintd/raw/refs/heads/main/data/quintd-1/data/owid/owid.zip" + + resumable_download(url=data_url, filename=f"{data_download_dir}/owid.zip", force_download=True) + logger.info(f"Downloaded owid.zip") + + with zipfile.ZipFile(f"{data_download_dir}/owid.zip", "r") as zip_ref: + zip_ref.extractall(data_download_dir) + + os.remove(f"{data_download_dir}/owid.zip") + logger.info(f"Unpacked owid.zip") + + else: + data_url = "https://raw.githubusercontent.com/kasnerz/quintd/refs/heads/main/data/quintd-1/data/{dataset_id}/{split}.json" + + for split in splits: + resumable_download( + url=data_url.format(dataset_id=dataset_id.replace("-", "_"), split=split), + filename=f"{data_download_dir}/{split}.json", + force_download=False, + ) + + logger.info(f"Downloaded {split}.json") + + @classmethod + def download_outputs(cls, dataset_id, out_download_dir, splits, outputs): + extra_ids = {"dev": "direct", "test": "default"} + output_url = "https://raw.githubusercontent.com/kasnerz/quintd/refs/heads/main/data/quintd-1/outputs/{split}/{dataset_id}/{extra_id}/{setup_id}.json" + + for split in splits: + for setup_id in outputs: + if split == "dev" and setup_id == "gpt-3.5": + # we do not have these outputs + continue + + extra_id = extra_ids[split] + try: + url = output_url.format( + dataset_id=dataset_id.replace("-", "_"), split=split, extra_id=extra_id, setup_id=setup_id + ) + + j = json.loads(requests.get(url).content) + + metadata = j["setup"] + metadata["model_args"] = metadata.pop("params") + metadata["prompt_template"] = metadata.pop("prompt") + + os.makedirs(out_download_dir / setup_id, exist_ok=True) + + with open(out_download_dir / setup_id / f"{dataset_id}-{split}.jsonl", "w") as f: + for i, gen in enumerate(j["generated"]): + record = { + "dataset": "quintd1-" + dataset_id, + "split": split, + "setup_id": setup_id, + "example_idx": i, + "metadata": metadata, + "output": gen["out"], + } + record["metadata"]["prompt"] = gen["in"] + f.write(json.dumps(record) + "\n") + + logger.info(f"Downloaded outputs for {split}/{setup_id}") + except Exception as e: + traceback.print_exc() + logger.warning(f"Could not download output file '{split}/{setup_id}.json'") + + @classmethod + def download_annotations(cls, dataset_id, annotation_download_dir, splits): + urls = { + "quintd1-gpt-4": "https://owncloud.cesnet.cz/index.php/s/qC6NkAhU5M9Ox9U/download", + "quintd1-human": "https://owncloud.cesnet.cz/index.php/s/IlTdmuhOzxWqEK2/download", + } + + for campaign_id, url in urls.items(): + if not os.path.exists(f"{annotation_download_dir}/{campaign_id}"): + os.makedirs(f"{annotation_download_dir}/{campaign_id}") + + resumable_download( + url=url, filename=f"{annotation_download_dir}/{campaign_id}.zip", force_download=False + ) + logger.info(f"Downloaded {campaign_id}.zip") + + with zipfile.ZipFile(f"{annotation_download_dir}/{campaign_id}.zip", "r") as zip_ref: + zip_ref.extractall(f"{annotation_download_dir}/{campaign_id}") + + # open campaign_id/metadata.json and change the key `source` to `mode` + with open(f"{annotation_download_dir}/{campaign_id}/metadata.json", "r") as f: + metadata = json.load(f) + + metadata["mode"] = metadata.pop("source") + + with open(f"{annotation_download_dir}/{campaign_id}/metadata.json", "w") as f: + json.dump(metadata, f, indent=4) + + # replace gpt-35 with gpt-3-5 (correctly slugified version) in all files + for file in Path(f"{annotation_download_dir}/{campaign_id}/files").rglob("*.jsonl"): + with open(file, "r") as f: + lines = f.readlines() + + with open(file, "w") as f: + for line in lines: + line = line.replace("gpt-35", "gpt-3-5") + j = json.loads(line) + + record_metadata = metadata["config"].copy() + record_metadata["campaign_id"] = campaign_id + record_metadata["annotator_id"] = j.pop("annotator_id") + record_metadata["annotator_group"] = 0 + + j["metadata"] = record_metadata + + f.write(json.dumps(j) + "\n") + + os.remove(f"{annotation_download_dir}/{campaign_id}.zip") + + else: + logger.info(f"Annotations {campaign_id} already downloaded, skipping") + + @classmethod + def download( + cls, + dataset_id, + data_download_dir, + out_download_dir, + annotation_download_dir, + splits, + outputs, + dataset_config, + **kwargs, + ): + dataset_id = dataset_id.removeprefix("quintd1-") + + cls.download_dataset(dataset_id, data_download_dir, splits) + cls.download_outputs(dataset_id, out_download_dir, splits, outputs) + cls.download_annotations(dataset_id, annotation_download_dir, splits) diff --git a/factgenie/datasets/rotowire_shared_task.py b/factgenie/datasets/rotowire_shared_task.py new file mode 100644 index 00000000..3ab808da --- /dev/null +++ b/factgenie/datasets/rotowire_shared_task.py @@ -0,0 +1,119 @@ +from factgenie.datasets.dataset import Dataset +import json +import markdown +import textwrap + + +class RotowireSharedTask(Dataset): + def load_examples(self, split, data_path): + examples = [] + + with open(f"{data_path}/{split}.jsonl") as f: + lines = f.readlines() + for line in lines: + j = json.loads(line) + summary = self.json_to_markdown_tables(data=j) + examples.append(summary) + + return examples + + def render(self, example): + html = markdown.markdown(example, extensions=["markdown.extensions.tables"]) + html = html.replace("", '
') + html = self.add_explanations(html) + + return html + + def create_game_summary_table(self, data): + home_team = f"{data['home_city']} {data['home_name']}" + away_team = f"{data['vis_city']} {data['vis_name']}" + + summary_table = textwrap.dedent( + f"""\ + #### Game Summary: {away_team} @ {home_team} + | Team | Quarter 1 | Quarter 2 | Quarter 3 | Quarter 4 | Final | + | ----------- | ------------------------------------ | ------------------------------------ | ------------------------------------ | ------------------------------------ | ------------------------------- | + | {away_team} | {data['vis_line']['TEAM-PTS_QTR1']} | {data['vis_line']['TEAM-PTS_QTR2']} | {data['vis_line']['TEAM-PTS_QTR3']} | {data['vis_line']['TEAM-PTS_QTR4']} | {data['vis_line']['TEAM-PTS']} | + | {home_team} | {data['home_line']['TEAM-PTS_QTR1']} | {data['home_line']['TEAM-PTS_QTR2']} | {data['home_line']['TEAM-PTS_QTR3']} | {data['home_line']['TEAM-PTS_QTR4']} | {data['home_line']['TEAM-PTS']} | + """ + ) + return summary_table + + def create_team_stats_table(self, data): + home_team = f"{data['home_city']} {data['home_name']}" + away_team = f"{data['vis_city']} {data['vis_name']}" + + team_stats_table = textwrap.dedent( + f"""\ + #### Team Statistics + | Statistic | {away_team} | {home_team} | + | ---------------------- | ----------------------------------- | ------------------------------------ | + | Field Goal Percentage | {data['vis_line']['TEAM-FG_PCT']}% | {data['home_line']['TEAM-FG_PCT']}% | + | Three Point Percentage | {data['vis_line']['TEAM-FG3_PCT']}% | {data['home_line']['TEAM-FG3_PCT']}% | + | Free Throw Percentage | {data['vis_line']['TEAM-FT_PCT']}% | {data['home_line']['TEAM-FT_PCT']}% | + | Rebounds | {data['vis_line']['TEAM-REB']} | {data['home_line']['TEAM-REB']} | + | Assists | {data['vis_line']['TEAM-AST']} | {data['home_line']['TEAM-AST']} | + | Turnovers | {data['vis_line']['TEAM-TOV']} | {data['home_line']['TEAM-TOV']} | + """ + ) + return team_stats_table + + def create_player_stats_tables(self, data): + def create_single_team_table(team_city, box_score): + table = textwrap.dedent( + f"""\ + #### {team_city} Player Statistics + | Player | Minutes | Points | Rebounds | Assists | Field Goals | Three Pointers | Free Throws | Steals | Blocks | Turnovers | + | ------ | ------- | ------ | -------- | ------- | ----------- | -------------- | ----------- | ------ | ------ | --------- |\n""" + ) + + for pid in box_score["PLAYER_NAME"].keys(): + if box_score["TEAM_CITY"][pid] == team_city and box_score["MIN"][pid] != "N/A": + + name = f"{box_score['FIRST_NAME'][pid]} {box_score['SECOND_NAME'][pid]}" + fg = f"{box_score['FGM'][pid]}/{box_score['FGA'][pid]}" + tpt = f"{box_score['FG3M'][pid]}/{box_score['FG3A'][pid]}" + ft = f"{box_score['FTM'][pid]}/{box_score['FTA'][pid]}" + + table += f"| {name} | {box_score['MIN'][pid]} | {box_score['PTS'][pid]} | " + table += f"{box_score['REB'][pid]} | {box_score['AST'][pid]} | " + table += f"{fg} | {tpt} | {ft} | " + table += f"{box_score['STL'][pid]} | {box_score['BLK'][pid]} | {box_score['TO'][pid]} |\n" + + return table + + home_table = create_single_team_table(data["home_city"], data["box_score"]) + away_table = create_single_team_table(data["vis_city"], data["box_score"]) + + return f"{home_table}\n{away_table}" + + def json_to_markdown_tables(self, data): + markdown = f"## NBA Game Report - {data['day']}\n\n" + markdown += self.create_game_summary_table(data) + markdown += "\n" + markdown += self.create_team_stats_table(data) + markdown += "\n" + markdown += self.create_player_stats_tables(data) + return markdown + + def add_explanations(self, html): + abbr_mappings = { + "Minutes": "The number of minutes played", + "Points": "Total points scored", + "Rebounds": "Total rebounds (offensive + defensive)", + "Assists": "Passes that directly lead to a made basket", + "Field Goals": "Shows makes/attempts for all shots except free throws", + "Three Pointers": "Shows makes/attempts for shots beyond the three-point line", + "Free Throws": "Shows makes/attempts for uncontested shots awarded after a foul", + "Steals": "Number of times the player took the ball from the opposing team", + "Blocks": "Number of opponents' shots that were blocked", + "Turnovers": "Number of times the player lost the ball to the opposing team", + "Field Goal Percentage": "The percentage of shots made (excluding free throws)", + "Three Point Percentage": "The percentage of three-point shots made", + "Free Throw Percentage": "The percentage of free throws made", + } + + for term, explanation in abbr_mappings.items(): + html = html.replace(term, f'{term}') + + return html diff --git a/factgenie/loaders/wikidata.py b/factgenie/datasets/wikidata.py similarity index 92% rename from factgenie/loaders/wikidata.py rename to factgenie/datasets/wikidata.py index 3d09e8ce..64f173de 100644 --- a/factgenie/loaders/wikidata.py +++ b/factgenie/datasets/wikidata.py @@ -2,8 +2,8 @@ import logging logger = logging.getLogger(__name__) -from factgenie.loaders.basic import JSONDataset -from factgenie.loaders.quintd import QuintdDataset +from factgenie.datasets.basic import JSONDataset +from factgenie.datasets.quintd import QuintdDataset from tinyhtml import h diff --git a/factgenie/llm_campaign.py b/factgenie/llm_campaign.py new file mode 100644 index 00000000..2d71b60e --- /dev/null +++ b/factgenie/llm_campaign.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python3 + +import pandas as pd +import datetime +import shutil +import time +import json +import os +import ast +import logging +import traceback +import requests +import urllib3 + +from slugify import slugify + +from factgenie.campaign import CampaignMode, CampaignStatus, ExampleStatus +from flask import jsonify +import factgenie.utils as utils +import factgenie.workflows as workflows + +from factgenie import CAMPAIGN_DIR, OUTPUT_DIR, TEMPLATES_DIR + +logger = logging.getLogger(__name__) + + +def create_llm_campaign(app, mode, campaign_id, config, campaign_data, datasets, overwrite=False): + campaign_id = slugify(campaign_id) + + # create a new directory + if os.path.exists(os.path.join(CAMPAIGN_DIR, campaign_id)): + if not overwrite: + raise ValueError(f"Campaign {campaign_id} already exists") + else: + shutil.rmtree(os.path.join(CAMPAIGN_DIR, campaign_id)) + + try: + os.makedirs(os.path.join(CAMPAIGN_DIR, campaign_id, "files"), exist_ok=True) + + # create the annotation CSV + db = generate_llm_campaign_db(app, mode, datasets, campaign_id, campaign_data) + db_path = os.path.join(CAMPAIGN_DIR, campaign_id, "db.csv") + logger.info(f"DB with {len(db)} free examples created for {campaign_id} at {db_path}") + db.to_csv(db_path, index=False) + + # save metadata + metadata_path = os.path.join(CAMPAIGN_DIR, campaign_id, "metadata.json") + logger.info(f"Metadata for {campaign_id} saved at {metadata_path}") + with open(metadata_path, "w") as f: + json.dump( + { + "id": campaign_id, + "created": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + "mode": mode, + "status": CampaignStatus.IDLE, + "config": config, + }, + f, + indent=4, + ) + except Exception as e: + # cleanup + shutil.rmtree(os.path.join(CAMPAIGN_DIR, campaign_id)) + os.remove(os.path.join(OUTPUT_DIR, campaign_id)) + raise e + + +def duplicate_llm_campaign(app, mode, campaign_id, new_campaign_id): + # copy the directory except for the annotations + old_campaign_dir = os.path.join(CAMPAIGN_DIR, campaign_id) + new_campaign_dir = os.path.join(CAMPAIGN_DIR, new_campaign_id) + + # if new campaign dir exists, return error + if os.path.exists(new_campaign_dir): + return utils.error("Campaign already exists") + + shutil.copytree(old_campaign_dir, new_campaign_dir, ignore=shutil.ignore_patterns("files")) + + # copy the db + old_db = pd.read_csv(os.path.join(old_campaign_dir, "db.csv")) + new_db = old_db.copy() + new_db["status"] = ExampleStatus.FREE + + # clean the columns + new_db["annotator_id"] = "" + new_db["start"] = None + new_db["end"] = None + + new_db.to_csv(os.path.join(new_campaign_dir, "db.csv"), index=False) + + # update the metadata + metadata_path = os.path.join(new_campaign_dir, "metadata.json") + with open(metadata_path) as f: + metadata = json.load(f) + + metadata["id"] = new_campaign_id + metadata["created"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + metadata["status"] = CampaignStatus.IDLE + + with open(metadata_path, "w") as f: + json.dump(metadata, f, indent=4) + + # if it is a human campaign, copy also the symlink to the annotate page + if metadata["mode"] == CampaignMode.CROWDSOURCING: + os.makedirs(os.path.join(TEMPLATES_DIR, "campaigns", new_campaign_id), exist_ok=True) + shutil.copy( + os.path.join(TEMPLATES_DIR, "campaigns", campaign_id, "annotate.html"), + os.path.join(TEMPLATES_DIR, "campaigns", new_campaign_id, "annotate.html"), + ) + + return utils.success() + + +def generate_llm_campaign_db(app, mode, datasets, campaign_id, campaign_data): + # load all outputs + all_examples = [] + + for c in campaign_data: + dataset = datasets[c["dataset"]] + + # for llm_gen, setup_id based on the generation model + if mode == CampaignMode.LLM_EVAL: + setup_id = c["setup_id"] + ids = workflows.get_output_ids(app, dataset.id, c["split"], setup_id) + elif mode == CampaignMode.LLM_GEN: + setup_id = campaign_id + ids = list(range(dataset.get_example_count(c["split"]))) + + for i in ids: + record = { + "dataset": c["dataset"], + "split": c["split"], + "example_idx": i, + } + record["setup_id"] = setup_id + + all_examples.append(record) + + df = pd.DataFrame.from_records(all_examples) + + # create a column for batch index and assign each example to a batch + df["annotator_id"] = "" + df["annotator_group"] = 0 + df["status"] = ExampleStatus.FREE + df["start"] = None + df["end"] = None + + return df + + +def run_llm_campaign(app, mode, campaign_id, announcer, campaign, datasets, model, running_campaigns): + db = campaign.db + + # set campaign status to running + campaign.metadata["status"] = CampaignStatus.RUNNING + campaign.metadata["last_run"] = int(time.time()) + campaign.update_metadata() + logger.info(f"Starting LLM campaign {campaign_id}") + + # regenerate output index + workflows.get_output_index(app, force_reload=True) + + # generate outputs / annotations for all free examples in the db + for i, row in db[db.status == ExampleStatus.FREE].iterrows(): + # campaign was paused + if campaign_id not in running_campaigns: + break + + dataset_id = row["dataset"] + split = row["split"] + example_idx = row["example_idx"] + example = datasets[dataset_id].get_example(split, example_idx) + # only for llm_eval + setup_id = row.get("setup_id") + + db.loc[i, "start"] = float(time.time()) + db.loc[i, "annotator_id"] = campaign.metadata["config"]["model"] + "-" + campaign_id + + # generate output or annotate example + try: + if mode == CampaignMode.LLM_EVAL: + generated_output = workflows.get_output_for_setup( + dataset_id, split, example_idx, setup_id, app=app, force_reload=False + ) + res = model.annotate_example(data=example, text=generated_output["output"]) + res["output"] = generated_output["output"] + elif mode == CampaignMode.LLM_GEN: + res = model.generate_output(data=example) + except requests.exceptions.ConnectionError as e: + traceback.print_exc() + return utils.error( + f"Error processing example {dataset_id}-{split}-{example_idx}: {e.__class__.__name__}: {str(e)}\n\n{model.new_connection_error_advice_docstring}\n" + ) + + except Exception as e: + traceback.print_exc() + return utils.error( + f"Error processing example {dataset_id}-{split}-{example_idx}: {e.__class__.__name__}: {str(e)}" + ) + + # update the DB + db.loc[i, "end"] = float(time.time()) + db.loc[i, "status"] = ExampleStatus.FINISHED + + campaign.update_db(db) + + # save the record to a JSONL file + response = workflows.save_record( + mode=mode, + campaign=campaign, + row=db.loc[i], + result=res, + ) + + # send a response to the frontend + stats = campaign.get_stats() + payload = {"campaign_id": campaign_id, "stats": stats, "type": "result", "response": response} + + utils.announce(announcer, payload) + logger.info(f"{campaign_id}: {stats['finished']}/{stats['total']} examples") + + # if all examples are finished, set the campaign status to finished + if len(db.status.unique()) == 1 and db.status.unique()[0] == ExampleStatus.FINISHED: + campaign.metadata["status"] = CampaignStatus.FINISHED + campaign.update_metadata() + + if campaign_id in running_campaigns: + running_campaigns.remove(campaign_id) + + return jsonify(success=True, status=campaign.metadata["status"]) + + +def pause_llm_campaign(app, campaign_id): + if campaign_id in app.db["running_campaigns"]: + app.db["running_campaigns"].remove(campaign_id) + + campaign = workflows.load_campaign(app, campaign_id=campaign_id) + campaign.metadata["status"] = CampaignStatus.IDLE + campaign.update_metadata() + + +def parse_llm_gen_config(config): + config = { + "type": config.get("metricType"), + "model": config.get("modelName"), + "prompt_template": config.get("promptTemplate"), + "system_msg": config.get("systemMessage"), + "start_with": config.get("startWith"), + "api_url": config.get("apiUrl"), + "model_args": config.get("modelArguments"), + "extra_args": config.get("extraArguments"), + } + return config + + +def parse_llm_eval_config(config): + config = { + "type": config.get("metricType"), + "model": config.get("modelName"), + "prompt_template": config.get("promptTemplate"), + "system_msg": config.get("systemMessage"), + "api_url": config.get("apiUrl"), + "model_args": config.get("modelArguments"), + "extra_args": config.get("extraArguments"), + "annotation_span_categories": config.get("annotationSpanCategories"), + } + return config + + +def parse_campaign_config(config): + def parse_value(value): + try: + # Try to parse the value as a literal (int, list, dict, etc.) + parsed_value = ast.literal_eval(value) + return parsed_value + except (ValueError, SyntaxError): + # If parsing fails, return the value as a string + return value + + parsed_config = {key: parse_value(value) for key, value in config.items()} + return parsed_config + + +def save_generation_outputs(app, campaign_id, setup_id): + """ + Load the files from the `GENERATIONS_DIR` and save them in the appropriate subdirectory in `OUTPUT_DIR`. + """ + + campaign = workflows.load_campaign(app, campaign_id) + metadata = campaign.metadata + + # load the metadata + with open(CAMPAIGN_DIR / campaign_id / "metadata.json") as f: + metadata = json.load(f) + + # load the outputs + outputs = [] + + for file in os.listdir(CAMPAIGN_DIR / campaign_id / "files"): + if file.endswith(".jsonl"): + with open(CAMPAIGN_DIR / campaign_id / "files" / file) as f: + for line in f: + record = json.loads(line) + # replace the campaign_id with the desired setup_id + record["setup_id"] = setup_id + + outputs.append(record) + + # save the outputs + path = OUTPUT_DIR / setup_id + os.makedirs(path, exist_ok=True) + + with open(path / f"{setup_id}.jsonl", "w") as f: + for example in outputs: + f.write(json.dumps(example) + "\n") + + return utils.success() diff --git a/factgenie/loaders/quintd.py b/factgenie/loaders/quintd.py deleted file mode 100644 index 77753c4b..00000000 --- a/factgenie/loaders/quintd.py +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env python3 -import logging -import requests -import json -import traceback -import zipfile -import os -from slugify import slugify -from factgenie.loaders.dataset import Dataset -from factgenie.utils import resumable_download -from pathlib import Path - -logger = logging.getLogger(__name__) - - -class QuintdDataset(Dataset): - @classmethod - def download_dataset( - cls, - dataset_id, - data_download_dir, - splits, - ): - if dataset_id == "owid": - # we need to download a zip file and unpack it - data_url = "https://github.com/kasnerz/quintd/raw/refs/heads/main/data/quintd-1/data/owid/owid.zip" - - resumable_download(url=data_url, filename=f"{data_download_dir}/owid.zip", force_download=True) - logger.info(f"Downloaded owid.zip") - - with zipfile.ZipFile(f"{data_download_dir}/owid.zip", "r") as zip_ref: - zip_ref.extractall(data_download_dir) - - os.remove(f"{data_download_dir}/owid.zip") - logger.info(f"Unpacked owid.zip") - - else: - data_url = "https://raw.githubusercontent.com/kasnerz/quintd/refs/heads/main/data/quintd-1/data/{dataset_id}/{split}.json" - - for split in splits: - resumable_download( - url=data_url.format(dataset_id=dataset_id, split=split), - filename=f"{data_download_dir}/{split}.json", - force_download=False, - ) - - logger.info(f"Downloaded {split}.json") - - @classmethod - def download_outputs(cls, dataset_id, out_download_dir, splits, outputs): - extra_ids = {"dev": "direct", "test": "default"} - output_url = "https://raw.githubusercontent.com/kasnerz/quintd/refs/heads/main/data/quintd-1/outputs/{split}/{dataset_id}/{extra_id}/{setup_id}.json" - - for split in splits: - - for setup_id in outputs: - if split == "dev" and setup_id == "gpt-3.5": - # we do not have these outputs - continue - - out_path = Path(f"{out_download_dir}/{split}/{setup_id}") - os.makedirs(out_path / "files", exist_ok=True) - - extra_id = extra_ids[split] - try: - url = output_url.format(dataset_id=dataset_id, split=split, extra_id=extra_id, setup_id=setup_id) - - j = json.loads(requests.get(url).content) - - with open(f"{out_download_dir}/{split}/{setup_id}/metadata.json", "w") as f: - metadata = j["setup"] - metadata["model_args"] = metadata.pop("params") - metadata["prompt_template"] = metadata.pop("prompt") - - json.dump(metadata, f, indent=4, ensure_ascii=False) - - new_out = [] - for i, gen in enumerate(j["generated"]): - record = { - "dataset": dataset_id, - "split": split, - "setup_id": setup_id, - "example_idx": i, - "in": gen["in"], - "out": gen["out"], - } - new_out.append(record) - - with open(out_path / "files" / f"{setup_id}.jsonl", "w") as f: - for gen in new_out: - f.write(json.dumps(gen) + "\n") - - logger.info(f"Downloaded outputs for {split}/{setup_id}") - except Exception as e: - traceback.print_exc() - logger.warning(f"Could not download output file '{split}/{setup_id}.json'") - - @classmethod - def download_annotations(cls, dataset_id, annotation_download_dir, splits): - gpt_4_url = "https://owncloud.cesnet.cz/index.php/s/qC6NkAhU5M9Ox9U/download" - human_url = "https://owncloud.cesnet.cz/index.php/s/IlTdmuhOzxWqEK2/download" - - if not os.path.exists(f"{annotation_download_dir}/quintd1-gpt-4"): - os.makedirs(f"{annotation_download_dir}/quintd1-gpt-4") - - resumable_download( - url=gpt_4_url, filename=f"{annotation_download_dir}/quintd1-gpt-4.zip", force_download=False - ) - logger.info(f"Downloaded quintd1-gpt-4.zip") - - with zipfile.ZipFile(f"{annotation_download_dir}/quintd1-gpt-4.zip", "r") as zip_ref: - zip_ref.extractall(f"{annotation_download_dir}/quintd1-gpt-4") - - os.remove(f"{annotation_download_dir}/quintd1-gpt-4.zip") - else: - logger.info(f"Annotations quintd1-gpt-4 already downloaded, skipping") - - if not os.path.exists(f"{annotation_download_dir}/quintd1-human"): - os.makedirs(f"{annotation_download_dir}/quintd1-human") - - resumable_download( - url=human_url, filename=f"{annotation_download_dir}/quintd1-human.zip", force_download=False - ) - logger.info(f"Downloaded quintd1-human.zip") - - with zipfile.ZipFile(f"{annotation_download_dir}/quintd1-human.zip", "r") as zip_ref: - zip_ref.extractall(f"{annotation_download_dir}/quintd1-human") - - os.remove(f"{annotation_download_dir}/quintd1-human.zip") - else: - logger.info(f"Annotations quintd1-gpt-4 already downloaded, skipping") - - @classmethod - def download( - cls, - dataset_id, - data_download_dir, - out_download_dir, - annotation_download_dir, - splits, - outputs, - dataset_config, - **kwargs, - ): - dataset_id = dataset_id.removeprefix("quintd1-") - - cls.download_dataset(dataset_id, data_download_dir, splits) - cls.download_outputs(dataset_id, out_download_dir, splits, outputs) - cls.download_annotations(dataset_id, annotation_download_dir, splits) diff --git a/factgenie/models.py b/factgenie/models.py index c3f28646..255d3582 100644 --- a/factgenie/models.py +++ b/factgenie/models.py @@ -1,25 +1,21 @@ #!/usr/bin/env python3 +from abc import abstractmethod import traceback +from typing import Optional from openai import OpenAI from textwrap import dedent -import argparse -import yaml import json -import sys -from pathlib import Path import os -import coloredlogs import logging -import time +from pydantic import BaseModel, Field, ValidationError import requests import copy from ast import literal_eval +from factgenie.campaign import CampaignMode -# logging.basicConfig(format="%(message)s", level=logging.INFO, datefmt="%H:%M:%S") -# coloredlogs.install(level="INFO", fmt="%(asctime)s %(levelname)s %(message)s") logger = logging.getLogger(__name__) DIR_PATH = os.path.dirname(__file__) @@ -33,11 +29,12 @@ class ModelFactory: @staticmethod def model_classes(): return { - "llm_eval": { + CampaignMode.LLM_EVAL: { "openai_metric": OpenAIMetric, "ollama_metric": OllamaMetric, + "vllm_metric": VLLMMetric, }, - "llm_gen": { + CampaignMode.LLM_GEN: { "openai_gen": OpenAIGen, "ollama_gen": OllamaGen, "tgwebui_gen": TextGenerationWebuiGen, @@ -55,15 +52,28 @@ def from_config(config, mode): return classes[metric_type](config) +class SpanAnnotation(BaseModel): + text: str = Field(description="The text which is annotated.") + # Do not name it type since it is a reserved keyword in JSON schema + annotation_type: int = Field( + description="Index to the list of span annotation types defined for the annotation campaign." + ) + reason: str = Field(description="The reason for the annotation.") + + +class OutputAnnotations(BaseModel): + annotations: list[SpanAnnotation] = Field(description="The list of annotations.") + + class Model: def __init__(self, config): self.validate_config(config) self.config = config self.parse_model_args() - if "extra_args" in config: - # the key in the model output that contains the annotations - self.annotation_key = config["extra_args"].get("annotation_key", "annotations") + @property + def new_connection_error_advice_docstring(self): + return """Please check the LLM engine documentation. The call to the LLM API server failed.""" def get_annotator_id(self): return "llm-" + self.config["type"] + "-" + self.config["model"] @@ -123,30 +133,33 @@ def get_optional_fields(self): "extra_args": dict, } - def postprocess_annotations(self, text, model_json): + def parse_annotations(self, text, annotations_json): + try: + annotations_obj = OutputAnnotations.parse_raw(annotations_json) + annotations = annotations_obj.annotations + except ValidationError as e: + logger.error(f"LLM response in not in the expected format: {e}\n\t{annotations_json=}") + annotation_list = [] current_pos = 0 - - if self.annotation_key not in model_json: - logger.error(f"Cannot find the key `{self.annotation_key}` in {model_json=}") - return [] - - for annotation in model_json[self.annotation_key]: + for annotation in annotations: # find the `start` index of the error in the text - start_pos = text.lower().find(annotation["text"].lower(), current_pos) - - if current_pos != 0 and start_pos == -1: - # try from the beginning - start_pos = text.find(annotation["text"]) + start_pos = text.lower().find(annotation.text.lower(), current_pos) if start_pos == -1: logger.warning(f"Cannot find {annotation=} in text {text}, skipping") continue - annotation["start"] = start_pos - annotation_list.append(copy.deepcopy(annotation)) + annotation_d = annotation.dict() + # For backward compatibility let's use shorter "type" + # We do not use the name "type" in JSON schema for error types because it has much broader sense in the schema (e.g. string or integer) + annotation_d["type"] = annotation.annotation_type + del annotation_d["annotation_type"] + # logging where the annotion starts to disambiguate errors on the same string in different places + annotation_d["start"] = start_pos + annotation_list.append(annotation_d) - current_pos = start_pos + len(annotation["text"]) + current_pos = start_pos + len(annotation.text) # does not allow for overlapping annotations return annotation_list @@ -168,13 +181,50 @@ def prompt(self, data, text): return prompt_template.replace("{data}", str(data_for_prompt)).replace("{text}", text) def annotate_example(self, data, text): + """ + Annotate the given text with the model. + + Args: + data: the data to be used in the prompt + text: the text to be annotated + + Returns: + A dictionary: { + "prompt": the prompt used for the annotation, + "annotations": a list of annotations + } + """ raise NotImplementedError("Override this method in the subclass to call the LLM API") -class OpenAIMetric(LLMMetric): - def __init__(self, config): +class OpenAIClientMetric(LLMMetric): + def __init__(self, config, **kwargs): super().__init__(config) - self.client = OpenAI() + self.client = OpenAI(**kwargs) + + config_schema = config.get("extra_args", {}).get("schema", {}) + pydantic_schema = OutputAnnotations.model_json_schema() + if config_schema: + self._schema = config_schema + logger.warning( + f"We expect parsing according to \n{pydantic_schema=}\n but got anoter schema from config\n{config_schema=}" + "\nAdapt parsing accordingly!" + ) + else: + self._schema = pydantic_schema + + # Required for OpenAI API but make sense in general too + # TODO make it more pydantic / Python friendly + self._schema["additionalProperties"] = False + self._schema["$defs"]["Annotation"]["additionalProperties"] = False + + logger.warning(f"The schema is set to\n{self._schema}.\n\tCheck that your prompt is compatible!!! ") + # access the later used config keys early to log them once and test if they are present + logger.info(f"Using {config['model']=} with {config['system_msg']=}") + + @property + def schema(self): + return self._schema def get_required_fields(self): return { @@ -192,29 +242,77 @@ def get_optional_fields(self): "extra_args": dict, # TODO we receive it from the UI, but can be removed } + @abstractmethod + def _prepare_chat_completions_create_args(self): + raise NotImplementedError("Override this method in the subclass to prepare the arguments for the OpenAI API") + def annotate_example(self, data, text): try: prompt = self.prompt(data, text) logger.debug(f"Calling OpenAI API with prompt: {prompt}") + + model = self.config["model"] + response = self.client.chat.completions.create( - model=self.config["model"], - response_format={"type": "json_object"}, + model=model, messages=[ {"role": "system", "content": self.config["system_msg"]}, {"role": "user", "content": prompt}, ], - **self.config.get("model_args", {}), + **self._prepare_chat_completions_create_args(), ) annotation_str = response.choices[0].message.content - j = json.loads(annotation_str) - logger.info(j) + logger.info(annotation_str) - return self.postprocess_annotations(text=text, model_json=j) + return { + "prompt": prompt, + "annotations": self.parse_annotations(text=text, annotations_json=annotation_str), + } except Exception as e: traceback.print_exc() logger.error(e) - return {"error": str(e)} + raise e + + +class VLLMMetric(OpenAIClientMetric): + def __init__(self, config, **kwargs): + base_url = config["api_url"] # Mandatory for VLLM + api_key = config.get("api_key", None) # Optional authentication for VLLM + + super().__init__(config, base_url=base_url, api_key=api_key, **kwargs) + + def _prepare_chat_completions_create_args(self): + guided_json = self.schema + # # works well with vllm https://docs.vllm.ai/en/latest/serving/openai_compatible_server.html#extra-parameters + config_args = {"extra_body": {"guided_json": guided_json}} + return config_args + + +class OpenAIMetric(OpenAIClientMetric): + def _prepare_chat_completions_create_args(self): + model = self.config["model"] + + model_supported = any(model.startswith(prefix) for prefix in ["gpt-4o", "gpt-4o-mini"]) + if not model_supported: + logger.warning( + f"Model {model} does not support structured output. It is probablye there will be SOME OF PARSING ERRORS" + ) + response_format = {"type": "json_object"} + else: + # Details at https://platform.openai.com/docs/guides/structured-outputs?context=without_parse + json_schema = dict(name="OutputNLGAnnotations", strict=True, schema=self.schema) + response_format = { + "type": "json_schema", + "json_schema": json_schema, + } + + config_args = self.config.get("model_args", {}) + if "response_format" in config_args and config_args["response_format"] != response_format: + logger.warning(f"Not using the default {response_format=} but using {config_args['response_format']=}") + else: + config_args["response_format"] = response_format + return config_args class OllamaMetric(LLMMetric): @@ -223,6 +321,13 @@ def __init__(self, config): self.set_api_endpoint() + @property + def new_connection_error_advice_docstring(self): + return """\ +Please check the Ollama documentation: + https://github.com/ollama/ollama?tab=readme-ov-file#generate-a-response +""" + def set_api_endpoint(self): # make sure the API URL ends with the `generate` endpoint self.config["api_url"] = self.config["api_url"].rstrip("/") @@ -233,13 +338,21 @@ def postprocess_output(self, output): output = output.strip() j = json.loads(output) + ANNOTATION_STR = "annotations" + assert ( + ANNOTATION_STR in OutputAnnotations.model_json_schema()["properties"] + ), f"Has the {OutputAnnotations=} schema changed?" + + # Required for OllamaMetric. You may want to switch to VLLMMetric which uses constrained decoding. + # It is especially useful for weaker models which have problems decoding valid JSON on output. if self.config["model"].startswith("llama3"): # the model often tends to produce a nested list - annotations = j[self.annotation_key] + + annotations = j[ANNOTATION_STR] if isinstance(annotations, list) and len(annotations) >= 1 and isinstance(annotations[0], list): - j[self.annotation_key] = j[self.annotation_key][0] + j[ANNOTATION_STR] = j[ANNOTATION_STR][0] - return j + return json.dumps(j) def annotate_example(self, data, text): prompt = self.prompt(data=data, text=text) @@ -255,25 +368,31 @@ def annotate_example(self, data, text): try: logger.debug(f"Calling {msg}") response = requests.post(self.config["api_url"], json=request_d) - response_json = response.json() + if response.status_code != 200: + raise ValueError(f"Received status code {response.status_code} from the API. Response: {response.text}") - if "error" in response_json: - return response_json + try: + response_json = response.json() + except json.JSONDecodeError: + logger.warning(f"Received non-JSON response: {response.text}") + return [] annotation_str = response_json["response"] - - j = self.postprocess_output(annotation_str) - logger.info(j) - return self.postprocess_annotations(text=text, model_json=j) + annotation_postprocessed = self.postprocess_output(annotation_str) + logger.info(annotation_postprocessed) + return { + "prompt": prompt, + "annotations": self.parse_annotations(text=text, annotations_json=annotation_postprocessed), + } except (ConnectionError, requests.exceptions.ConnectionError) as e: # notifiy the user that the API is down logger.error(f"Connection error: {e}") - return {"error": str(e)} + raise e except Exception as e: # ignore occasional problems not to interrupt the annotation process logger.error(f"Received\n\t{response=}\n\t{annotation_str=}\n\t{j=}\nError:{e}") traceback.print_exc() - return [] + return {} class LLMGen(Model): @@ -300,6 +419,7 @@ def postprocess_output(self, output): if output.endswith(suffix): output = output[: -len(suffix)] + output = output.strip() return output def prompt(self, data): @@ -318,6 +438,18 @@ def preprocess_data_for_prompt(self, data): return data def generate_output(self, data): + """ + Generate the output with the model. + + Args: + data: the data to be used in the prompt + + Returns: + A dictionary: { + "prompt": the prompt used for the generation, + "output": the generated output + } + """ raise NotImplementedError("Override this method in the subclass to call the LLM API") @@ -361,14 +493,15 @@ def generate_output(self, data): **self.config.get("model_args", {}), ) output = response.choices[0].message.content + output = self.postprocess_output(output) logger.info(output) - return {"prompt": prompt, "output": self.postprocess_output(output)} + return {"prompt": prompt, "output": output} except Exception as e: traceback.print_exc() logger.error(e) - return {"error": str(e)} + raise e class TextGenerationWebuiGen(LLMGen): @@ -410,13 +543,14 @@ def generate_output(self, data): ) output = response.choices[0].message.content + output = self.postprocess_output(output) logger.info(output) - return {"prompt": prompt, "output": self.postprocess_output(output)} + return {"prompt": prompt, "output": output} except Exception as e: traceback.print_exc() logger.error(e) - return {"error": str(e)} + raise e class OllamaGen(LLMGen): @@ -425,6 +559,13 @@ def __init__(self, config): self.set_api_endpoint() + @property + def new_connection_error_advice_docstring(self): + return """\ +Please check the Ollama documentation: + https://github.com/ollama/ollama?tab=readme-ov-file#generate-a-response +""" + def set_api_endpoint(self): # make sure the API URL ends with the `chat` endpoint self.config["api_url"] = self.config["api_url"].rstrip("/") @@ -457,17 +598,18 @@ def generate_output(self, data): response_json = response.json() if "error" in response_json: - return response_json + raise ValueError(f"Received error from the API: {response_json['error']}") output = response_json["message"]["content"] + output = self.postprocess_output(output) logger.info(output) - return {"prompt": prompt, "output": self.postprocess_output(output)} + return {"prompt": prompt, "output": output} except (ConnectionError, requests.exceptions.ConnectionError) as e: # notifiy the user that the API is down logger.error(f"Connection error: {e}") - return {"error": str(e)} + raise e except Exception as e: # ignore occasional problems not to interrupt the annotation process logger.error(f"Received\n\t{response=}\n\t{output=}\nError:{e}") traceback.print_exc() - return [] + raise e diff --git a/factgenie/static/css/custom.css b/factgenie/static/css/custom.css index a30a43e5..7d18d977 100755 --- a/factgenie/static/css/custom.css +++ b/factgenie/static/css/custom.css @@ -273,6 +273,11 @@ pre { box-shadow: 0 0 0 0.2rem rgba(1, 117, 172, 0.5); } +.btn-primary:focus-visible, +.btn-primary.focus-visible { + background-color: #005c88; +} + .btn-primary.disabled, .btn-primary:disabled { color: #fff; @@ -961,4 +966,34 @@ a:hover { #outputarea { max-height: 90vh; overflow-y: auto; +} + + +.detail-out { + padding: 7px; + /* overflow-y: auto; */ + margin: 0px !important; + height: 30px; +} + +.detail-out-card { + margin: 5px 20px 5px 5px; +} + +@keyframes jump-out { + 0% { + transform: scale(1); + } + + 50% { + transform: scale(1.1); + } + + 100% { + transform: scale(1); + } +} + +.panel-50 { + min-width: 900px; } \ No newline at end of file diff --git a/factgenie/static/img/config.png b/factgenie/static/img/config.png new file mode 100644 index 00000000..c81ad071 Binary files /dev/null and b/factgenie/static/img/config.png differ diff --git a/factgenie/static/img/datatypes/default.png b/factgenie/static/img/datatypes/default.png deleted file mode 100644 index ef43aeae..00000000 Binary files a/factgenie/static/img/datatypes/default.png and /dev/null differ diff --git a/factgenie/static/img/datatypes/json.png b/factgenie/static/img/datatypes/json.png deleted file mode 100644 index d0ff1fd9..00000000 Binary files a/factgenie/static/img/datatypes/json.png and /dev/null differ diff --git a/factgenie/static/img/datatypes/table.png b/factgenie/static/img/datatypes/table.png deleted file mode 100644 index 71610883..00000000 Binary files a/factgenie/static/img/datatypes/table.png and /dev/null differ diff --git a/factgenie/static/img/datatypes/text.png b/factgenie/static/img/datatypes/text.png deleted file mode 100644 index 4288c093..00000000 Binary files a/factgenie/static/img/datatypes/text.png and /dev/null differ diff --git a/factgenie/static/img/dice.png b/factgenie/static/img/dice.png index 3ad63592..6d4e90ac 100644 Binary files a/factgenie/static/img/dice.png and b/factgenie/static/img/dice.png differ diff --git a/factgenie/static/img/favicon.png b/factgenie/static/img/favicon.png index 97e750b7..7305a52d 100644 Binary files a/factgenie/static/img/favicon.png and b/factgenie/static/img/favicon.png differ diff --git a/factgenie/static/img/genie.png b/factgenie/static/img/genie.png index cd88292b..f84bf790 100644 Binary files a/factgenie/static/img/genie.png and b/factgenie/static/img/genie.png differ diff --git a/factgenie/static/img/src/factgenie-icons.psd b/factgenie/static/img/src/factgenie-icons.psd deleted file mode 100644 index 1afb0321..00000000 Binary files a/factgenie/static/img/src/factgenie-icons.psd and /dev/null differ diff --git a/factgenie/static/img/src/factgenie-icons.xcf b/factgenie/static/img/src/factgenie-icons.xcf deleted file mode 100644 index 6b0ddbf6..00000000 Binary files a/factgenie/static/img/src/factgenie-icons.xcf and /dev/null differ diff --git a/factgenie/static/img/src/factgenie-monocolor.xcf b/factgenie/static/img/src/factgenie-monocolor.xcf deleted file mode 100644 index bbf39df7..00000000 Binary files a/factgenie/static/img/src/factgenie-monocolor.xcf and /dev/null differ diff --git a/factgenie/static/img/src/favicon.xcf b/factgenie/static/img/src/favicon.xcf deleted file mode 100644 index 1291f59a..00000000 Binary files a/factgenie/static/img/src/favicon.xcf and /dev/null differ diff --git a/factgenie/static/js/analyze.js b/factgenie/static/js/analyze.js index e708de8c..dcc4ce13 100644 --- a/factgenie/static/js/analyze.js +++ b/factgenie/static/js/analyze.js @@ -1,3 +1,5 @@ +const metadata = window.metadata; + function deleteRow(button) { $(button).parent().parent().remove(); } @@ -10,18 +12,6 @@ function getSelectedCampaigns() { } -function gatherSelectedCombinations() { - // read all the rows the remained in #selectedDatasetsContent - var selectedData = []; - $("#selectedDatasetsContent tr").each(function () { - var dataset = $(this).find("td:eq(0)").text(); - var split = $(this).find("td:eq(1)").text(); - var setup_id = $(this).find("td:eq(2)").text(); - selectedData.push({ dataset: dataset, split: split, setup_id: setup_id }); - }); - return selectedData; -} - function showAgreement(response) { // uncover `agreement-modal`, fill the `agreement-area` with the data @@ -30,21 +20,38 @@ function showAgreement(response) { var content = ''; // iterate over the objects in the `response` list for (const data of response) { - const agreement = ` + content += `
${data.first_annotator} vs. ${data.second_annotator}
-
- Dataset-level agreement -

Pearson r between the annotators computed over a list of average error counts, one number for each (dataset, split, setup_id) combination.

-
-
Pearson r (macro)
-
${data.dataset_level_pearson_r_macro.toFixed(2)} (avg. of [${data.dataset_level_pearson_r_macro_categories}])
-

An average of coefficients computed separately for each category.

-
Pearson r (micro)
-
${data.dataset_level_pearson_r_micro.toFixed(2)}
-

Computed over concatenated results from all the categories.

-
-
- Example-level agreement +
+ `; + + var dataset_level_agreement = ''; + if (data.dataset_level_pearson_r_macro !== null) { + dataset_level_agreement = ` + Dataset-level agreement +

Pearson r between the annotators computed over a list of average error counts, one number for each (dataset, split, setup_id) combination.

+
+
Pearson r (macro)
+
${data.dataset_level_pearson_r_macro.toFixed(2)} (avg. of [${data.dataset_level_pearson_r_macro_categories}])
+

An average of coefficients computed separately for each category.

+
Pearson r (micro)
+
${data.dataset_level_pearson_r_micro.toFixed(2)}
+

Computed over concatenated results from all the categories.

+
+
+ `; + } else { + dataset_level_agreement = ` + Dataset-level agreement +

Dataset-level agreement not available for a single split.

+
+ `; + } + + content += dataset_level_agreement; + + const example_level_agreement = ` + Example-level agreement

Pearson r between the annotators computed over a list of error counts, one number for each example.

Pearson r (macro)
@@ -56,7 +63,7 @@ function showAgreement(response) {

`; - content += agreement; + content += example_level_agreement; } $('#agreement-area').html(content); @@ -90,6 +97,9 @@ function computeAgreement() { } else { showAgreement(response); } + }, + error: function (response) { + alert("An error occurred: " + response.responseText); } }); } diff --git a/factgenie/static/js/annotate.js b/factgenie/static/js/annotate.js new file mode 100644 index 00000000..3af1aa5a --- /dev/null +++ b/factgenie/static/js/annotate.js @@ -0,0 +1,339 @@ +const sizes = [50, 50]; +const annotator_id = window.annotator_id; +const metadata = window.metadata; + +var current_example_idx = 0; +var annotation_set = window.annotation_set; + +const total_examples = annotation_set.length; + +var examples_cached = {}; + +var splitInstance = Split(['#centerpanel', '#rightpanel'], { + sizes: sizes, + gutterSize: 1, + onDrag: function () { + // trigger a resize update on slider inputs when the handle is dragged + // not a perfect solution, but it works + $('.slider-crowdsourcing').each(function () { + $(this).css('width', "80%"); + }); + } +}); + +function clearExampleLevelFields() { + // uncheck all checkboxes + $(".crowdsourcing-flag input[type='checkbox']").prop("checked", false); + + // reset all options to the first value + $(".crowdsourcing-option select").val(0); + $(".crowdsourcing-option input[type='range']").val(0); + + // clear the values in text inputs + $(".crowdsourcing-text input[type='text']").val(""); +} + + +function collectFlags() { + const flags = []; + $(".crowdsourcing-flag").each(function () { + const label = $(this).find("label").text().trim(); + const value = $(this).find("input[type='checkbox']").prop("checked"); + flags.push({ + label: label, + value: value + }); + }); + return flags; +} + +function collectOptions() { + const options = []; + + $(".crowdsourcing-option").each(function (x) { + if ($(this).hasClass("option-select")) { + const type = "select"; + const label = $(this).find("label").text().trim(); + const index = $(this).find("select").val(); + const value = $(this).find("select option:selected").text(); + + const optionList = $(this).find("select option").map(function () { + return $(this).text(); + }).get(); + options.push({ type: type, label: label, index: index, value: value, optionList: optionList }); + } else if ($(this).hasClass("option-slider")) { + const type = "slider"; + const label = $(this).find("label").text(); + const index = $(this).find("input[type='range']").val(); + const value = $(this).find("datalist option")[index].value; + const optionList = $(this).find("datalist option").map(function () { + return $(this).val(); + }).get(); + options.push({ type: type, label: label, index: index, value: value, optionList: optionList }); + } + }); + return options; +} + +function collectTextFields() { + const textFields = []; + + $(".crowdsourcing-text").each(function (x) { + const label = $(this).find("label").text().trim(); + const value = $(this).find("input[type='text']").val(); + textFields.push({ label: label, value: value }); + }); + return textFields; +} + + +function fetchAnnotation(dataset, split, setup_id, example_idx, annotation_idx) { + return new Promise((resolve, reject) => { + $.get(`${url_prefix}/example`, { + "dataset": dataset, + "example_idx": example_idx, + "split": split, + "setup_id": setup_id + }, function (data) { + $('
', { + id: `out-text-${annotation_idx}`, + class: `annotate-box `, + style: 'display: none;' + }).appendTo('#outputarea'); + + // we have always only a single generated output here + data.generated_outputs = data.generated_outputs[0]; + examples_cached[annotation_idx] = data; + resolve(); + }).fail(function () { + reject(); + }); + }); +} + + +function goToAnnotation(example_idx) { + $(".page-link").removeClass("bg-active"); + $(`#page-link-${example_idx}`).addClass("bg-active"); + + $(".annotate-box").hide(); + $(`#out-text-${example_idx}`).show(); + + const data = examples_cached[example_idx]; + $("#examplearea").html(data.html); + + const flags = annotation_set[example_idx].flags; + const options = annotation_set[example_idx].options; + const textFields = annotation_set[example_idx].textFields; + + clearExampleLevelFields(); + + if (flags !== undefined) { + $(".crowdsourcing-flag").each(function (i) { + $(this).find("input[type='checkbox']").prop("checked", flags[i]["value"]); + }); + } + + if (options !== undefined) { + for (const [i, option] of Object.entries(options)) { + const div = $(`.crowdsourcing-option:eq(${i})`); + // we can have either a select or a slider (we can tell by `type`) + // we need to set the option defined by `index` + if (option.type == "select") { + div.find("select").val(option.index); + } + if (option.type == "slider") { + div.find("input[type='range']").val(option.index); + } + } + } + + if (textFields !== undefined) { + for (const [i, textField] of Object.entries(textFields)) { + $(`.crowdsourcing-text input:eq(${i})`).val(textField.value); + } + } + +} + +function goToPage(page) { + const example_idx = current_example_idx; + + current_example_idx = page; + current_example_idx = mod(current_example_idx, total_examples); + + saveCurrentAnnotations(example_idx); + goToAnnotation(current_example_idx); +} + +function loadAnnotations() { + $("#dataset-spinner").show(); + + const promises = []; + const annotation_span_categories = metadata.config.annotation_span_categories; + + // prefetch the examples for annotation: we need them for YPet initialization + for (const [annotation_idx, example] of Object.entries(annotation_set)) { + const dataset = example.dataset; + const split = example.split; + const example_idx = example.example_idx; + const setup_id = example.setup_id; + + const promise = fetchAnnotation(dataset, split, setup_id, example_idx, annotation_idx); + promises.push(promise); + } + Promise.all(promises) + .then(() => { + + YPet.addInitializer(function (options) { + /* Configure the # and colors of Annotation types (minimum 1 required) */ + YPet.AnnotationTypes = new AnnotationTypeList(annotation_span_categories); + var regions = {}; + var paragraphs = {}; + + for (const [annotation_idx, data] of Object.entries(examples_cached)) { + + var p = new Paragraph({ 'text': data.generated_outputs.output, 'granularity': metadata.config.annotation_granularity }); + + paragraphs[`p${annotation_idx}`] = p; + regions[`p${annotation_idx}`] = `#out-text-${annotation_idx}`; + + const li = $('
  • ', { class: "page-item" }); + const a = $('', { class: "page-link bg-incomplete", style: "min-height: 28px;", id: `page-link-${annotation_idx}` }).text(annotation_idx); + li.append(a); + $("#nav-example-cnt").append(li); + + // switch to the corresponding example when clicking on the page number + $(`#page-link-${annotation_idx}`).click(function () { + goToPage(annotation_idx); + }); + } + YPet.addRegions(regions); + + for (const [p, p_obj] of Object.entries(paragraphs)) { + YPet[p].show(new WordCollectionView({ collection: p_obj.get('words') })); + + YPet[p].currentView.collection.parentDocument.get('annotations').on('remove', function (model, collection) { + if (collection.length == 0) { + collection = []; + } + }); + } + goToAnnotation(0); + }); + YPet.start(); + + $("#hideOverlayBtn").attr("disabled", false); + $("#hideOverlayBtn").html("View the annotation page"); + }) + .catch((e) => { + // Handle errors if any request fails + console.error("One or more requests failed."); + // Log the error + console.error(e); + + }) + .finally(() => { + // This block will be executed regardless of success or failure + $("#dataset-spinner").hide(); + }); +} + +function markAnnotationAsComplete() { + $('#page-link-' + current_example_idx).removeClass("bg-incomplete"); + $('#page-link-' + current_example_idx).addClass("bg-complete"); + + // if all the examples are annotated, post the annotations + if ($(".bg-incomplete").length == 0) { + saveCurrentAnnotations(current_example_idx); + + // show the `submit` button + $("#submit-annotations-btn").show(); + + // scroll to the top + $('html, body').animate({ + scrollTop: $("#submit-annotations-btn").offset().top + }, 500); + + } else if (current_example_idx < total_examples - 1) { + // annotations will be saved automatically + nextBtn(); + } +} + +function saveCurrentAnnotations(example_idx) { + var collection = YPet[`p${example_idx}`].currentView.collection.parentDocument.get('annotations').toJSON(); + annotation_set[example_idx]["annotations"] = collection; + annotation_set[example_idx]["flags"] = collectFlags(); + annotation_set[example_idx]["options"] = collectOptions(); + annotation_set[example_idx]["textFields"] = collectTextFields(); +} + +function submitAnnotations(campaign_id) { + // remove `words` from the annotations: they are only used by the YPet library + for (const example of annotation_set) { + for (const annotation of example.annotations) { + delete annotation.words; + } + } + + $.post({ + url: `${url_prefix}/submit_annotations`, + contentType: 'application/json', // Specify JSON content type + data: JSON.stringify({ + campaign_id: metadata.id, + annotator_id: annotator_id, + annotation_set: annotation_set + } + ), + success: function (response) { + console.log(response); + window.onbeforeunload = null; + + if (response.success !== true) { + $("#error-message").html(response.error); + $("#overlay-fail").show(); + } else { + $("#final-message").html(response.message); + $("#overlay-end").show(); + } + }, + error: function (response) { + console.log(response); + $("#overlay-fail").show(); + } + }); +} + + +$("#hideOverlayBtn").click(function () { + $("#overlay-start").fadeOut(); +}); + +$(".btn-err-cat").change(function () { + if (this.checked) { + const cat_idx = $(this).attr("data-cat-idx"); + YPet.setCurrentAnnotationType(cat_idx); + } +}); + +$('.btn-check').on('change', function () { + $('.btn-check').each(function () { + const label = $(`label[for=${this.id}]`); + if (this.checked) { + label.addClass('active'); + } else { + label.removeClass('active'); + } + }); +}); + +$(document).ready(function () { + loadAnnotations(); + $("#total-examples").html(total_examples - 1); + enableTooltips(); +}); + +window.onbeforeunload = function () { + return "Are you sure you want to reload the page? Your work will be lost."; +} \ No newline at end of file diff --git a/factgenie/static/js/browse.js b/factgenie/static/js/browse.js new file mode 100644 index 00000000..a676c560 --- /dev/null +++ b/factgenie/static/js/browse.js @@ -0,0 +1,446 @@ +var current_example_idx = 0; +var selected_campaigns = []; +var collapsed_boxes = []; +var splitInstance = Split(['#centerpanel', '#rightpanel'], { + sizes: [66, 33], + gutterSize: 1, +}); + +function changeDataset() { + $("#dataset-spinner").show(); + const dataset = $('#dataset-select').val(); + + // set available splits in #split-select + $('#split-select').empty(); + for (const split of datasets[dataset].splits) { + $('#split-select').append(``); + } + const split = $('#split-select').val(); + + current_example_idx = 0; + fetchExample(dataset, split, current_example_idx); + $("#page-input").val(current_example_idx); +} + +function changeSplit() { + $("#dataset-spinner").show(); + const dataset = $('#dataset-select').val(); + const split = $('#split-select').val(); + current_example_idx = 0; + fetchExample(dataset, split, current_example_idx); + $("#page-input").val(current_example_idx); +} + +function changeExample(dataset, split, example_idx) { + $("#dataset-spinner").show(); + $('#dataset-select').val(dataset); + $('#split-select').empty(); + for (const split of datasets[dataset].splits) { + $('#split-select').append(``); + } + $('#split-select').val(split); + current_example_idx = example_idx; + fetchExample(dataset, split, example_idx); + $("#page-input").val(example_idx); +} + + +function createOutputBox(content, exampleLevelFields, annId, setup_id) { + var card = $('
    ', { class: `card output-box generated-output-box box-${setup_id} box-${annId} box-${setup_id}-${annId}` }); + + var annotationBadge = (annId !== "original") ? ` ${annId}` : "" + var headerHTML = `
    + ${setup_id} + ${annotationBadge} +
    + ` + var cardHeader = $('
    ', { class: "card-header card-header-collapse small", "data-bs-toggle": "collapse", "data-bs-target": `#out-${setup_id}-${annId}` }).html(headerHTML); + var cardBody = $('
    ', { class: "card-body show", id: `out-${setup_id}-${annId}`, "aria-expanded": "true" }); + // var cardTitle = $('
    ', { class: "card-title" }).text(setup_id); + var cardText = $('
    ', { class: "card-text mt-2" }).html(content); + + // cardBody.append(cardTitle); + cardBody.append(cardText); + card.append(cardHeader); + card.append(cardBody); + + if (exampleLevelFields !== null) { + cardBody.append(exampleLevelFields); + } + + return card; +} + +function generateAnnotatorShortId(campaign_id, annotator_group) { + const ann_id = `${campaign_id}-ann${annotator_group}`; + return ann_id; +} + +function createOutputBoxes(generated_outputs, highlight_setup_id) { + // clear the output area + $("#outputarea").empty(); + + // sort outputs by setup id + generated_outputs.sort(function (a, b) { + return a.setup_id.localeCompare(b.setup_id); + }); + + // find all campaign ids in output annotations + const annIds = new Map(); + generated_outputs.forEach(output => { + output.annotations.forEach(annotation => { + const campaign_id = annotation.campaign_id; + const annotator_group = annotation.annotator_group; + const ann_id = generateAnnotatorShortId(campaign_id, annotator_group); + + annIds.set(ann_id, { "campaign_id": campaign_id, "annotator_group": annotator_group }); + }); + }); + const selectBox = $("#annotations-select"); + // clear the selectbox + selectBox.empty(); + + // add an option for each campaign id + for (const [ann_id, { campaign_id, annotator_group }] of annIds) { + const button = $(``); + button.on('click', function () { + $(this).toggleClass('active'); + updateDisplayedAnnotations(); + }); + selectBox.append(button); + } + if (annIds.size > 0) { + $("#setuparea").show(); + } else { + $("#setuparea").hide(); + } + + // add the annotated outputs + for (const output of generated_outputs) { + var groupDiv = $('
    ', { class: `output-group box-${output.setup_id} d-inline-flex gap-2` }); + groupDiv.appendTo("#outputarea"); + + const plain_output = getAnnotatedOutput(output, "original", null); + card = createOutputBox(plain_output, null, "original", output.setup_id); + card.appendTo(groupDiv); + + for (const [annId, { campaign_id, annotator_group }] of annIds) { + annotations = output.annotations.filter(a => a.campaign_id == campaign_id && a.annotator_group == annotator_group)[0]; + + const annotated_output = getAnnotatedOutput(output, annId, annotations); + const exampleLevelFields = getExampleLevelFields(annotations); + + card = createOutputBox(annotated_output, exampleLevelFields, annId, output.setup_id); + card.appendTo(groupDiv); + card.hide(); + } + + // Highlight the output box if highlight_setup_id matches + if (highlight_setup_id && highlight_setup_id === output.setup_id) { + card.addClass('border border-primary border-2'); + groupDiv.css({ animation: 'jump-out 0.5s ease' }); + } + } +} + +function fetchExample(dataset, split, example_idx) { + // change the URL so that it shows the permalink + const newUrl = `${url_prefix}/browse?dataset=${dataset}&split=${split}&example_idx=${example_idx}`; + + // the check prevents being stuck at the same URL + if (!window.location.href.includes(newUrl)) { + history.pushState(null, '', newUrl); + } + $.get(`${url_prefix}/example`, { + "dataset": dataset, + "example_idx": example_idx, + "split": split, + }, function (data) { + if (data.error !== undefined) { + console.log(data.error); + alert(data.error); + return; + } + $("#dataset-spinner").hide(); + $("#examplearea").html(data.html); + + showRawData(data); + + total_examples = datasets[dataset].example_count[split]; + $("#total-examples").html(total_examples - 1); + + createOutputBoxes(data.generated_outputs, window.highlight_setup_id); + showSelectedCampaigns(); + updateDisplayedAnnotations(); + + window.highlight_setup_id = null; + }).fail(function (response) { + console.log(response); + alert("Failed to fetch example."); + }); +} + +function getAnnotatedOutput(output, annId, annotations) { + const setup_id = output.setup_id; + + // replace newlines with any spaces around them with
    + const content = output.output.replace(/\\n/g, '
    '); + + var placeholder = $('
    ', { id: `out-${setup_id}-${annId}-placeholder`, class: `font-mono out-placeholder out-${annId}-placeholder` });
    +    var annotated_content;
    +
    +    if (annotations !== null && annotations !== undefined) {
    +        const annotation_span_categories = annotations.annotation_span_categories;
    +        annotated_content = highlightContent(content, annotations, annotation_span_categories);
    +    } else {
    +        // we do not have outputs for the particular campaign -> grey out the text
    +        if (annId != "original") {
    +            placeholder.css("color", "#c2c2c2");
    +        }
    +        annotated_content = content;
    +    }
    +    placeholder.html(annotated_content);
    +    // placeholder.hide();
    +    return placeholder;
    +}
    +
    +function getExampleLevelFields(annotations) {
    +    if (annotations === null || annotations === undefined) {
    +        return null;
    +    }
    +    // show `outputs.flags`, `outputs.options`, and `outputs.textFields`
    +    var flags = annotations.flags;
    +    var options = annotations.options;
    +    var textFields = annotations.textFields;
    +
    +    var html = $('
    ', { class: "p-2 extra-fields" }); + + if (flags !== undefined && flags.length > 0) { + var flagsDiv = $('
    ', { class: "small" }); + // flagsDiv.append($('').html("Flags")); + for (const flag of flags) { + var labelDiv = $('
    ', { class: "small text-muted " }).text(`${flag.label}`); + var valueDiv = $('
    ', { class: "small mb-1 fw-bold" }).text(`${flag.value}`); + + flagsDiv.append(labelDiv); + flagsDiv.append(valueDiv); + } + html.append(flagsDiv); + } + + if (options !== undefined && options.length > 0) { + var optionsDiv = $('
    ', { class: "small" }); + + for (const option of options) { + var labelDiv = $('
    ', { class: "small text-muted" }).text(`${option.label}`); + var valueDiv = $('
    ', { class: "small mb-1 fw-bold" }).text(`${option.value}`); + + optionsDiv.append(labelDiv); + optionsDiv.append(valueDiv); + } + html.append(optionsDiv); + } + + if (textFields !== undefined && textFields.length > 0) { + var textFieldsDiv = $('
    ', { class: "small" }); + + for (const textField of textFields) { + var labelDiv = $('
    ', { class: "small text-muted" }).text(`${textField.label}`); + var valueDiv = $('
    ', { class: "small mb-1 fw-bold" }).text(`${textField.value}`); + + textFieldsDiv.append(labelDiv); + textFieldsDiv.append(valueDiv); + } + html.append(textFieldsDiv); + } + return html; +} + +function goToPage(page) { + current_example_idx = Math.min(page, total_examples - 1); + current_example_idx = Math.max(0, current_example_idx); + + const dataset = $('#dataset-select').val(); + const split = $('#split-select').val(); + + fetchExample(dataset, split, current_example_idx); + + $("#page-input").val(current_example_idx); +} + +// Our custom function to highlight the text spans using the collected annotations +// Here we do *not* use the YPet library, but directly work with HTML +function highlightContent(content, annotations, annotation_span_categories) { + let offset = 0; // Track cumulative offset + const annotationSet = annotations.annotations; + + // sort by start + annotationSet.sort(function (a, b) { + return a.start - b.start; + }); + var html = content; + + annotationSet.forEach(annotation => { + const annotationType = annotation.type; + + if (!(annotationType in annotation_span_categories)) { + console.log("Warning: annotation type not found in annotation_span_categories: " + annotationType); + return; + } + const color = annotation_span_categories[annotationType].color; + const text = annotation.text.trimEnd(); + + const start = annotation.start + offset; + const end = start + text.length; + + const error_name = annotation_span_categories[annotationType].name; + const note = annotation.reason || annotation.note; + let tooltip_text; + + if (note !== undefined && note !== "" && note !== null) { + tooltip_text = `${error_name} (${note})`; + } else { + tooltip_text = `${error_name}`; + } + + const spanId = `span-${start}-${end}`; + const spanContent = `${text}`; + + html = html.slice(0, start) + spanContent + html.slice(end); + // Update the offset + offset += spanContent.length - text.length; + }); + return html; +} + +function showRawData(data) { + var rawDataStr = JSON.stringify(data.raw_data, null, 2).replace(/\\n/g, '
    '); + + if (rawDataStr[0] == '"') { + // remove the first and last double quotes + rawDataStr = rawDataStr.slice(1, -1); + } + rawDataStr = rawDataStr.replace(/&/g, '&').replace(//g, '>'); + $("#rawarea").html(`
    ${rawDataStr}
    `); +} + + +function showSelectedCampaigns() { + // if the annotator is still among the values, restore it + // prevent resetting to the first annotation when switching examples + $(".btn-ann-select").each(function () { + if (selected_campaigns.includes($(this).data('ann'))) { + $(this).addClass("active").trigger("change"); + } else { + $(this).removeClass("active"); + } + }); + + +} + +function toggleRaw() { + // toggle display: none on rawarea and examplearea + $("#rawarea").toggle(); + $("#examplearea").toggle(); +} + + +function updateDisplayedAnnotations() { + const activeButtons = $('.btn-ann-select.active'); + selected_campaigns = activeButtons.map(function () { + return $(this).data('ann'); + }).get(); + // hide all placeholders + $(".output-box").hide(); + + // if no campaign ids, show the original output + if (selected_campaigns.length == 0) { + $(".box-original").show(); + } + for (const campaign_id of selected_campaigns) { + // show the selected annotator + $(`.box-${campaign_id}`).show(); + } + + restoreCollapsedStates(); + enableTooltips(); +} + +$('#page-input').keypress(function (event) { + // Enter = Go to page + if (event.keyCode == 13) { + goToBtn(); + } +}); + +$("#dataset-select").on("change", changeDataset); +$("#split-select").on("change", changeSplit); + +$(document).keydown(function (event) { + const key = event.key; + + if (key === "ArrowRight") { + event.preventDefault(); + nextBtn(); + } else if (key === "ArrowLeft") { + event.preventDefault(); + prevBtn(); + } +}); + +// checking whether the user is navigating to an example using history: if so, we need to load the particular example +window.addEventListener('popstate', function (event) { + if (window.location.pathname === `/browse`) { + const params = new URLSearchParams(window.location.search); + const dataset = params.get('dataset'); + const split = params.get('split'); + const example_idx = params.get('example_idx'); + if (dataset && split && example_idx) { + changeExample(dataset, split, example_idx); + } + } +}); + + +$(document).ready(function () { + if (window.display_example != null) { + const e = window.display_example; + changeExample(e.dataset, e.split, e.example_idx); + } + else { + // select the first dataset from the selectbox + $("#dataset-select").val( + $("#dataset-select option:first").val() + ).trigger("change"); + $("#page-input").val(current_example_idx); + } + + enableTooltips(); +}); + +// Restore collapsed states after page change/content load +function restoreCollapsedStates() { + collapsed_boxes.forEach(targetId => { + const element = document.getElementById(targetId); + if (element) { + const bsCollapse = new bootstrap.Collapse(element, { + toggle: false + }); + bsCollapse.hide(); + } + }); +} + +// Store collapsed state when boxes are toggled +document.addEventListener('shown.bs.collapse', function (e) { + const targetId = e.target.id; + collapsed_boxes = collapsed_boxes.filter(id => id !== targetId); +}); + +document.addEventListener('hidden.bs.collapse', function (e) { + const targetId = e.target.id; + if (!collapsed_boxes.includes(targetId)) { + collapsed_boxes.push(targetId); + } +}); \ No newline at end of file diff --git a/factgenie/static/js/campaigns.js b/factgenie/static/js/campaigns.js new file mode 100644 index 00000000..e875a571 --- /dev/null +++ b/factgenie/static/js/campaigns.js @@ -0,0 +1,644 @@ +const available_data = window.available_data; + +function clearCampaign(campaignId) { + // ask for confirmation + if (!confirm("Are you sure you want to clear all campaign outputs?")) { + return; + } + $.post({ + url: `${url_prefix}/clear_campaign`, + contentType: 'application/json', + data: JSON.stringify({ + campaignId: campaignId, + mode: mode + }), + success: function (response) { + console.log(response); + window.location.reload(); + } + }); +} + +function clearOutput(campaignId, mode, idx, annotatorGroup) { + // ask for confirmation + if (!confirm(`Are you sure you want to free the row id ${idx}? Any collected outputs will be deleted.`)) { + return; + } + $.post({ + url: `${url_prefix}/clear_output`, + contentType: 'application/json', // Specify JSON content type + data: JSON.stringify({ + campaignId: campaignId, + mode: mode, + idx: idx, + annotatorGroup: annotatorGroup + }), + success: function (response) { + console.log(response); + + if (response.success !== true) { + alert(response.error); + } else { + // reload the page + location.reload(); + } + } + }); +} + +function createLLMCampaign() { + const campaignId = $('#campaignId').val(); + // const llmConfig = $('#llmConfig').val(); + + const config = gatherConfig(); + var campaignData = gatherSelectedCombinations(); + + // if no datasets are selected, show an alert + if (campaignData.length == 0) { + alert("Please select at least one existing combination of dataset, split, and output."); + return; + } + + $.post({ + url: `${url_prefix}/${mode}/create`, + contentType: 'application/json', // Specify JSON content type + data: JSON.stringify({ + campaignId: campaignId, + campaignData: campaignData, + config: config + }), + success: function (response) { + console.log(response); + + if (response.success !== true) { + alert(response.error); + } else { + window.location.href = `${url_prefix}/${mode}`; + } + } + }); +} + +function createHumanCampaign() { + const campaignId = $('#campaignId').val(); + const config = gatherConfig(); + var campaignData = gatherSelectedCombinations(); + + // if no datasets are selected, show an alert + if (campaignData.length == 0) { + alert("Please select at least one existing combination of dataset, split, and output."); + return; + } + + $.post({ + url: `${url_prefix}/crowdsourcing/create`, + contentType: 'application/json', // Specify JSON content type + data: JSON.stringify({ + campaignId: campaignId, + config: config, + campaignData: campaignData + }), + success: function (response) { + console.log(response); + + if (response.success !== true) { + alert(response.error); + } else { + window.location.href = `${url_prefix}/crowdsourcing`; + } + } + }); +} + +function deleteCampaign(campaignId, mode) { + // ask for confirmation + if (!confirm(`Are you sure you want to delete the campaign ${campaignId}? All the data will be lost!`)) { + return; + } + + $.post({ + url: `${url_prefix}/delete_campaign`, + contentType: 'application/json', // Specify JSON content type + data: JSON.stringify({ + campaignId: campaignId, + mode: mode, + }), + success: function (response) { + console.log(response); + + if (response.success !== true) { + alert(response.error); + } else { + // remove the campaign from the list + $(`#campaign-${campaignId}`).remove(); + + // reload the page + location.reload(); + } + } + }); +} + +function duplicateConfig(btnElem, filenameElemId, modeTo, campaignId) { + const filename = $("#" + filenameElemId).val() + ".yaml"; + const modeFrom = window.mode; + + // TODO warn overwrite + $.post({ + url: `${url_prefix}/duplicate_config`, + contentType: 'application/json', // Specify JSON content type + data: JSON.stringify({ + campaignId: campaignId, + modeFrom: modeFrom, + modeTo: modeTo, + filename: filename, + }), + success: function (response) { + console.log(response); + + if (response.success !== true) { + alert(response.error); + } else { + // change color of the button save-cfg-submit to green for a second with the label "Saved!", then back to normal + const origText = $(btnElem).text(); + $(btnElem).removeClass("btn-primary").addClass("btn-success").text("Saved!"); + setTimeout(function () { + $('#save-cfg-modal').modal('hide'); + $(btnElem).removeClass("btn-success").addClass("btn-primary").text(origText); + }, 1500); + } + } + }); +} + +function duplicateEval(inputDuplicateId, campaignId) { + newCampaignId = $(`#${inputDuplicateId}`).val(); + + $.post({ + url: `${url_prefix}/duplicate_eval`, + contentType: 'application/json', // Specify JSON content type + data: JSON.stringify({ + campaignId: campaignId, + newCampaignId: newCampaignId, + mode: window.mode + }), + success: function (response) { + console.log(response); + + if (response.success !== true) { + alert(response.error); + } else { + // hide the modal and reload the page + $('#duplicate-eval-modal').modal('hide'); + location.reload(); + } + } + }); +} + + +function gatherConfig() { + var config = {}; + + if (window.mode == "crowdsourcing") { + config.annotatorInstructions = annotatorInstructionsMDE.value(); + // config.annotatorPrompt = $("#annotatorPrompt").val(); + config.finalMessage = finalMessageMDE.value(); + config.examplesPerBatch = $("#examplesPerBatch").val(); + config.annotatorsPerExample = $("#annotatorsPerExample").val(); + config.idleTime = $("#idleTime").val(); + config.annotationGranularity = $("#annotationGranularity").val(); + config.service = $("#service").val(); + config.sortOrder = $("#sortOrder").val(); + config.annotationSpanCategories = getAnnotationSpanCategories(); + config.flags = getKeys($("#flags")); + config.options = getOptions(); + config.textFields = getKeys($("#textFields")); + } else if (window.mode == "llm_eval" || window.mode == "llm_gen") { + config.metricType = $("#metric-type").val(); + config.modelName = $("#model-name").val(); + config.promptTemplate = $("#prompt-template").val(); + config.systemMessage = $("#system-message").val(); + config.apiUrl = $("#api-url").val(); + config.modelArguments = getKeysAndValues($("#model-arguments")); + config.extraArguments = getKeysAndValues($("#extra-arguments")); + + if (window.mode == "llm_eval") { + config.annotationSpanCategories = getAnnotationSpanCategories(); + } + if (window.mode == "llm_gen") { + config.startWith = $("#start-with").val(); + } + } + return config; +} + + +function pauseLLMCampaign(campaignId) { + $(`#run-button-${campaignId}`).show(); + $(`#stop-button-${campaignId}`).hide(); + setCampaignStatus(campaignId, "idle"); + + $.post({ + url: `${url_prefix}/llm_campaign/pause`, + contentType: 'application/json', + data: JSON.stringify({ + campaignId: campaignId + }), + success: function (response) { + console.log(response); + } + }); +} + +function prefillInstructions() { + const annotationSpanCategories = getAnnotationSpanCategories(); + const defaultInstructions = window.default_prompts.crowdsourcing; + + if (annotationSpanCategories.length == 0) { + alert("Please add at least one annotation span category."); + return; + } + // if annotatorInstructionsMDE contains some text, ask for confirmation + if (annotatorInstructionsMDE.value().length > 0) { + if (!confirm("Are you sure you want to overwrite the current instructions?")) { + return; + } + } + + var errorList = []; + annotationSpanCategories.forEach((category) => { + const span = `- ${category.name}: ${category.description}`; + errorList.push(span); + }); + var instructions = defaultInstructions.replace(/{error_list}/g, errorList.join("\n")); + annotatorInstructionsMDE.value(instructions); +} + +function prefillPrompt() { + const annotationSpanCategories = getAnnotationSpanCategories(); + const defaultPrompt = window.default_prompts.llm_eval; + + if (annotationSpanCategories.length == 0) { + alert("Please add at least one annotation span category."); + return; + } + + // if promptTemplate contains some text, ask for confirmation + if ($("#prompt-template").val().length > 0) { + if (!confirm("Are you sure you want to overwrite the current prompt?")) { + return; + } + } + var errorList = []; + + annotationSpanCategories.forEach((category, idx) => { + const span = `${idx}: ${category.name} (${category.description})`; + errorList.push(span); + }); + + var prompt = defaultPrompt.replace(/{error_list}/g, errorList.join("\n")); + $("#prompt-template").val(prompt); +} + + +function runLLMCampaign(campaignId) { + $(`#run-button-${campaignId}`).hide(); + $(`#stop-button-${campaignId}`).show(); + setCampaignStatus(campaignId, "running"); + + startLLMCampaignListener(campaignId); + + $.post({ + url: `${url_prefix}/${mode}/run`, + contentType: 'application/json', + data: JSON.stringify({ + campaignId: campaignId + }), + success: function (response) { + if (response.success !== true) { + alert(response.error); + $("#log-area").text(JSON.stringify(response.error)); + console.log(JSON.stringify(response)); + + setCampaignStatus(campaignId, "idle"); + $(`#run-button-${campaignId}`).show(); + $(`#stop-button-${campaignId}`).hide(); + } else { + console.log(response); + } + } + }); +} + +function saveConfig(mode) { + const filename = $("#config-save-filename").val() + ".yaml"; + const config = gatherConfig(); + + if (filename in window.configs) { + if (!confirm(`The configuration with the name ${filename} already exists. Do you want to overwrite it?`)) { + return; + } + } + $.post({ + url: `${url_prefix}/save_config`, + contentType: 'application/json', // Specify JSON content type + data: JSON.stringify({ + mode: mode, + filename: filename, + config: config + }), + success: function (response) { + console.log(response); + + if (response.success !== true) { + alert(response.error); + } else { + // change color of the button save-cfg-submit to green for a second with the label "Saved!", then back to normal + $("#save-cfg-submit").removeClass("btn-primary").addClass("btn-success").text("Saved!"); + setTimeout(function () { + $('#save-cfg-modal').modal('hide'); + $("#save-cfg-submit").removeClass("btn-success").addClass("btn-primary").text("Save"); + }, 1500); + } + } + }); + +} + +function saveGenerationOutputs(campaignId) { + modelName = $("#save-generations-model-name").val(); + + $.post({ + url: `${url_prefix}/save_generation_outputs`, + contentType: 'application/json', // Specify JSON content type + data: JSON.stringify({ + campaignId: campaignId, + modelName: modelName + }), + success: function (response) { + console.log(response); + + if (response.success !== true) { + alert(response.error); + } else { + // change color of the button save-cfg-submit to green for a second with the label "Saved!", then back to normal + $("#save-generations-submit").removeClass("btn-primary").addClass("btn-success").text("Saved!"); + setTimeout(function () { + $('#save-generations-modal').modal('hide'); + $("#save-generations-submit").removeClass("btn-success").addClass("btn-primary").text("Save"); + }, 1500); + } + } + }); +} + +function setCampaignStatus(campaignId, status) { + $(`#metadata-status-${campaignId}`).html(status); + $(`#metadata-status-${campaignId}`).removeClass("bg-idle bg-running bg-finished bg-error"); + $(`#metadata-status-${campaignId}`).addClass(`bg-${status}`); +} + +function setExampleStatus(status, button) { + button.removeClass("bg-free bg-finished"); + button.addClass(`bg-${status}`); + button.text(status); +} + + +function showResult(payload, campaignId) { + const finished_examples = payload.stats.finished; + const total_examples = payload.stats.total; + const progress = Math.round((finished_examples / total_examples) * 100); + $(`#llm-progress-bar-${campaignId}`).css("width", `${progress}%`); + $(`#llm-progress-bar-${campaignId}`).attr("aria-valuenow", progress); + $(`#metadata-example-cnt-${campaignId}`).html(`${finished_examples} / ${total_examples}`); + console.log(`Progress: ${progress}%`); + + + // update the annotation button + const example = payload.response; + const dataset = example.dataset; + const split = example.split; + const setup_id = example.setup_id || campaignId; + const example_idx = example.example_idx; + const rowId = `${dataset}-${split}-${setup_id}-${example_idx}`; + const annotation_button = $(`#annotBtn${rowId}`); + annotation_button.show(); + + const clear_output_button = $(`#clearOutput${rowId}`); + clear_output_button.show(); + + // update the annotation content + const annotation_div = $(`#annotPre${rowId}`); + + // llm_eval mode + if (example.annotations !== undefined) { + annotation_div.text(JSON.stringify(example.annotations)); + } else { // llm_gen mode + annotation_div.text(example.output); + } + $(`#annotCard${rowId}`).show(); + // update the status + const status_button = $(`#statusBtn${rowId}`); + setExampleStatus("finished", status_button); +} + +function finalizeCampaign(campaignId) { + console.log("Closing the connection"); + + setCampaignStatus(campaignId, "finished"); + $(`#run-button-${campaignId}`).hide(); + $(`#stop-button-${campaignId}`).hide(); + $(`#download-button-${campaignId}`).show(); + + if (window.mode == "llm_gen") { + $("#save-generations-button").show(); + } + +} + +function startLLMCampaignListener(campaignId) { + var source = new EventSource(`${url_prefix}/llm_campaign/progress/${campaignId}`); + console.log(`Listening for progress events for campaign ${campaignId}`); + + source.onmessage = function (event) { + // update the progress bar + var payload = JSON.parse(event.data); + + if (payload.type === "status") { + console.log(payload.message); + } + else if (payload.type === "result") { + showResult(payload, campaignId); + + if (payload.stats.finished == payload.stats.total) { + source.close(); + finalizeCampaign(campaignId); + } + } + }; +} + + +function updateCampaignConfig(campaignId) { + // collect values of all .campaign-metadata textareas, for each input also extract the key in `data-key` + var config = {}; + $(`.campaign-metadata-${campaignId}`).each(function () { + const key = $(this).data("key"); + const value = $(this).val(); + config[key] = value; + }); + + $.post({ + url: `${url_prefix}/llm_campaign/update_metadata`, + contentType: 'application/json', + data: JSON.stringify({ + campaignId: campaignId, + config: config + }), + success: function (response) { + console.log(response); + $(".update-config-btn").removeClass("btn-danger").addClass("btn-success").text("Saved!"); + + setTimeout(function () { + $(`#config-modal-${campaignId}`).modal('hide'); + $(".update-config-btn").removeClass("btn-success").addClass("btn-danger").text("Update configuration"); + }, 1500); + + } + }); +} + +function updateCrowdsourcingConfig() { + const crowdsourcingConfig = $('#crowdsourcingConfig').val(); + + if (crowdsourcingConfig === "[None]") { + annotatorInstructionsMDE.value(""); + // $("#annotatorPrompt").val(""); + finalMessageMDE.value(""); + $("#examplesPerBatch").val(""); + $("#annotatorsPerExample").val(""); + $("#idleTime").val(""); + $("#annotation-span-categories").empty(); + $("#flags").empty(); + $("#options").empty(); + $("#textFields").empty(); + return; + } + const cfg = window.configs[crowdsourcingConfig]; + + const annotatorInstructions = cfg.annotator_instructions; + const finalMessage = cfg.final_message; + const examplesPerBatch = cfg.examples_per_batch; + const annotatorsPerExample = cfg.annotators_per_example; + const idleTime = cfg.idle_time; + const annotationGranularity = cfg.annotation_granularity; + const service = cfg.service; + const sortOrder = cfg.sort_order; + const annotationSpanCategories = cfg.annotation_span_categories; + const flags = cfg.flags; + const options = cfg.options; + const textFields = cfg.text_fields; + + annotatorInstructionsMDE.value(annotatorInstructions); + // $("#annotatorPrompt").val(annotatorPrompt); + finalMessageMDE.value(finalMessage); + $("#examplesPerBatch").val(examplesPerBatch); + $("#annotatorsPerExample").val(annotatorsPerExample); + $("#idleTime").val(idleTime); + $("#annotationGranularity").val(annotationGranularity); + $("#service").val(service); + $("#sortOrder").val(sortOrder); + $("#annotation-span-categories").empty(); + + annotationSpanCategories.forEach((annotationSpanCategory) => { + const newCategory = createAnnotationSpanCategoryElem(annotationSpanCategory.name, annotationSpanCategory.color, annotationSpanCategory.description); + $("#annotation-span-categories").append(newCategory); + }); + $("#flags").empty(); + + if (flags !== undefined) { + flags.forEach((flag) => { + const newFlag = createFlagElem(flag); + $("#flags").append(newFlag); + }); + } + + $("#options").empty(); + + if (options !== undefined) { + options.forEach((option) => { + const newOption = createOptionElem(option.type, option.label, option.values.join(", ")); + $("#options").append(newOption); + }); + } + + $("#textFields").empty(); + + if (textFields !== undefined) { + textFields.forEach((textField) => { + const newTextField = createTextFieldElem(textField); + $("#textFields").append(newTextField); + }); + } +} + + + +function updateLLMMetricConfig() { + const llmConfigValue = $('#llmConfig').val(); + + if (llmConfigValue === "[None]") { + $("#model-name").val(""); + $("#prompt-template").val(""); + $("#system-message").val(""); + $("#api-url").val(""); + $("#model-arguments").empty(); + $("#annotation-span-categories").empty(); + $("#extra-arguments").empty(); + return; + } + const cfg = window.configs[llmConfigValue]; + + const metric_type = cfg.type; + const model_name = cfg.model; + const prompt_template = cfg.prompt_template; + const system_msg = cfg.system_msg; + const api_url = cfg.api_url; + const model_args = cfg.model_args; + const extra_args = cfg.extra_args; + + // for metric, we need to select the appropriate one from the values in the select box + $("#metric-type").val(metric_type); + $("#model-name").html(model_name); + $("#prompt-template").html(prompt_template); + $("#system-message").html(system_msg); + $("#api-url").html(api_url); + $("#model-arguments").empty(); + $("#extra-arguments").empty(); + + $.each(model_args, function (key, value) { + const newArg = createArgElem(key, value); + $("#model-arguments").append(newArg); + }); + + $.each(extra_args, function (key, value) { + const newArg = createArgElem(key, value); + $("#extra-arguments").append(newArg); + }); + + if (mode == "llm_eval") { + const annotationSpanCategories = cfg.annotation_span_categories; + $("#annotation-span-categories").empty(); + + annotationSpanCategories.forEach((annotationSpanCategory) => { + const newCategory = createAnnotationSpanCategoryElem(annotationSpanCategory.name, annotationSpanCategory.color, annotationSpanCategory.description); + $("#annotation-span-categories").append(newCategory); + }); + } + if (mode == "llm_gen") { + const start_with = cfg.start_with; + $("#start-with").val(start_with); + } +} diff --git a/factgenie/static/js/config.js b/factgenie/static/js/config.js new file mode 100644 index 00000000..78c28ab5 --- /dev/null +++ b/factgenie/static/js/config.js @@ -0,0 +1,44 @@ + +function updateConfig() { + const config = { + logging: { + flask_debug: $('#debug').is(':checked'), + level: $('#logging_level').val(), + }, + host_prefix: $('#host_prefix').val(), + login: { + active: $('#login_active').is(':checked'), + username: $('#login_username').val(), + password: $('#login_password').val() + } + }; + + $.post({ + url: `${url_prefix}/update_config`, + contentType: 'application/json', // Specify JSON content type + data: JSON.stringify(config), + success: function (response) { + alert('Configuration updated successfully'); + }, + error: function (error) { + alert('Error updating configuration: ' + error.responseText); + } + }); + +} + + +$(document).ready(function () { + $("#show_hide_password a").on('click', function (event) { + event.preventDefault(); + if ($('#show_hide_password input').attr("type") == "text") { + $('#show_hide_password input').attr('type', 'password'); + $('#show_hide_password i').addClass("fa-eye-slash"); + $('#show_hide_password i').removeClass("fa-eye"); + } else if ($('#show_hide_password input').attr("type") == "password") { + $('#show_hide_password input').attr('type', 'text'); + $('#show_hide_password i').removeClass("fa-eye-slash"); + $('#show_hide_password i').addClass("fa-eye"); + } + }); +}); \ No newline at end of file diff --git a/factgenie/static/js/datasets.js b/factgenie/static/js/datasets.js index 724485d8..bf159d61 100644 --- a/factgenie/static/js/datasets.js +++ b/factgenie/static/js/datasets.js @@ -12,6 +12,17 @@ function getSelectedSplits() { return selectedSplits; } +function addDatasetButton(dataset) { + $('#datasets-container').append(` +
    + + +
    +`); +} + + function addSplitButton(split) { if ($('#splits-container').find(`#btn-check-split-${split}`).length > 0) { return; @@ -38,6 +49,50 @@ function addOutputButton(output) { `); } + +function gatherComparisonData() { + var campaign_datasets = []; + var campaign_splits = []; + var campaign_outputs = []; + + $(".btn-check-dataset").each(function () { + if ($(this).prop("checked")) { + campaign_datasets.push($(this).attr("data-content")); + } + }); + $(".btn-check-split").each(function () { + if ($(this).prop("checked")) { + campaign_splits.push($(this).attr("data-content")); + } + }); + $(".btn-check-output").each(function () { + if ($(this).prop("checked")) { + campaign_outputs.push($(this).attr("data-content")); + } + }); + var combinations = []; + + if (mode == "llm_eval" || mode == "crowdsourcing") { + // Select all the available combinations according to the selection + combinations = available_data.filter(function (model_out) { + return campaign_datasets.includes(model_out.dataset) && campaign_splits.includes(model_out.split) && campaign_outputs.includes(model_out.setup_id); + }); + // Remove duplicates + combinations = combinations.filter((v, i, a) => a.findIndex(t => (t.dataset === v.dataset && t.split === v.split && t.setup_id === v.setup_id)) === i); + + } else if (mode == "llm_gen") { + // Select all the available combinations according to the selection + combinations = available_data.filter(function (model_out) { + return campaign_datasets.includes(model_out.dataset) && campaign_splits.includes(model_out.split); + }); + // Remove duplicates + combinations = combinations.filter((v, i, a) => a.findIndex(t => (t.dataset === v.dataset && t.split === v.split)) === i); + } + + return combinations; +} + + function sortCheckboxes(container) { // Sort all the checkboxes in the given container alphabetically const checkboxes = container.find('.form-check-input'); @@ -50,6 +105,15 @@ function sortCheckboxes(container) { }); } +function populateDatasets() { + for (const dataset of Object.keys(datasets)) { + addDatasetButton(dataset); + } + sortCheckboxes($('#datasets-container')); +} + + + function updateComparisonData() { const selectedDatasets = getSelectedDatasets(); const selectedSplits = getSelectedSplits(); @@ -81,17 +145,26 @@ function updateComparisonData() { $('#splits-container').empty(); $('#outputs-container').empty(); - for (const dataset of selectedDatasets) { - Object.keys(model_outs[dataset]).forEach(split => addSplitButton(split)); + // unique splits + const splits = available_data + .filter(model_out => selectedDatasets.includes(model_out.dataset)) + .map(model_out => model_out.split) + .filter((value, index, self) => self.indexOf(value) === index); - for (const split of selectedSplits) { - // if the split is not available in the dataset, skip - if (!model_outs[dataset][split]) { - continue; - } - Object.keys(model_outs[dataset][split]).forEach(output => addOutputButton(output)); - } + for (const split of splits) { + addSplitButton(split); } + + // unique outputs + const outputs = available_data + .filter(model_out => selectedDatasets.includes(model_out.dataset) && selectedSplits.includes(model_out.split)) + .map(model_out => model_out.setup_id) + .filter((value, index, self) => self.indexOf(value) === index); + + for (const output of outputs) { + addOutputButton(output); + } + // Sort all the checkboxes alphabetically sortCheckboxes($('#splits-container')); sortCheckboxes($('#outputs-container')); @@ -113,5 +186,43 @@ function updateComparisonData() { } +function updateSelectedDatasets() { + var selectedData = gatherComparisonData(); + + if (mode == 'llm_eval' || mode == 'crowdsourcing') { + $("#selectedDatasetsContent").html( + selectedData.map(d => + `
  • + + + + + + ` + ).join("\n") + ); + } else if (mode == 'llm_gen') { + $("#selectedDatasetsContent").html( + selectedData.map(d => + ` + + + + + ` + ).join("\n") + ); + } +} + + $(document).on('change', '.btn-check-dataset', updateComparisonData); $(document).on('change', '.btn-check-split', updateComparisonData); + +$(document).on('change', "#data-select-area input[type='checkbox']", function () { + updateSelectedDatasets(); +}); + +$(document).ready(function () { + populateDatasets(); +}); \ No newline at end of file diff --git a/factgenie/static/js/example-annotation.js b/factgenie/static/js/example-annotation.js new file mode 100644 index 00000000..a52873d1 --- /dev/null +++ b/factgenie/static/js/example-annotation.js @@ -0,0 +1,175 @@ +// Initialize an array to store annotations +let annotations = []; + +// Function to update the outputarea with the annotations JSON +function updateOutputArea() { + const output = { "annotations": annotations.map(({ id, ...rest }) => rest) }; + $('#outputarea').text(JSON.stringify(output, null, 4)); +} + +// Function to handle adding a new annotation +function onAnnotationAdded(annotation) { + // Extract the necessary information from the annotation + const categoryIndex = annotation.attributes.type; + const annotatedText = annotation.attributes.text; + const annotationId = annotation.cid; + + // Add the annotation to the annotations array + annotations.push({ + id: annotationId, + type: categoryIndex, + text: annotatedText, + reason: "" + }); + + // Add a new row to the errorarea + const row = $(` + + + + + + `); + $('#errorarea').append(row); + + // Attach event listener to the reason input + row.find('.reason-input').on('input', function () { + const id = $(this).data('id'); + const reason = $(this).val(); + // Update the reason in the annotations array + const annotation = annotations.find(a => a.id === id); + if (annotation) { + annotation.reason = reason; + updateOutputArea(); + } + }); + + // Update the output area + updateOutputArea(); +} + +// Function to handle deleting an annotation +function onAnnotationDeleted(annotation) { + const annotationId = annotation.cid; + + // Remove the annotation from the annotations array + annotations = annotations.filter(a => a.id !== annotationId); + + // Remove the corresponding row from errorarea + $(`#error-row-${annotationId}`).remove(); + + // Update the output area + updateOutputArea(); +} + +function pasteExampleIntoPrompt() { + const exampleData = $('#example-data').val(); + const exampleText = $('#example-text').val(); + const output = $('#outputarea').text(); + const prompt = `\n\n*Example:*\ninput:\n\`\`\`\n${exampleData}\n\`\`\`\ntext:\n\`\`\`\n${exampleText}\n\`\`\`\noutput:\n\`\`\`\n${output}\n`; + $('#prompt-template').val($('#prompt-template').val() + prompt); + $('#exampleAnnotation').modal('hide'); +} + +function createButtons() { + // if buttons already exist, remove them + $('#buttons-area').empty(); + + const annotationSpanCategories = getAnnotationSpanCategories(); + + for (const [idx, category] of Object.entries(annotationSpanCategories)) { + const input = $('', { + type: "radio", + class: "btn-check btn-outline-secondary btn-err-cat", + name: "btnradio", + id: `btnradio${idx}`, + autocomplete: "off", + "data-cat-idx": idx + }); + + const label = $('
    ', { - id: `out-text-${annotation_idx}`, - class: `annotate-box `, - style: 'display: none;' - }).appendTo('#outputarea'); - - // filter the data to only include the setup we want - const setup_id = annotation_set[annotation_idx].setup_id; - - data.generated_outputs = data.generated_outputs.filter(o => o.setup_id == setup_id)[0]; - - examples_cached[annotation_idx] = data; - - resolve(); - }).fail(function () { - reject(); - }); - }); -} - -function loadAnnotations() { - $("#dataset-spinner").show(); - - const promises = []; - const annotation_span_categories = metadata.config.annotation_span_categories; - - // prefetch the examples for annotation: we need them for YPet initialization - for (const [annotation_idx, example] of Object.entries(annotation_set)) { - const dataset = example.dataset; - const split = example.split; - const example_idx = example.example_idx; - const promise = fetchAnnotation(dataset, split, example_idx, annotation_idx); - promises.push(promise); - } - Promise.all(promises) - .then(() => { - - YPet.addInitializer(function (options) { - /* Configure the # and colors of Annotation types (minimum 1 required) */ - YPet.AnnotationTypes = new AnnotationTypeList(annotation_span_categories); - var regions = {}; - var paragraphs = {}; - - for (const [annotation_idx, data] of Object.entries(examples_cached)) { - - var p = new Paragraph({ 'text': data.generated_outputs.out, 'granularity': metadata.config.annotation_granularity }); - - paragraphs[`p${annotation_idx}`] = p; - regions[`p${annotation_idx}`] = `#out-text-${annotation_idx}`; - - const li = $('
  • ', { class: "page-item" }); - const a = $('', { class: "page-link bg-incomplete", style: "min-height: 28px;", id: `page-link-${annotation_idx}` }).text(annotation_idx); - li.append(a); - $("#nav-example-cnt").append(li); - - // switch to the corresponding example when clicking on the page number - $(`#page-link-${annotation_idx}`).click(function () { - goToPage(annotation_idx); - }); - } - YPet.addRegions(regions); - - for (const [p, p_obj] of Object.entries(paragraphs)) { - YPet[p].show(new WordCollectionView({ collection: p_obj.get('words') })); - - YPet[p].currentView.collection.parentDocument.get('annotations').on('remove', function (model, collection) { - if (collection.length == 0) { - collection = []; - } - }); - } - goToAnnotation(0); - }); - YPet.start(); - }) - .catch((e) => { - // Handle errors if any request fails - console.error("One or more requests failed."); - // Log the error - console.error(e); - - }) - .finally(() => { - // This block will be executed regardless of success or failure - $("#dataset-spinner").hide(); - }); -} - -function submitAnnotations(campaign_id) { - // remove `words` from the annotations: they are only used by the YPet library - for (const example of annotation_set) { - for (const annotation of example.annotations) { - delete annotation.words; - } - } - - $.post({ - url: `${url_prefix}/submit_annotations`, - contentType: 'application/json', // Specify JSON content type - data: JSON.stringify({ - campaign_id: metadata.id, - annotator_id: annotator_id, - annotation_set: annotation_set - } - ), - success: function (response) { - console.log(response); - window.onbeforeunload = null; - - if (response.success !== true) { - $("#error-message").html(response.error); - $("#overlay-fail").show(); - } else { - $("#final-message").html(response.message); - $("#overlay-end").show(); - } - }, - error: function (response) { - console.log(response); - $("#overlay-fail").show(); - } - }); -} - -function collectFlags() { - const flags = []; - $(".crowdsourcing-flag").each(function () { - const label = $(this).find("label").text().trim(); - const value = $(this).find("input[type='checkbox']").prop("checked"); - flags.push({ - label: label, - value: value - }); - }); - return flags; -} - -function collectOptions() { - const options = []; - - $(".crowdsourcing-option").each(function (x) { - if ($(this).hasClass("option-select")) { - const type = "select"; - const label = $(this).find("label").text().trim(); - const index = $(this).find("select").val(); - const value = $(this).find("select option:selected").text(); - options.push({ type: type, label: label, index: index, value: value }); - } else if ($(this).hasClass("option-slider")) { - const type = "slider"; - const label = $(this).find("label").text(); - const index = $(this).find("input[type='range']").val(); - const value = $(this).find("datalist option")[index].value; - options.push({ type: type, label: label, index: index, value: value }); - } - }); - return options; -} - -function collectTextFields() { - const textFields = []; - - $(".crowdsourcing-text").each(function (x) { - const label = $(this).find("label").text().trim(); - const value = $(this).find("input[type='text']").val(); - textFields.push({ label: label, value: value }); - }); - return textFields; -} - - -function saveCurrentAnnotations(example_idx) { - var collection = YPet[`p${example_idx}`].currentView.collection.parentDocument.get('annotations').toJSON(); - annotation_set[example_idx]["annotations"] = collection; - annotation_set[example_idx]["flags"] = collectFlags(); - annotation_set[example_idx]["options"] = collectOptions(); - annotation_set[example_idx]["textFields"] = collectTextFields(); -} - -function clearExampleLevelFields() { - // uncheck all checkboxes - $(".crowdsourcing-flag input[type='checkbox']").prop("checked", false); - - // reset all options to the first value - $(".crowdsourcing-option select").val(0); - $(".crowdsourcing-option input[type='range']").val(0); - - // clear the values in text inputs - $(".crowdsourcing-text input[type='text']").val(""); -} - - -function markAnnotationAsComplete() { - $('#page-link-' + current_example_idx).removeClass("bg-incomplete"); - $('#page-link-' + current_example_idx).addClass("bg-complete"); - - // if all the examples are annotated, post the annotations - if ($(".bg-incomplete").length == 0) { - saveCurrentAnnotations(current_example_idx); - - // show the `submit` button - $("#submit-annotations-btn").show(); - - // scroll to the top - $('html, body').animate({ - scrollTop: $("#submit-annotations-btn").offset().top - }, 500); - - } else if (current_example_idx < total_examples - 1) { - // annotations will be saved automatically - nextBtn(); - } -} - -function showRawData(data) { - var rawDataStr = JSON.stringify(data.raw_data, null, 2).replace(/\\n/g, '
    '); - - if (rawDataStr[0] == '"') { - // remove the first and last double quotes - rawDataStr = rawDataStr.slice(1, -1); - } - rawDataStr = rawDataStr.replace(/&/g, '&').replace(//g, '>'); - $("#rawarea").html(`
    ${rawDataStr}
    `); -} - -function goToAnnotation(example_idx) { - $(".page-link").removeClass("bg-active"); - $(`#page-link-${example_idx}`).addClass("bg-active"); - - $(".annotate-box").hide(); - $(`#out-text-${example_idx}`).show(); - - const data = examples_cached[example_idx]; - $("#examplearea").html(data.html); - - const flags = annotation_set[example_idx].flags; - const options = annotation_set[example_idx].options; - const textFields = annotation_set[example_idx].textFields; - - clearExampleLevelFields(); - - if (flags !== undefined) { - $(".crowdsourcing-flag").each(function (i) { - $(this).find("input[type='checkbox']").prop("checked", flags[i]["value"]); - }); - } - - if (options !== undefined) { - for (const [i, option] of Object.entries(options)) { - const div = $(`.crowdsourcing-option:eq(${i})`); - // we can have either a select or a slider (we can tell by `type`) - // we need to set the option defined by `index` - if (option.type == "select") { - div.find("select").val(option.index); - } - if (option.type == "slider") { - div.find("input[type='range']").val(option.index); - } - } - } - - if (textFields !== undefined) { - for (const [i, textField] of Object.entries(textFields)) { - $(`.crowdsourcing-text input:eq(${i})`).val(textField.value); - } - } - -} - -function goToBtn() { - var n = $("#page-input").val(); - goToPage(n); -} - -function toggleRaw() { - // toggle display: none on rawarea and examplearea - $("#rawarea").toggle(); - $("#examplearea").toggle(); -} - - -function changeDataset() { - $("#dataset-spinner").show(); - const dataset = $('#dataset-select').val(); - - // set available splits in #split-select - $('#split-select').empty(); - for (const split of datasets[dataset].splits) { - $('#split-select').append(``); - } - const split = $('#split-select').val(); - - current_example_idx = 0; - fetchExample(dataset, split, current_example_idx); - $("#page-input").val(current_example_idx); -} - -function changeSplit() { - $("#dataset-spinner").show(); - const dataset = $('#dataset-select').val(); - const split = $('#split-select').val(); - current_example_idx = 0; - fetchExample(dataset, split, current_example_idx); - $("#page-input").val(current_example_idx); -} - -function changeExample(dataset, split, example_idx) { - // used only for direct links - $("#dataset-spinner").show(); - $('#dataset-select').val(dataset); - $('#split-select').empty(); - for (const split of datasets[dataset].splits) { - $('#split-select').append(``); - } - $('#split-select').val(split); - current_example_idx = example_idx; - fetchExample(dataset, split, example_idx); - $("#page-input").val(example_idx); -} - -function getAnnotatedOutput(output, campaign_id) { - const setup_id = output.setup_id; - // replace newlines with any spaces around them with
    - const content = output.out.replace(/\\n/g, '
    '); - - // if the campaign_id is in output.annotations, show the annotated content - const annotations_campaign = output.annotations.filter(a => a.metadata.id == campaign_id); - - var placeholder = $('
    ', { id: `out-${setup_id}-${campaign_id}-placeholder`, class: `font-mono out-placeholder out-${campaign_id}-placeholder` });
    -    var annotated_content;
    -
    -    if (annotations_campaign.length > 0) {
    -        const annotations = annotations_campaign[0];
    -        const annotation_span_categories = annotations.metadata.config.annotation_span_categories;
    -
    -        annotated_content = annotateContent(content, annotations, annotation_span_categories);
    -    } else {
    -        // we do not have outputs for the particular campaign -> grey out the text
    -        if (campaign_id != "original") {
    -            placeholder.css("color", "#c2c2c2");
    -        }
    -        annotated_content = content;
    -    }
    -    placeholder.html(annotated_content);
    -    // placeholder.hide();
    -    return placeholder;
    -}
    -
    -// Our custom function to highlight the text spans using the collected annotations
    -// Here we do *not* use the YPet library, but directly work with HTML
    -function annotateContent(content, annotations, annotation_span_categories) {
    -    let offset = 0; // Track cumulative offset
    -    const annotationSet = annotations.annotations;
    -
    -    // sort by start
    -    annotationSet.sort(function (a, b) {
    -        return a.start - b.start;
    -    });
    -    var html = content;
    -
    -    annotationSet.forEach(annotation => {
    -        const annotationType = annotation.type;
    -
    -        if (!(annotationType in annotation_span_categories)) {
    -            console.log("Warning: annotation type not found in annotation_span_categories: " + annotationType);
    -            return;
    -        }
    -        const color = annotation_span_categories[annotationType].color;
    -        const text = annotation.text.trimEnd();
    -
    -        const start = annotation.start + offset;
    -        const end = start + text.length;
    -
    -        const error_name = annotation_span_categories[annotationType].name;
    -        const note = annotation.reason || annotation.note;
    -        let tooltip_text;
    -
    -        if (note !== undefined && note !== "" && note !== null) {
    -            tooltip_text = `${error_name} (${note})`;
    -        } else {
    -            tooltip_text = `${error_name}`;
    -        }
    -
    -        const spanId = `span-${start}-${end}`;
    -        const spanContent = `${text}`;
    -
    -        html = html.slice(0, start) + spanContent + html.slice(end);
    -        // Update the offset
    -        offset += spanContent.length - text.length;
    -    });
    -    return html;
    -}
    -
    -function updateDisplayedAnnotations() {
    -    const activeButtons = $('.btn-ann-select.active');
    -    selected_campaigns = activeButtons.map(function () {
    -        return $(this).data('ann');
    -    }).get();
    -    // hide all placeholders
    -    $(".output-box").hide();
    -
    -    // if no campaign ids, show the original output
    -    if (selected_campaigns.length == 0) {
    -        $(".box-original").show();
    -    }
    -    for (const campaign_id of selected_campaigns) {
    -        // show the selected annotator
    -        $(`.box-${campaign_id}`).show();
    -    }
    -    enableTooltips();
    -}
    -
    -
    -function getExampleLevelFields(output, campaign_id) {
    -    const fieldsCampaign = output.annotations.filter(a => a.metadata.id == campaign_id)[0];
    -
    -    if (fieldsCampaign === undefined) {
    -        return null;
    -    }
    -    // show `outputs.flags`, `outputs.options`, and `outputs.textFields`
    -    var flags = fieldsCampaign.flags;
    -    var options = fieldsCampaign.options;
    -    var textFields = fieldsCampaign.textFields;
    -
    -    var html = $('
    ', { class: "p-2 extra-fields" }); - - if (flags !== undefined && flags.length > 0) { - var flagsDiv = $('
    ', { class: "small" }); - // flagsDiv.append($('').html("Flags")); - for (const flag of flags) { - var labelDiv = $('
    ', { class: "small text-muted " }).text(`${flag.label}`); - var valueDiv = $('
    ', { class: "small mb-1 fw-bold" }).text(`${flag.value}`); - - flagsDiv.append(labelDiv); - flagsDiv.append(valueDiv); - } - html.append(flagsDiv); - } - - if (options !== undefined && options.length > 0) { - var optionsDiv = $('
    ', { class: "small" }); - - for (const option of options) { - var labelDiv = $('
    ', { class: "small text-muted" }).text(`${option.label}`); - var valueDiv = $('
    ', { class: "small mb-1 fw-bold" }).text(`${option.value}`); - - optionsDiv.append(labelDiv); - optionsDiv.append(valueDiv); - } - html.append(optionsDiv); - } - - if (textFields !== undefined && textFields.length > 0) { - var textFieldsDiv = $('
    ', { class: "small" }); - - for (const textField of textFields) { - var labelDiv = $('
    ', { class: "small text-muted" }).text(`${textField.label}`); - var valueDiv = $('
    ', { class: "small mb-1 fw-bold" }).text(`${textField.value}`); - - textFieldsDiv.append(labelDiv); - textFieldsDiv.append(valueDiv); - } - html.append(textFieldsDiv); - } - return html; -} - - -function createOutputBox(content, exampleLevelFields, campaign_id, setup_id) { - var card = $('
    ', { class: `card output-box generated-output-box box-${setup_id} box-${campaign_id} box-${setup_id}-${campaign_id}` }); - - var annotationBadge = (campaign_id !== "original") ? ` ${campaign_id}` : "" - var headerHTML = `
    - ${setup_id} - ${annotationBadge} -
    - ` - - var cardHeader = $('
    ', { class: "card-header card-header-collapse small", "data-bs-toggle": "collapse", "data-bs-target": `#out-${setup_id}-${campaign_id}` }).html(headerHTML); - var cardBody = $('
    ', { class: "card-body show", id: `out-${setup_id}-${campaign_id}`, "aria-expanded": "true" }); - // var cardTitle = $('
    ', { class: "card-title" }).text(setup_id); - var cardText = $('
    ', { class: "card-text mt-2" }).html(content); - - // cardBody.append(cardTitle); - cardBody.append(cardText); - card.append(cardHeader); - card.append(cardBody); - - if (exampleLevelFields !== null) { - cardBody.append(exampleLevelFields); - } - - return card; -} - -function createOutputBoxes(generated_outputs) { - // clear the output area - $("#outputarea").empty(); - - // sort outputs by setup id - generated_outputs.sort(function (a, b) { - return a.setup_id.localeCompare(b.setup_id); - }); - - // find all campaign ids in output annotations - const campaign_ids = new Set(); - - generated_outputs.forEach(output => { - output.annotations.forEach(annotation => { - campaign_ids.add(annotation.metadata.id); - }); - }); - const selectBox = $("#annotations-select"); - // clear the selectbox - selectBox.empty(); - - // add an option for each campaign id - for (const campaign_id of campaign_ids) { - const button = $(``); - button.on('click', function () { - $(this).toggleClass('active'); - updateDisplayedAnnotations(); - }); - selectBox.append(button); - } - - if (campaign_ids.size > 0) { - $("#setuparea").show(); - } else { - $("#setuparea").hide(); - } - - // add the annotated outputs - for (const output of generated_outputs) { - var groupDiv = $('
    ', { class: `output-group box-${output.setup_id} d-inline-flex gap-2` }); - groupDiv.appendTo("#outputarea"); - - const plain_output = getAnnotatedOutput(output, "original"); - card = createOutputBox(plain_output, null, "original", output.setup_id); - card.appendTo(groupDiv); - - for (const campaign_id of campaign_ids) { - const annotated_output = getAnnotatedOutput(output, campaign_id); - const exampleLevelFields = getExampleLevelFields(output, campaign_id); - card = createOutputBox(annotated_output, exampleLevelFields, campaign_id, output.setup_id); - card.appendTo(groupDiv); - card.hide(); - } - } - -} - -function showSelectedCampaigns() { - // if the annotator is still among the values, restore it - // prevent resetting to the first annotation when switching examples - $(".btn-ann-select").each(function () { - if (selected_campaigns.includes($(this).data('ann'))) { - $(this).addClass("active").trigger("change"); - } else { - $(this).removeClass("active"); - } - }); -} - -function fetchExample(dataset, split, example_idx) { - // change the URL so that it shows the permalink - const newUrl = `${url_prefix}/browse?dataset=${dataset}&split=${split}&example_idx=${example_idx}`; - - // the check prevents being stuck at the same URL - if (!window.location.href.includes(newUrl)) { - history.pushState(null, '', newUrl); - } - - $.get(`${url_prefix}/example`, { - "dataset": dataset, - "example_idx": example_idx, - "split": split, - }, function (data) { - $("#dataset-spinner").hide(); - $("#examplearea").html(data.html); - showRawData(data); - - total_examples = data.total_examples; - $("#total-examples").html(total_examples - 1); - - createOutputBoxes(data.generated_outputs); - showSelectedCampaigns(); - updateDisplayedAnnotations(); - }); -} - - -$("#dataset-select").on("change", changeDataset); -$("#split-select").on("change", changeSplit); - -if (mode == "annotate") { - $('.btn-check').on('change', function () { - $('.btn-check').each(function () { - const label = $(`label[for=${this.id}]`); - if (this.checked) { - label.addClass('active'); - } else { - label.removeClass('active'); - } - }); - }); -} - - -$(document).keydown(function (event) { - const key = event.key; - - if (mode == "browse") { - if (key === "ArrowRight") { - event.preventDefault(); - nextBtn(); - } else if (key === "ArrowLeft") { - event.preventDefault(); - prevBtn(); - } - } -}); - -$('#page-input').keypress(function (event) { - // Enter = Go to page - if (event.keyCode == 13) { - goToBtn(); - } -}); - -$("#hideOverlayBtn").click(function () { - $("#overlay-start").fadeOut(); -}); - -$(document).on('change', "#data-select-area input[type='checkbox']", function () { - updateSelectedDatasets(); -}); - -$(".btn-err-cat").change(function () { - if (this.checked) { - const cat_idx = $(this).attr("data-cat-idx"); - YPet.setCurrentAnnotationType(cat_idx); - } -}); - -function updateSelectedDatasets() { - var selectedData = gatherComparisonData(); - - if (mode == 'llm_eval' || mode == 'crowdsourcing') { - $("#selectedDatasetsContent").html( - selectedData.map(d => - `
  • - - - - - - ` - ).join("\n") - ); - } else if (mode == 'llm_gen') { - $("#selectedDatasetsContent").html( - selectedData.map(d => - ` - - - - - ` - ).join("\n") - ); - } -} - -function gatherSelectedCombinations() { - // read all the rows the remained in #selectedDatasetsContent - var selectedData = []; - $("#selectedDatasetsContent tr").each(function () { - var dataset = $(this).find("td:eq(0)").text(); - var split = $(this).find("td:eq(1)").text(); - if (mode != "llm_gen") { - var setup_id = $(this).find("td:eq(2)").text(); - selectedData.push({ dataset: dataset, split: split, setup_id: setup_id }); - } else { - selectedData.push({ dataset: dataset, split: split }); - } - }); - return selectedData; -} - - -function gatherComparisonData() { - var campaign_datasets = []; - var campaign_splits = []; - var campaign_outputs = []; - - $(".btn-check-dataset").each(function () { - if ($(this).prop("checked")) { - campaign_datasets.push($(this).attr("data-content")); - } - }); - $(".btn-check-split").each(function () { - if ($(this).prop("checked")) { - campaign_splits.push($(this).attr("data-content")); - } - }); - $(".btn-check-output").each(function () { - if ($(this).prop("checked")) { - campaign_outputs.push($(this).attr("data-content")); - } - }); - var combinations = []; - - if (mode == "llm_eval" || mode == "crowdsourcing") { - // get all available combinations of datasets, splits, and outputs - for (const dataset of campaign_datasets) { - for (const split of campaign_splits) { - for (const output of campaign_outputs) { - if (model_outs[dataset][split] !== undefined && output in model_outs[dataset][split]) { - combinations.push({ dataset: dataset, split: split, setup_id: output, example_cnt: datasets[dataset].output_ids[split][output].length }); - } - } - } - } - } else if (mode == "llm_gen") { - // get all available combinations of datasets and splits - for (const dataset of campaign_datasets) { - for (const split of campaign_splits) { - if (split in model_outs[dataset]) { - combinations.push({ dataset: dataset, split: split, example_cnt: datasets[dataset].example_count[split] }); - } - } - } - } - - return combinations; - -} - -function gatherConfig() { - var config = {}; - - if (window.mode == "crowdsourcing") { - config.annotatorInstructions = annotatorInstructionsMDE.value(); - // config.annotatorPrompt = $("#annotatorPrompt").val(); - config.finalMessage = finalMessageMDE.value(); - config.hasDisplayOverlay = $("#displayOverlay").is(":checked"); - config.examplesPerBatch = $("#examplesPerBatch").val(); - config.idleTime = $("#idleTime").val(); - config.annotationGranularity = $("#annotationGranularity").val(); - config.service = $("#service").val(); - config.sortOrder = $("#sortOrder").val(); - config.annotationSpanCategories = getAnnotationSpanCategories(); - config.flags = getKeys($("#flags")); - config.options = getOptions(); - config.textFields = getKeys($("#textFields")); - } else if (window.mode == "llm_eval" || window.mode == "llm_gen") { - config.metricType = $("#metric-type").val(); - config.modelName = $("#model-name").val(); - config.promptTemplate = $("#prompt-template").val(); - config.systemMessage = $("#system-message").val(); - config.apiUrl = $("#api-url").val(); - config.modelArguments = getKeysAndValues($("#model-arguments")); - config.extraArguments = getKeysAndValues($("#extra-arguments")); - - if (window.mode == "llm_eval") { - config.annotationSpanCategories = getAnnotationSpanCategories(); - } - if (window.mode == "llm_gen") { - config.startWith = $("#start-with").val(); - } - } - return config; -} - -function deleteCampaign(campaignId, mode) { - // ask for confirmation - if (!confirm(`Are you sure you want to delete the campaign ${campaignId}? All the data will be lost!`)) { - return; - } - - $.post({ - url: `${url_prefix}/delete_campaign`, - contentType: 'application/json', // Specify JSON content type - data: JSON.stringify({ - campaignId: campaignId, - mode: mode, - }), - success: function (response) { - console.log(response); - - if (response.success !== true) { - alert(response.error); - } else { - // remove the campaign from the list - $(`#campaign-${campaignId}`).remove(); - - // reload the page - location.reload(); - } - } - }); -} - -function clearOutput(campaignId, mode, idx) { - // ask for confirmation - if (!confirm(`Are you sure you want to free the row id ${idx}? Any collected outputs will be deleted.`)) { - return; - } - $.post({ - url: `${url_prefix}/clear_output`, - contentType: 'application/json', // Specify JSON content type - data: JSON.stringify({ - campaignId: campaignId, - mode: mode, - idx: idx, - }), - success: function (response) { - console.log(response); - - if (response.success !== true) { - alert(response.error); - } else { - // reload the page - location.reload(); - } - } - }); -} - -function createLLMCampaign() { - const campaignId = $('#campaignId').val(); - // const llmConfig = $('#llmConfig').val(); - - const config = gatherConfig(); - var campaignData = gatherSelectedCombinations(); - - // if no datasets are selected, show an alert - if (campaignData.length == 0) { - alert("Please select at least one existing combination of dataset, split, and output."); - return; - } - - $.post({ - url: `${url_prefix}/llm_campaign/create?mode=${mode}`, - contentType: 'application/json', // Specify JSON content type - data: JSON.stringify({ - campaignId: campaignId, - campaignData: campaignData, - config: config - }), - success: function (response) { - console.log(response); - - if (response.success !== true) { - alert(response.error); - } else { - window.location.href = `${url_prefix}/llm_campaign?mode=${mode}`; - } - } - }); -} - - - -function getAnnotationSpanCategories() { - var annotationSpanCategories = []; - - $("#annotation-span-categories").children().each(function () { - const name = $(this).find("input[name='annotationSpanCategoryName']").val(); - const color = $(this).find("input[name='annotationSpanCategoryColor']").val(); - annotationSpanCategories.push({ name: name, color: color }); - }); - return annotationSpanCategories; -} - -function getKeysAndValues(div) { - var args = {}; - div.children().each(function () { - const key = $(this).find("input[name='argName']").val(); - const value = $(this).find("input[name='argValue']").val(); - args[key] = value; - }); - return args; -} - -function getKeys(div) { - var keys = []; - div.children().each(function () { - const key = $(this).find("input[name='argName']").val(); - keys.push(key); - }); - return keys; -} - -function getOptions() { - var options = []; - - $("#options").children().each(function () { - const type = $(this).find("select[name='optionType']").val(); - const label = $(this).find("input[name='optionLabel']").val(); - const values = $(this).find("input[name='optionValues']").val().split(",").map(v => v.trim()); - options.push({ type: type, label: label, values: values }); - }); - return options; -} - -function createHumanCampaign() { - const campaignId = $('#campaignId').val(); - const config = gatherConfig(); - var campaignData = gatherSelectedCombinations(); - - // if no datasets are selected, show an alert - if (campaignData.length == 0) { - alert("Please select at least one existing combination of dataset, split, and output."); - return; - } - - $.post({ - url: `${url_prefix}/crowdsourcing/create`, - contentType: 'application/json', // Specify JSON content type - data: JSON.stringify({ - campaignId: campaignId, - config: config, - campaignData: campaignData - }), - success: function (response) { - console.log(response); - - if (response.success !== true) { - alert(response.error); - } else { - window.location.href = `${url_prefix}/crowdsourcing`; - } - } - }); -} - - -function startLLMCampaignListener(campaignId) { - var source = new EventSource(`${url_prefix}/llm_campaign/progress/${campaignId}`); - console.log(`Listening for progress events for campaign ${campaignId}`); - - source.onmessage = function (event) { - // update the progress bar - var payload = JSON.parse(event.data); - var finished_examples = payload.stats.finished; - var total_examples = payload.stats.total; - var progress = Math.round((finished_examples / total_examples) * 100); - $(`#llm-progress-bar-${campaignId}`).css("width", `${progress}%`); - $(`#llm-progress-bar-${campaignId}`).attr("aria-valuenow", progress); - $(`#metadata-example-cnt-${campaignId}`).html(`${finished_examples} / ${total_examples}`); - console.log(`Received progress: ${progress}%`); - - // update the annotation button - const example = payload.annotation; - const dataset = example.dataset; - const split = example.split; - const setup_id = example.setup_id; - const example_idx = example.example_idx; - const rowId = `${dataset}-${split}-${setup_id}-${example_idx}`; - const annotation_button = $(`#annotBtn${rowId}`); - annotation_button.show(); - - // update the annotation content - const annotation_content = example.output; - const annotation_div = $(`#annotPre${rowId}`); - - // if annotation_content is a dict, convert it to a string - if (typeof annotation_content === 'object') { - annotation_div.text(JSON.stringify(annotation_content)); - } else { - annotation_div.text(annotation_content); - } - - // update the status - const status_button = $(`#statusBtn${rowId}`); - setExampleStatus("finished", status_button); - - if (progress == 100) { - source.close(); - console.log("Closing the connection"); - - - setCampaignStatus(campaignId, "finished"); - $(`#run-button-${campaignId}`).hide(); - $(`#stop-button-${campaignId}`).hide(); - $(`#download-button-${campaignId}`).show(); - - $("#log-area").text(payload.final_message); - - if (window.mode == "llm_gen") { - $("#save-generations-button").show(); - } - } - - }; -} - -function setCampaignStatus(campaignId, status) { - $(`#metadata-status-${campaignId}`).html(status); - $(`#metadata-status-${campaignId}`).removeClass("bg-idle bg-running bg-finished bg-error"); - $(`#metadata-status-${campaignId}`).addClass(`bg-${status}`); -} - -function setExampleStatus(status, button) { - button.removeClass("btn-free btn-finished"); - button.addClass(`btn-${status}`); -} - -function runLLMCampaign(campaignId) { - $(`#run-button-${campaignId}`).hide(); - $(`#stop-button-${campaignId}`).show(); - setCampaignStatus(campaignId, "running"); - - startLLMCampaignListener(campaignId); - - $.post({ - url: `${url_prefix}/llm_campaign/run?mode=${mode}`, - contentType: 'application/json', - data: JSON.stringify({ - campaignId: campaignId - }), - success: function (response) { - if (response.success !== true) { - alert(response.error); - $("#log-area").text(JSON.stringify(response.error)); - console.log(JSON.stringify(response)); - - setCampaignStatus(campaignId, "idle"); - $(`#run-button-${campaignId}`).show(); - $(`#stop-button-${campaignId}`).hide(); - } else { - console.log(response); - } - } - }); -} - -function pauseLLMCampaign(campaignId) { - $(`#run-button-${campaignId}`).show(); - $(`#stop-button-${campaignId}`).hide(); - setCampaignStatus(campaignId, "idle"); - - $.post({ - url: `${url_prefix}/llm_campaign/pause?mode=${mode}`, - contentType: 'application/json', - data: JSON.stringify({ - campaignId: campaignId - }), - success: function (response) { - console.log(response); - } - }); -} -function updateCampaignConfig(campaignId) { - // collect values of all .campaign-metadata textareas, for each input also extract the key in `data-key` - var config = {}; - $(`.campaign-metadata-${campaignId}`).each(function () { - const key = $(this).data("key"); - const value = $(this).val(); - config[key] = value; - }); - - $.post({ - url: `${url_prefix}/llm_campaign/update_metadata?mode=${mode}`, - contentType: 'application/json', - data: JSON.stringify({ - campaignId: campaignId, - config: config - }), - success: function (response) { - console.log(response); - $(".update-config-btn").removeClass("btn-danger").addClass("btn-success").text("Saved!"); - - setTimeout(function () { - $(`#config-modal-${campaignId}`).modal('hide'); - $(".update-config-btn").removeClass("btn-success").addClass("btn-danger").text("Update configuration"); - }, 1500); - - } - }); -} - -function clearCampaign(campaignId) { - // ask for confirmation - if (!confirm("Are you sure you want to clear all campaign outputs?")) { - return; - } - $.post({ - url: `${url_prefix}/clear_campaign`, - contentType: 'application/json', - data: JSON.stringify({ - campaignId: campaignId, - mode: mode - }), - success: function (response) { - console.log(response); - window.location.reload(); - } - }); -} - -function addAnnotationSpanCategory() { - const annotationSpanCategories = $("#annotation-span-categories"); - const randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16); - const newCategory = createAnnotationSpanCategoryElem("", randomColor); - annotationSpanCategories.append(newCategory); -} - -function addModelArgument() { - const modelArguments = $("#model-arguments"); - const newArg = createArgElem("", ""); - modelArguments.append(newArg); -} - -function addExtraArgument() { - const modelArguments = $("#extra-arguments"); - const newArg = createArgElem("", ""); - modelArguments.append(newArg); -} - -function addFlag() { - const flags = $("#flags"); - const newFlag = createFlagElem(""); - flags.append(newFlag); -} - -function addOption() { - const options = $("#options"); - const newOption = createOptionElem("select", "", ""); - options.append(newOption); -} - -function addTextField() { - const textFields = $("#textFields"); - const newTextField = createTextFieldElem(""); - textFields.append(newTextField); -} - - -function deleteRow(button) { - $(button).parent().parent().remove(); -} - -function createFlagElem(key) { - // text area and selectbox for the flag ("checked" or "unchecked" based on the value) - const newFlag = $(` -
    -
    - -
    -
    - -
    -
    - `); - return newFlag; -} - -function createOptionElem(type, label, values) { - // three columns: option type (selectbox, slider) text input for the label, and text input for comma-separated values - const newOption = $(` -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    - `); - return newOption; -} - -function createTextFieldElem(key) { - // text area and selectbox for the flag ("checked" or "unchecked" based on the value) - const newFlag = $(` -
    -
    - -
    -
    - -
    -
    - `); - return newFlag; -} - -function createArgElem(key, value) { - // escape quotes in the value - if (typeof value === 'string') { - value = value.replace(/"/g, """); - } - const newArg = $(` -
    -
    - -
    -
    - -
    -
    - -
    -
    - `); - return newArg; -} - -function createAnnotationSpanCategoryElem(name, color) { - const newCategory = $(` -
    -
    - -
    -
    - -
    -
    - -
    -
    - `); - return newCategory; -} - - -function duplicateConfig(btnElem, filenameElemId, modeTo, campaignId) { - const filename = $("#" + filenameElemId).val() + ".yaml"; - const modeFrom = window.mode; - - // TODO warn overwrite - $.post({ - url: `${url_prefix}/duplicate_config`, - contentType: 'application/json', // Specify JSON content type - data: JSON.stringify({ - campaignId: campaignId, - modeFrom: modeFrom, - modeTo: modeTo, - filename: filename, - }), - success: function (response) { - console.log(response); - - if (response.success !== true) { - alert(response.error); - } else { - // change color of the button save-cfg-submit to green for a second with the label "Saved!", then back to normal - const origText = $(btnElem).text(); - $(btnElem).removeClass("btn-primary").addClass("btn-success").text("Saved!"); - setTimeout(function () { - $('#save-cfg-modal').modal('hide'); - $(btnElem).removeClass("btn-success").addClass("btn-primary").text(origText); - }, 1500); - } - } - }); -} - -function duplicateEval(inputDuplicateId, campaignId) { - newCampaignId = $(`#${inputDuplicateId}`).val(); - - $.post({ - url: `${url_prefix}/duplicate_eval`, - contentType: 'application/json', // Specify JSON content type - data: JSON.stringify({ - campaignId: campaignId, - newCampaignId: newCampaignId, - mode: window.mode - }), - success: function (response) { - console.log(response); - - if (response.success !== true) { - alert(response.error); - } else { - // hide the modal and reload the page - $('#duplicate-eval-modal').modal('hide'); - location.reload(); - } - } - }); -} - -function saveGenerationOutputs(campaignId) { - modelName = $("#save-generations-model-name").val(); - - $.post({ - url: `${url_prefix}/save_generation_outputs`, - contentType: 'application/json', // Specify JSON content type - data: JSON.stringify({ - campaignId: campaignId, - modelName: modelName - }), - success: function (response) { - console.log(response); - - if (response.success !== true) { - alert(response.error); - } else { - // change color of the button save-cfg-submit to green for a second with the label "Saved!", then back to normal - $("#save-generations-submit").removeClass("btn-primary").addClass("btn-success").text("Saved!"); - setTimeout(function () { - $('#save-generations-modal').modal('hide'); - $("#save-generations-submit").removeClass("btn-success").addClass("btn-primary").text("Save"); - }, 1500); - } - } - }); -} - - -function saveConfig(mode) { - const filename = $("#config-save-filename").val() + ".yaml"; - const config = gatherConfig(); - - if (filename in window.configs) { - if (!confirm(`The configuration with the name ${filename} already exists. Do you want to overwrite it?`)) { - return; - } - } - $.post({ - url: `${url_prefix}/save_config`, - contentType: 'application/json', // Specify JSON content type - data: JSON.stringify({ - mode: mode, - filename: filename, - config: config - }), - success: function (response) { - console.log(response); - - if (response.success !== true) { - alert(response.error); - } else { - // change color of the button save-cfg-submit to green for a second with the label "Saved!", then back to normal - $("#save-cfg-submit").removeClass("btn-primary").addClass("btn-success").text("Saved!"); - setTimeout(function () { - $('#save-cfg-modal').modal('hide'); - $("#save-cfg-submit").removeClass("btn-success").addClass("btn-primary").text("Save"); - }, 1500); - } - } - }); - -} -function updateCrowdsourcingConfig() { - const crowdsourcingConfig = $('#crowdsourcingConfig').val(); - - if (crowdsourcingConfig === "[None]") { - annotatorInstructionsMDE.value(""); - // $("#annotatorPrompt").val(""); - finalMessageMDE.value(""); - $("#examplesPerBatch").val(""); - $("#idleTime").val(""); - $("#annotation-span-categories").empty(); - $("#flags").empty(); - $("#options").empty(); - $("#textFields").empty(); - return; - } - const cfg = window.configs[crowdsourcingConfig]; - - const annotatorInstructions = cfg.annotator_instructions; - const finalMessage = cfg.final_message; - const examplesPerBatch = cfg.examples_per_batch; - const idleTime = cfg.idle_time; - const annotationGranularity = cfg.annotation_granularity; - const service = cfg.service; - const sortOrder = cfg.sort_order; - const annotationSpanCategories = cfg.annotation_span_categories; - const flags = cfg.flags; - const options = cfg.options; - const textFields = cfg.text_fields; - - annotatorInstructionsMDE.value(annotatorInstructions); - // $("#annotatorPrompt").val(annotatorPrompt); - finalMessageMDE.value(finalMessage); - $("#examplesPerBatch").val(examplesPerBatch); - $("#idleTime").val(idleTime); - $("#annotationGranularity").val(annotationGranularity); - $("#service").val(service); - $("#sortOrder").val(sortOrder); - $("#annotation-span-categories").empty(); - - annotationSpanCategories.forEach((annotationSpanCategory) => { - const newCategory = createAnnotationSpanCategoryElem(annotationSpanCategory.name, annotationSpanCategory.color); - $("#annotation-span-categories").append(newCategory); - }); - $("#flags").empty(); - - if (flags !== undefined) { - flags.forEach((flag) => { - const newFlag = createFlagElem(flag); - $("#flags").append(newFlag); - }); - } - - $("#options").empty(); - - if (options !== undefined) { - options.forEach((option) => { - const newOption = createOptionElem(option.type, option.label, option.values.join(", ")); - $("#options").append(newOption); - }); - } - - $("#textFields").empty(); - - if (textFields !== undefined) { - textFields.forEach((textField) => { - const newTextField = createTextFieldElem(textField); - $("#textFields").append(newTextField); - }); - } -} - - -function updateLLMMetricConfig() { - const llmConfigValue = $('#llmConfig').val(); - - if (llmConfigValue === "[None]") { - $("#model-name").html(""); - $("#prompt-template").html(""); - $("#system-message").html(""); - $("#api-url").html(""); - $("#model-arguments").empty(); - $("#annotation-span-categories").empty(); - $("#extra-arguments").empty(); - return; - } - const cfg = window.configs[llmConfigValue]; - - const metric_type = cfg.type; - const model_name = cfg.model; - const prompt_template = cfg.prompt_template; - const system_msg = cfg.system_msg; - const api_url = cfg.api_url; - const model_args = cfg.model_args; - const extra_args = cfg.extra_args; - - // for metric, we need to select the appropriate one from the values in the select box - $("#metric-type").val(metric_type); - $("#model-name").html(model_name); - $("#prompt-template").html(prompt_template); - $("#system-message").html(system_msg); - $("#api-url").html(api_url); - $("#model-arguments").empty(); - $("#extra-arguments").empty(); - - $.each(model_args, function (key, value) { - const newArg = createArgElem(key, value); - $("#model-arguments").append(newArg); - }); - - $.each(extra_args, function (key, value) { - const newArg = createArgElem(key, value); - $("#extra-arguments").append(newArg); - }); - - if (mode == "llm_eval") { - const annotationSpanCategories = cfg.annotation_span_categories; - $("#annotation-span-categories").empty(); - - annotationSpanCategories.forEach((annotationSpanCategory) => { - const newCategory = createAnnotationSpanCategoryElem(annotationSpanCategory.name, annotationSpanCategory.color); - $("#annotation-span-categories").append(newCategory); - }); - } - if (mode == "llm_gen") { - const start_with = cfg.start_with; - $("#start-with").val(start_with); - } -} - - -function enableTooltips() { - // enable tooltips - var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')) - var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { - return new bootstrap.Tooltip(tooltipTriggerEl) - }) -} - -// checking whether the user is navigating to an example using history: if so, we need to load the particular example -window.addEventListener('popstate', function (event) { - if (window.location.pathname === `/browse`) { - const params = new URLSearchParams(window.location.search); - const dataset = params.get('dataset'); - const split = params.get('split'); - const example_idx = params.get('example_idx'); - if (dataset && split && example_idx) { - changeExample(dataset, split, example_idx); - } - } -}); - - -$(document).ready(function () { - if (mode == "annotate") { - loadAnnotations(); - - $("#total-examples").html(total_examples - 1); - } else if (mode == "browse") { - if (window.display_example != null) { - const e = window.display_example; - changeExample(e.dataset, e.split, e.example_idx); - } - else { - // select the first dataset from the selectbox - $("#dataset-select").val( - $("#dataset-select option:first").val() - ).trigger("change"); - $("#page-input").val(current_example_idx); - } - } - - enableTooltips(); -}); - -if (mode == "annotate") { - window.onbeforeunload = function () { - return "Are you sure you want to reload the page? Your work will be lost."; - } -} diff --git a/factgenie/static/js/lib/backbone-min.js b/factgenie/static/js/lib/backbone-min.js new file mode 100644 index 00000000..9bcce86e --- /dev/null +++ b/factgenie/static/js/lib/backbone-min.js @@ -0,0 +1,2 @@ +(function(r){var n=typeof self=="object"&&self.self===self&&self||typeof global=="object"&&global.global===global&&global;if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(t,e,i){n.Backbone=r(n,i,t,e)})}else if(typeof exports!=="undefined"){var t=require("underscore"),e;try{e=require("jquery")}catch(t){}r(n,exports,t,e)}else{n.Backbone=r(n,{},n._,n.jQuery||n.Zepto||n.ender||n.$)}})(function(t,h,x,e){var i=t.Backbone;var a=Array.prototype.slice;h.VERSION="1.6.0";h.$=e;h.noConflict=function(){t.Backbone=i;return this};h.emulateHTTP=false;h.emulateJSON=false;var r=h.Events={};var o=/\s+/;var l;var u=function(t,e,i,r,n){var s=0,a;if(i&&typeof i==="object"){if(r!==void 0&&"context"in n&&n.context===void 0)n.context=r;for(a=x.keys(i);sthis.length)r=this.length;if(r<0)r+=this.length+1;var n=[];var s=[];var a=[];var o=[];var h={};var l=e.add;var u=e.merge;var c=e.remove;var f=false;var d=this.comparator&&r==null&&e.sort!==false;var v=x.isString(this.comparator)?this.comparator:null;var p,g;for(g=0;g0&&!e.silent)delete e.index;return i},_isModel:function(t){return t instanceof g},_addReference:function(t,e){this._byId[t.cid]=t;var i=this.modelId(t.attributes,t.idAttribute);if(i!=null)this._byId[i]=t;t.on("all",this._onModelEvent,this)},_removeReference:function(t,e){delete this._byId[t.cid];var i=this.modelId(t.attributes,t.idAttribute);if(i!=null)delete this._byId[i];if(this===t.collection)delete t.collection;t.off("all",this._onModelEvent,this)},_onModelEvent:function(t,e,i,r){if(e){if((t==="add"||t==="remove")&&i!==this)return;if(t==="destroy")this.remove(e,r);if(t==="changeId"){var n=this.modelId(e.previousAttributes(),e.idAttribute);var s=this.modelId(e.attributes,e.idAttribute);if(n!=null)delete this._byId[n];if(s!=null)this._byId[s]=e}}this.trigger.apply(this,arguments)},_forwardPristineError:function(t,e,i){if(this.has(t))return;this._onModelEvent("error",t,e,i)}});var y=typeof Symbol==="function"&&Symbol.iterator;if(y){m.prototype[y]=m.prototype.values}var b=function(t,e){this._collection=t;this._kind=e;this._index=0};var S=1;var I=2;var k=3;if(y){b.prototype[y]=function(){return this}}b.prototype.next=function(){if(this._collection){if(this._index7);this._useHashChange=this._wantsHashChange&&this._hasHashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.history&&this.history.pushState);this._usePushState=this._wantsPushState&&this._hasPushState;this.fragment=this.getFragment();this.root=("/"+this.root+"/").replace(L,"/");if(this._wantsHashChange&&this._wantsPushState){if(!this._hasPushState&&!this.atRoot()){var e=this.root.slice(0,-1)||"/";this.location.replace(e+"#"+this.getPath());return true}else if(this._hasPushState&&this.atRoot()){this.navigate(this.getHash(),{replace:true})}}if(!this._hasHashChange&&this._wantsHashChange&&!this._usePushState){this.iframe=document.createElement("iframe");this.iframe.src="javascript:0";this.iframe.style.display="none";this.iframe.tabIndex=-1;var i=document.body;var r=i.insertBefore(this.iframe,i.firstChild).contentWindow;r.document.open();r.document.close();r.location.hash="#"+this.fragment}var n=window.addEventListener||function(t,e){return attachEvent("on"+t,e)};if(this._usePushState){n("popstate",this.checkUrl,false)}else if(this._useHashChange&&!this.iframe){n("hashchange",this.checkUrl,false)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}if(!this.options.silent)return this.loadUrl()},stop:function(){var t=window.removeEventListener||function(t,e){return detachEvent("on"+t,e)};if(this._usePushState){t("popstate",this.checkUrl,false)}else if(this._useHashChange&&!this.iframe){t("hashchange",this.checkUrl,false)}if(this.iframe){document.body.removeChild(this.iframe);this.iframe=null}if(this._checkUrlInterval)clearInterval(this._checkUrlInterval);B.started=false},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe){e=this.getHash(this.iframe.contentWindow)}if(e===this.fragment){if(!this.matchRoot())return this.notfound();return false}if(this.iframe)this.navigate(e);this.loadUrl()},loadUrl:function(e){if(!this.matchRoot())return this.notfound();e=this.fragment=this.getFragment(e);return x.some(this.handlers,function(t){if(t.route.test(e)){t.callback(e);return true}})||this.notfound()},notfound:function(){this.trigger("notfound");return false},navigate:function(t,e){if(!B.started)return false;if(!e||e===true)e={trigger:!!e};t=this.getFragment(t||"");var i=this.root;if(!this._trailingSlash&&(t===""||t.charAt(0)==="?")){i=i.slice(0,-1)||"/"}var r=i+t;t=t.replace(W,"");var n=this.decodeFragment(t);if(this.fragment===n)return;this.fragment=n;if(this._usePushState){this.history[e.replace?"replaceState":"pushState"]({},document.title,r)}else if(this._wantsHashChange){this._updateHash(this.location,t,e.replace);if(this.iframe&&t!==this.getHash(this.iframe.contentWindow)){var s=this.iframe.contentWindow;if(!e.replace){s.document.open();s.document.close()}this._updateHash(s.location,t,e.replace)}}else{return this.location.assign(r)}if(e.trigger)return this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});h.history=new B;var D=function(t,e){var i=this;var r;if(t&&x.has(t,"constructor")){r=t.constructor}else{r=function(){return i.apply(this,arguments)}}x.extend(r,i,e);r.prototype=x.create(i.prototype,t);r.prototype.constructor=r;r.__super__=i.prototype;return r};g.extend=m.extend=O.extend=A.extend=B.extend=D;var V=function(){throw new Error('A "url" property or function must be specified')};var G=function(e,i){var r=i.error;i.error=function(t){if(r)r.call(i.context,e,t,i);e.trigger("error",e,t,i)}};h._debug=function(){return{root:t,_:x}};return h}); +//# sourceMappingURL=backbone-min.js.map \ No newline at end of file diff --git a/factgenie/static/js/backbone-relational.js b/factgenie/static/js/lib/backbone-relational.js similarity index 100% rename from factgenie/static/js/backbone-relational.js rename to factgenie/static/js/lib/backbone-relational.js diff --git a/factgenie/static/js/backbone.marionette.min.js b/factgenie/static/js/lib/backbone.marionette.min.js similarity index 100% rename from factgenie/static/js/backbone.marionette.min.js rename to factgenie/static/js/lib/backbone.marionette.min.js diff --git a/factgenie/static/js/bootstrap-table-export.min.js b/factgenie/static/js/lib/bootstrap-table-export.min.js similarity index 100% rename from factgenie/static/js/bootstrap-table-export.min.js rename to factgenie/static/js/lib/bootstrap-table-export.min.js diff --git a/factgenie/static/js/bootstrap-table.min.js b/factgenie/static/js/lib/bootstrap-table.min.js similarity index 100% rename from factgenie/static/js/bootstrap-table.min.js rename to factgenie/static/js/lib/bootstrap-table.min.js diff --git a/factgenie/static/js/bootstrap.bundle.min.js b/factgenie/static/js/lib/bootstrap.bundle.min.js similarity index 100% rename from factgenie/static/js/bootstrap.bundle.min.js rename to factgenie/static/js/lib/bootstrap.bundle.min.js diff --git a/factgenie/static/js/bootstrap.bundle.min.js.map b/factgenie/static/js/lib/bootstrap.bundle.min.js.map similarity index 100% rename from factgenie/static/js/bootstrap.bundle.min.js.map rename to factgenie/static/js/lib/bootstrap.bundle.min.js.map diff --git a/factgenie/static/js/bootstrap.esm.min.js b/factgenie/static/js/lib/bootstrap.esm.min.js similarity index 100% rename from factgenie/static/js/bootstrap.esm.min.js rename to factgenie/static/js/lib/bootstrap.esm.min.js diff --git a/factgenie/static/js/bootstrap.esm.min.js.map b/factgenie/static/js/lib/bootstrap.esm.min.js.map similarity index 100% rename from factgenie/static/js/bootstrap.esm.min.js.map rename to factgenie/static/js/lib/bootstrap.esm.min.js.map diff --git a/factgenie/static/js/bootstrap.min.js b/factgenie/static/js/lib/bootstrap.min.js similarity index 100% rename from factgenie/static/js/bootstrap.min.js rename to factgenie/static/js/lib/bootstrap.min.js diff --git a/factgenie/static/js/bootstrap.min.js.map b/factgenie/static/js/lib/bootstrap.min.js.map similarity index 100% rename from factgenie/static/js/bootstrap.min.js.map rename to factgenie/static/js/lib/bootstrap.min.js.map diff --git a/factgenie/static/js/lib/highcharts.js b/factgenie/static/js/lib/highcharts.js new file mode 100644 index 00000000..a4cd754a --- /dev/null +++ b/factgenie/static/js/lib/highcharts.js @@ -0,0 +1,8 @@ +!/** + * Highcharts JS v11.4.8 (2024-08-29) + * + * (c) 2009-2024 Torstein Honsi + * + * License: www.highcharts.com/license + */function(t,e){"object"==typeof module&&module.exports?(e.default=e,module.exports=t&&t.document?e(t):e):"function"==typeof define&&define.amd?define("highcharts/highcharts",function(){return e(t)}):(t.Highcharts&&t.Highcharts.error(16,!0),t.Highcharts=e(t))}("undefined"!=typeof window?window:this,function(t){"use strict";var e={};function i(e,i,s,r){!e.hasOwnProperty(i)&&(e[i]=r.apply(null,s),t&&"function"==typeof CustomEvent&&t.dispatchEvent(new CustomEvent("HighchartsModuleLoaded",{detail:{path:i,module:e[i]}})))}return i(e,"Core/Globals.js",[],function(){var e,i;return(i=e||(e={})).SVG_NS="http://www.w3.org/2000/svg",i.product="Highcharts",i.version="11.4.8",i.win=void 0!==t?t:{},i.doc=i.win.document,i.svg=i.doc&&i.doc.createElementNS&&!!i.doc.createElementNS(i.SVG_NS,"svg").createSVGRect,i.userAgent=i.win.navigator&&i.win.navigator.userAgent||"",i.isChrome=i.win.chrome,i.isFirefox=-1!==i.userAgent.indexOf("Firefox"),i.isMS=/(edge|msie|trident)/i.test(i.userAgent)&&!i.win.opera,i.isSafari=!i.isChrome&&-1!==i.userAgent.indexOf("Safari"),i.isTouchDevice=/(Mobile|Android|Windows Phone)/.test(i.userAgent),i.isWebKit=-1!==i.userAgent.indexOf("AppleWebKit"),i.deg2rad=2*Math.PI/360,i.hasBidiBug=i.isFirefox&&4>parseInt(i.userAgent.split("Firefox/")[1],10),i.marginNames=["plotTop","marginRight","marginBottom","plotLeft"],i.noop=function(){},i.supportsPassiveEvents=function(){let t=!1;if(!i.isMS){let e=Object.defineProperty({},"passive",{get:function(){t=!0}});i.win.addEventListener&&i.win.removeEventListener&&(i.win.addEventListener("testPassive",i.noop,e),i.win.removeEventListener("testPassive",i.noop,e))}return t}(),i.charts=[],i.composed=[],i.dateFormats={},i.seriesTypes={},i.symbolSizes={},i.chartCount=0,e}),i(e,"Core/Utilities.js",[e["Core/Globals.js"]],function(t){let e;let{charts:i,doc:s,win:r}=t;function o(e,i,s,n){let a=i?"Highcharts error":"Highcharts warning";32===e&&(e=`${a}: Deprecated member`);let h=p(e),l=h?`${a} #${e}: www.highcharts.com/errors/${e}/`:e.toString();if(void 0!==n){let t="";h&&(l+="?"),C(n,function(e,i){t+=` + - ${i}: ${e}`,h&&(l+=encodeURI(i)+"="+encodeURI(e))}),l+=t}M(t,"displayError",{chart:s,code:e,message:l,params:n},function(){if(i)throw Error(l);r.console&&-1===o.messages.indexOf(l)&&console.warn(l)}),o.messages.push(l)}function n(t,e){return parseInt(t,e||10)}function a(t){return"string"==typeof t}function h(t){let e=Object.prototype.toString.call(t);return"[object Array]"===e||"[object Array Iterator]"===e}function l(t,e){return!!t&&"object"==typeof t&&(!e||!h(t))}function d(t){return l(t)&&"number"==typeof t.nodeType}function c(t){let e=t&&t.constructor;return!!(l(t,!0)&&!d(t)&&e&&e.name&&"Object"!==e.name)}function p(t){return"number"==typeof t&&!isNaN(t)&&t<1/0&&t>-1/0}function u(t){return null!=t}function g(t,e,i){let s;let r=a(e)&&!u(i),o=(e,i)=>{u(e)?t.setAttribute(i,e):r?(s=t.getAttribute(i))||"class"!==i||(s=t.getAttribute(i+"Name")):t.removeAttribute(i)};return a(e)?o(i,e):C(e,o),s}function f(t){return h(t)?t:[t]}function m(t,e){let i;for(i in t||(t={}),e)t[i]=e[i];return t}function x(){let t=arguments,e=t.length;for(let i=0;i1e14?t:parseFloat(t.toPrecision(e||14))}(o||(o={})).messages=[],Math.easeInOutSine=function(t){return -.5*(Math.cos(Math.PI*t)-1)};let S=Array.prototype.find?function(t,e){return t.find(e)}:function(t,e){let i;let s=t.length;for(i=0;it.order-e.order),t.forEach(t=>{!1===t.fn.call(e,r)&&r.preventDefault()})}o&&!r.defaultPrevented&&o.call(e,r)}C({map:"map",each:"forEach",grep:"filter",reduce:"reduce",some:"some"},function(e,i){t[i]=function(t){return o(32,!1,void 0,{[`Highcharts.${i}`]:`use Array.${e}`}),Array.prototype[e].apply(t,[].slice.call(arguments,1))}});let w=function(){let t=Math.random().toString(36).substring(2,9)+"-",i=0;return function(){return"highcharts-"+(e?"":t)+i++}}();return r.jQuery&&(r.jQuery.fn.highcharts=function(){let e=[].slice.call(arguments);if(this[0])return e[0]?(new t[a(e[0])?e.shift():"Chart"](this[0],e[0],e[1]),this):i[g(this[0],"data-highcharts-chart")]}),{addEvent:function(e,i,s,r={}){let o="function"==typeof e&&e.prototype||e;Object.hasOwnProperty.call(o,"hcEvents")||(o.hcEvents={});let n=o.hcEvents;t.Point&&e instanceof t.Point&&e.series&&e.series.chart&&(e.series.chart.runTrackerClick=!0);let a=e.addEventListener;a&&a.call(e,i,s,!!t.supportsPassiveEvents&&{passive:void 0===r.passive?-1!==i.indexOf("touch"):r.passive,capture:!1}),n[i]||(n[i]=[]);let h={fn:s,order:"number"==typeof r.order?r.order:1/0};return n[i].push(h),n[i].sort((t,e)=>t.order-e.order),function(){k(e,i,s)}},arrayMax:function(t){let e=t.length,i=t[0];for(;e--;)t[e]>i&&(i=t[e]);return i},arrayMin:function(t){let e=t.length,i=t[0];for(;e--;)t[e]e?t{let s=e%2/2,r=i?-1:1;return(Math.round(t*r-s)+s)*r},css:y,defined:u,destroyObjectProperties:function(t,e,i){C(t,function(s,r){s!==e&&s?.destroy&&s.destroy(),(s?.destroy||!i)&&delete t[r]})},diffObjects:function(t,e,i,s){let r={};return function t(e,r,o,n){let a=i?r:e;C(e,function(i,d){if(!n&&s&&s.indexOf(d)>-1&&r[d]){i=f(i),o[d]=[];for(let e=0;e{if(t.length>1)for(o=s=t.length-1;o>0;o--)(r=t[o]-t[o-1])<0&&!n?(e?.(),e=void 0):r&&(void 0===i||r=i-1&&(i=Math.floor(s)),Math.max(0,i-(t(e,"padding-left",!0)||0)-(t(e,"padding-right",!0)||0))}if("height"===i)return Math.max(0,Math.min(e.offsetHeight,e.scrollHeight)-(t(e,"padding-top",!0)||0)-(t(e,"padding-bottom",!0)||0));let a=r.getComputedStyle(e,void 0);return a&&(o=a.getPropertyValue(i),x(s,"opacity"!==i)&&(o=n(o))),o},inArray:function(t,e,i){return o(32,!1,void 0,{"Highcharts.inArray":"use Array.indexOf"}),e.indexOf(t,i)},insertItem:function(t,e){let i;let s=t.options.index,r=e.length;for(i=t.options.isInternal?r:0;i=t))&&(r||!(a<=(e[o]+(e[o+1]||e[o]))/2)));o++);return v(n*i,-Math.round(Math.log(.001)/Math.LN10))},objectEach:C,offset:function(t){let e=s.documentElement,i=t.parentElement||t.parentNode?t.getBoundingClientRect():{top:0,left:0,width:0,height:0};return{top:i.top+(r.pageYOffset||e.scrollTop)-(e.clientTop||0),left:i.left+(r.pageXOffset||e.scrollLeft)-(e.clientLeft||0),width:i.width,height:i.height}},pad:function(t,e,i){return Array((e||2)+1-String(t).replace("-","").length).join(i||"0")+t},pick:x,pInt:n,pushUnique:function(t,e){return 0>t.indexOf(e)&&!!t.push(e)},relativeLength:function(t,e,i){return/%$/.test(t)?e*parseFloat(t)/100+(i||0):parseFloat(t)},removeEvent:k,replaceNested:function(t,...e){let i,s;do for(s of(i=t,e))t=t.replace(s[0],s[1]);while(t!==i);return t},splat:f,stableSort:function(t,e){let i,s;let r=t.length;for(s=0;s0?setTimeout(t,e,i):(t.call(0,i),-1)},timeUnits:{millisecond:1,second:1e3,minute:6e4,hour:36e5,day:864e5,week:6048e5,month:24192e5,year:314496e5},uniqueKey:w,useSerialIds:function(t){return e=x(t,e)},wrap:function(t,e,i){let s=t[e];t[e]=function(){let t=arguments,e=this;return i.apply(this,[function(){return s.apply(e,arguments.length?arguments:t)}].concat([].slice.call(arguments)))}}}}),i(e,"Core/Chart/ChartDefaults.js",[],function(){return{alignThresholds:!1,panning:{enabled:!1,type:"x"},styledMode:!1,borderRadius:0,colorCount:10,allowMutatingData:!0,ignoreHiddenSeries:!0,spacing:[10,10,15,10],resetZoomButton:{theme:{},position:{}},reflow:!0,type:"line",zooming:{singleTouch:!1,resetButton:{theme:{zIndex:6},position:{align:"right",x:-10,y:10}}},width:null,height:null,borderColor:"#334eff",backgroundColor:"#ffffff",plotBorderColor:"#cccccc"}}),i(e,"Core/Color/Palettes.js",[],function(){return{colors:["#2caffe","#544fc5","#00e272","#fe6a35","#6b8abc","#d568fb","#2ee0ca","#fa4b42","#feb56a","#91e8e1"]}}),i(e,"Core/Time.js",[e["Core/Globals.js"],e["Core/Utilities.js"]],function(t,e){let{win:i}=t,{defined:s,error:r,extend:o,isNumber:n,isObject:a,merge:h,objectEach:l,pad:d,pick:c,splat:p,timeUnits:u}=e,g=t.isSafari&&i.Intl&&i.Intl.DateTimeFormat.prototype.formatRange,f=t.isSafari&&i.Intl&&!i.Intl.DateTimeFormat.prototype.formatRange;class m{constructor(t){this.options={},this.useUTC=!1,this.variableTimezone=!1,this.Date=i.Date,this.getTimezoneOffset=this.timezoneOffsetFunction(),this.update(t)}get(t,e){if(this.variableTimezone||this.timezoneOffset){let i=e.getTime(),s=i-this.getTimezoneOffset(e);e.setTime(s);let r=e["getUTC"+t]();return e.setTime(i),r}return this.useUTC?e["getUTC"+t]():e["get"+t]()}set(t,e,i){if(this.variableTimezone||this.timezoneOffset){if("Milliseconds"===t||"Seconds"===t||"Minutes"===t&&this.getTimezoneOffset(e)%36e5==0)return e["setUTC"+t](i);let s=this.getTimezoneOffset(e),r=e.getTime()-s;e.setTime(r),e["setUTC"+t](i);let o=this.getTimezoneOffset(e);return r=e.getTime()+o,e.setTime(r)}return this.useUTC||g&&"FullYear"===t?e["setUTC"+t](i):e["set"+t](i)}update(t={}){let e=c(t.useUTC,!0);this.options=t=h(!0,this.options,t),this.Date=t.Date||i.Date||Date,this.useUTC=e,this.timezoneOffset=e&&t.timezoneOffset||void 0,this.getTimezoneOffset=this.timezoneOffsetFunction(),this.variableTimezone=e&&!!(t.getTimezoneOffset||t.timezone)}makeTime(t,e,i,s,r,o){let n,a,h;return this.useUTC?(n=this.Date.UTC.apply(0,arguments),a=this.getTimezoneOffset(n),n+=a,a!==(h=this.getTimezoneOffset(n))?n+=h-a:a-36e5!==this.getTimezoneOffset(n-36e5)||f||(n-=36e5)):n=new this.Date(t,e,c(i,1),c(s,0),c(r,0),c(o,0)).getTime(),n}timezoneOffsetFunction(){let t=this,e=this.options,i=e.getTimezoneOffset;return this.useUTC?e.timezone?t=>{try{let i=`shortOffset,${e.timezone||""}`,[s,r,o,a,h=0]=(m.formatCache[i]=m.formatCache[i]||Intl.DateTimeFormat("en",{timeZone:e.timezone,timeZoneName:"shortOffset"})).format(t).split(/(GMT|:)/).map(Number),l=-(36e5*(o+h/60));if(n(l))return l}catch(t){r(34)}return 0}:this.useUTC&&i?t=>6e4*i(t.valueOf()):()=>6e4*(t.timezoneOffset||0):t=>6e4*new Date(t.toString()).getTimezoneOffset()}dateFormat(e,i,r){if(!s(i)||isNaN(i))return t.defaultOptions.lang&&t.defaultOptions.lang.invalidDate||"";e=c(e,"%Y-%m-%d %H:%M:%S");let n=this,a=new this.Date(i),h=this.get("Hours",a),p=this.get("Day",a),u=this.get("Date",a),g=this.get("Month",a),f=this.get("FullYear",a),m=t.defaultOptions.lang,x=m&&m.weekdays,y=m&&m.shortWeekdays;return l(o({a:y?y[p]:x[p].substr(0,3),A:x[p],d:d(u),e:d(u,2," "),w:p,b:m.shortMonths[g],B:m.months[g],m:d(g+1),o:g+1,y:f.toString().substr(2,2),Y:f,H:d(h),k:h,I:d(h%12||12),l:h%12||12,M:d(this.get("Minutes",a)),p:h<12?"AM":"PM",P:h<12?"am":"pm",S:d(this.get("Seconds",a)),L:d(Math.floor(i%1e3),3)},t.dateFormats),function(t,s){for(;-1!==e.indexOf("%"+s);)e=e.replace("%"+s,"function"==typeof t?t.call(n,i):t)}),r?e.substr(0,1).toUpperCase()+e.substr(1):e}resolveDTLFormat(t){return a(t,!0)?t:{main:(t=p(t))[0],from:t[1],to:t[2]}}getTimeTicks(t,e,i,r){let n,a,h,l;let d=this,p=d.Date,g=[],f={},m=new p(e),x=t.unitRange,y=t.count||1;if(r=c(r,1),s(e)){d.set("Milliseconds",m,x>=u.second?0:y*Math.floor(d.get("Milliseconds",m)/y)),x>=u.second&&d.set("Seconds",m,x>=u.minute?0:y*Math.floor(d.get("Seconds",m)/y)),x>=u.minute&&d.set("Minutes",m,x>=u.hour?0:y*Math.floor(d.get("Minutes",m)/y)),x>=u.hour&&d.set("Hours",m,x>=u.day?0:y*Math.floor(d.get("Hours",m)/y)),x>=u.day&&d.set("Date",m,x>=u.month?1:Math.max(1,y*Math.floor(d.get("Date",m)/y))),x>=u.month&&(d.set("Month",m,x>=u.year?0:y*Math.floor(d.get("Month",m)/y)),a=d.get("FullYear",m)),x>=u.year&&(a-=a%y,d.set("FullYear",m,a)),x===u.week&&(l=d.get("Day",m),d.set("Date",m,d.get("Date",m)-l+r+(l4*u.month||d.getTimezoneOffset(e)!==d.getTimezoneOffset(i));let p=m.getTime();for(n=1;p1?p=d.makeTime(a,t,o,c+n*y):p+=x*y,n++;g.push(p),x<=u.hour&&g.length<1e4&&g.forEach(function(t){t%18e5==0&&"000000000"===d.dateFormat("%H%M%S%L",t)&&(f[t]="day")})}return g.info=o(t,{higherRanks:f,totalRange:x*y}),g}getDateFormat(t,e,i,s){let r=this.dateFormat("%m-%d %H:%M:%S.%L",e),o="01-01 00:00:00.000",n={millisecond:15,second:12,minute:9,hour:6,day:3},a="millisecond",h=a;for(a in u){if(t===u.week&&+this.dateFormat("%w",e)===i&&r.substr(6)===o.substr(6)){a="week";break}if(u[a]>t){a=h;break}if(n[a]&&r.substr(n[a])!==o.substr(n[a]))break;"week"!==a&&(h=a)}return this.resolveDTLFormat(s[a]).main}}return m.formatCache={},m}),i(e,"Core/Defaults.js",[e["Core/Chart/ChartDefaults.js"],e["Core/Globals.js"],e["Core/Color/Palettes.js"],e["Core/Time.js"],e["Core/Utilities.js"]],function(t,e,i,s,r){let{isTouchDevice:o}=e,{fireEvent:n,merge:a}=r,h={colors:i.colors,symbols:["circle","diamond","square","triangle","triangle-down"],lang:{loading:"Loading...",months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],weekdays:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],decimalPoint:".",numericSymbols:["k","M","G","T","P","E"],resetZoom:"Reset zoom",resetZoomTitle:"Reset zoom level 1:1",thousandsSep:" "},global:{buttonTheme:{fill:"#f7f7f7",padding:8,r:2,stroke:"#cccccc","stroke-width":1,style:{color:"#333333",cursor:"pointer",fontSize:"0.8em",fontWeight:"normal"},states:{hover:{fill:"#e6e6e6"},select:{fill:"#e6e9ff",style:{color:"#000000",fontWeight:"bold"}},disabled:{style:{color:"#cccccc"}}}}},time:{Date:void 0,getTimezoneOffset:void 0,timezone:void 0,timezoneOffset:0,useUTC:!0},chart:t,title:{style:{color:"#333333",fontWeight:"bold"},text:"Chart title",align:"center",margin:15,widthAdjust:-44},subtitle:{style:{color:"#666666",fontSize:"0.8em"},text:"",align:"center",widthAdjust:-44},caption:{margin:15,style:{color:"#666666",fontSize:"0.8em"},text:"",align:"left",verticalAlign:"bottom"},plotOptions:{},legend:{enabled:!0,align:"center",alignColumns:!0,className:"highcharts-no-tooltip",events:{},layout:"horizontal",itemMarginBottom:2,itemMarginTop:2,labelFormatter:function(){return this.name},borderColor:"#999999",borderRadius:0,navigation:{style:{fontSize:"0.8em"},activeColor:"#0022ff",inactiveColor:"#cccccc"},itemStyle:{color:"#333333",cursor:"pointer",fontSize:"0.8em",textDecoration:"none",textOverflow:"ellipsis"},itemHoverStyle:{color:"#000000"},itemHiddenStyle:{color:"#666666",textDecoration:"line-through"},shadow:!1,itemCheckboxStyle:{position:"absolute",width:"13px",height:"13px"},squareSymbol:!0,symbolPadding:5,verticalAlign:"bottom",x:0,y:0,title:{style:{fontSize:"0.8em",fontWeight:"bold"}}},loading:{labelStyle:{fontWeight:"bold",position:"relative",top:"45%"},style:{position:"absolute",backgroundColor:"#ffffff",opacity:.5,textAlign:"center"}},tooltip:{enabled:!0,animation:{duration:300,easing:t=>Math.sqrt(1-Math.pow(t-1,2))},borderRadius:3,dateTimeLabelFormats:{millisecond:"%A, %e %b, %H:%M:%S.%L",second:"%A, %e %b, %H:%M:%S",minute:"%A, %e %b, %H:%M",hour:"%A, %e %b, %H:%M",day:"%A, %e %b %Y",week:"Week from %A, %e %b %Y",month:"%B %Y",year:"%Y"},footerFormat:"",headerShape:"callout",hideDelay:500,padding:8,shape:"callout",shared:!1,snap:o?25:10,headerFormat:'{point.key}
    ',pointFormat:' {series.name}: {point.y}
    ',backgroundColor:"#ffffff",borderWidth:void 0,shadow:!0,stickOnContact:!1,style:{color:"#333333",cursor:"default",fontSize:"0.8em"},useHTML:!1},credits:{enabled:!0,href:"https://www.highcharts.com?credits",position:{align:"right",x:-10,verticalAlign:"bottom",y:-5},style:{cursor:"pointer",color:"#999999",fontSize:"0.6em"},text:"Highcharts.com"}};h.chart.styledMode=!1;let l=new s(h.time);return{defaultOptions:h,defaultTime:l,getOptions:function(){return h},setOptions:function(t){return n(e,"setOptions",{options:t}),a(!0,h,t),(t.time||t.global)&&(e.time?e.time.update(a(h.global,h.time,t.global,t.time)):e.time=l),h}}}),i(e,"Core/Color/Color.js",[e["Core/Globals.js"],e["Core/Utilities.js"]],function(t,e){let{isNumber:i,merge:s,pInt:r}=e;class o{static parse(t){return t?new o(t):o.None}constructor(e){let i,s,r,n;this.rgba=[NaN,NaN,NaN,NaN],this.input=e;let a=t.Color;if(a&&a!==o)return new a(e);if("object"==typeof e&&void 0!==e.stops)this.stops=e.stops.map(t=>new o(t[1]));else if("string"==typeof e){if(this.input=e=o.names[e.toLowerCase()]||e,"#"===e.charAt(0)){let t=e.length,i=parseInt(e.substr(1),16);7===t?s=[(16711680&i)>>16,(65280&i)>>8,255&i,1]:4===t&&(s=[(3840&i)>>4|(3840&i)>>8,(240&i)>>4|240&i,(15&i)<<4|15&i,1])}if(!s)for(r=o.parsers.length;r--&&!s;)(i=(n=o.parsers[r]).regex.exec(e))&&(s=n.parse(i))}s&&(this.rgba=s)}get(t){let e=this.input,r=this.rgba;if("object"==typeof e&&void 0!==this.stops){let i=s(e);return i.stops=[].slice.call(i.stops),this.stops.forEach((e,s)=>{i.stops[s]=[i.stops[s][0],e.get(t)]}),i}return r&&i(r[0])?"rgb"!==t&&(t||1!==r[3])?"a"===t?`${r[3]}`:"rgba("+r.join(",")+")":"rgb("+r[0]+","+r[1]+","+r[2]+")":e}brighten(t){let e=this.rgba;if(this.stops)this.stops.forEach(function(e){e.brighten(t)});else if(i(t)&&0!==t)for(let i=0;i<3;i++)e[i]+=r(255*t),e[i]<0&&(e[i]=0),e[i]>255&&(e[i]=255);return this}setOpacity(t){return this.rgba[3]=t,this}tweenTo(t,e){let s=this.rgba,r=t.rgba;if(!i(s[0])||!i(r[0]))return t.input||"none";let o=1!==r[3]||1!==s[3];return(o?"rgba(":"rgb(")+Math.round(r[0]+(s[0]-r[0])*(1-e))+","+Math.round(r[1]+(s[1]-r[1])*(1-e))+","+Math.round(r[2]+(s[2]-r[2])*(1-e))+(o?","+(r[3]+(s[3]-r[3])*(1-e)):"")+")"}}return o.names={white:"#ffffff",black:"#000000"},o.parsers=[{regex:/rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d?(?:\.\d+)?)\s*\)/,parse:function(t){return[r(t[1]),r(t[2]),r(t[3]),parseFloat(t[4],10)]}},{regex:/rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/,parse:function(t){return[r(t[1]),r(t[2]),r(t[3]),1]}}],o.None=new o(""),o}),i(e,"Core/Animation/Fx.js",[e["Core/Color/Color.js"],e["Core/Globals.js"],e["Core/Utilities.js"]],function(t,e,i){let{parse:s}=t,{win:r}=e,{isNumber:o,objectEach:n}=i;class a{constructor(t,e,i){this.pos=NaN,this.options=e,this.elem=t,this.prop=i}dSetter(){let t=this.paths,e=t&&t[0],i=t&&t[1],s=this.now||0,r=[];if(1!==s&&e&&i){if(e.length===i.length&&s<1)for(let t=0;t=h+this.startTime?(this.now=this.end,this.pos=1,this.update(),l[this.prop]=!0,i=!0,n(l,function(t){!0!==t&&(i=!1)}),i&&a&&a.call(o),e=!1):(this.pos=r.easing((s-this.startTime)/h),this.now=this.start+(this.end-this.start)*this.pos,this.update(),e=!0),e}initPath(t,e,i){let s=t.startX,r=t.endX,n=i.slice(),a=t.isArea,h=a?2:1,l=e&&i.length>e.length&&i.hasStackedCliffs,d,c,p,u,g=e&&e.slice();if(!g||l)return[n,n];function f(t,e){for(;t.length{let s=d(t.options.animation);a=n(e)&&i(e.defer)?r.defer:Math.max(a,s.duration+s.defer),h=Math.min(r.duration,s.duration)}),t.renderer.forExport&&(a=0),{defer:Math.max(0,a-h),duration:Math.min(a,h)}},setAnimation:function(t,e){e.renderer.globalAnimation=l(t,e.options.chart.animation,!0)},stop:c}}),i(e,"Core/Renderer/HTML/AST.js",[e["Core/Globals.js"],e["Core/Utilities.js"]],function(t,e){let{SVG_NS:i,win:s}=t,{attr:r,createElement:o,css:n,error:a,isFunction:h,isString:l,objectEach:d,splat:c}=e,{trustedTypes:p}=s,u=p&&h(p.createPolicy)&&p.createPolicy("highcharts",{createHTML:t=>t}),g=u?u.createHTML(""):"",f=function(){try{return!!new DOMParser().parseFromString(g,"text/html")}catch(t){return!1}}();class m{static filterUserAttributes(t){return d(t,(e,i)=>{let s=!0;-1===m.allowedAttributes.indexOf(i)&&(s=!1),-1!==["background","dynsrc","href","lowsrc","src"].indexOf(i)&&(s=l(e)&&m.allowedReferences.some(t=>0===e.indexOf(t))),s||(a(33,!1,void 0,{"Invalid attribute in config":`${i}`}),delete t[i]),l(e)&&t[i]&&(t[i]=e.replace(/{let i=e.split(":").map(t=>t.trim()),s=i.shift();return s&&i.length&&(t[s.replace(/-([a-z])/g,t=>t[1].toUpperCase())]=i.join(":")),t},{})}static setElementHTML(t,e){t.innerHTML=m.emptyHTML,e&&new m(e).addToDOM(t)}constructor(t){this.nodes="string"==typeof t?this.parseMarkup(t):t}addToDOM(e){return function e(s,o){let h;return c(s).forEach(function(s){let l;let c=s.tagName,p=s.textContent?t.doc.createTextNode(s.textContent):void 0,u=m.bypassHTMLFiltering;if(c){if("#text"===c)l=p;else if(-1!==m.allowedTags.indexOf(c)||u){let a="svg"===c?i:o.namespaceURI||i,h=t.doc.createElementNS(a,c),g=s.attributes||{};d(s,function(t,e){"tagName"!==e&&"attributes"!==e&&"children"!==e&&"style"!==e&&"textContent"!==e&&(g[e]=t)}),r(h,u?g:m.filterUserAttributes(g)),s.style&&n(h,s.style),p&&h.appendChild(p),e(s.children||[],h),l=h}else a(33,!1,void 0,{"Invalid tagName in config":c})}l&&o.appendChild(l),h=l}),h}(this.nodes,e)}parseMarkup(t){let e;let i=[];if(t=t.trim().replace(/ style=(["'])/g," data-style=$1"),f)e=new DOMParser().parseFromString(u?u.createHTML(t):t,"text/html");else{let i=o("div");i.innerHTML=t,e={body:i}}let s=(t,e)=>{let i=t.nodeName.toLowerCase(),r={tagName:i};"#text"===i&&(r.textContent=t.textContent||"");let o=t.attributes;if(o){let t={};[].forEach.call(o,e=>{"data-style"===e.name?r.style=m.parseStyle(e.value):t[e.name]=e.value}),r.attributes=t}if(t.childNodes.length){let e=[];[].forEach.call(t.childNodes,t=>{s(t,e)}),e.length&&(r.children=e)}e.push(r)};return[].forEach.call(e.body.childNodes,t=>s(t,i)),i}}return m.allowedAttributes=["alt","aria-controls","aria-describedby","aria-expanded","aria-haspopup","aria-hidden","aria-label","aria-labelledby","aria-live","aria-pressed","aria-readonly","aria-roledescription","aria-selected","class","clip-path","color","colspan","cx","cy","d","dx","dy","disabled","fill","filterUnits","flood-color","flood-opacity","height","href","id","in","in2","markerHeight","markerWidth","offset","opacity","operator","orient","padding","paddingLeft","paddingRight","patternUnits","r","radius","refX","refY","role","scope","slope","src","startOffset","stdDeviation","stroke","stroke-linecap","stroke-width","style","tableValues","result","rowspan","summary","target","tabindex","text-align","text-anchor","textAnchor","textLength","title","type","valign","width","x","x1","x2","xlink:href","y","y1","y2","zIndex"],m.allowedReferences=["https://","http://","mailto:","/","../","./","#"],m.allowedTags=["a","abbr","b","br","button","caption","circle","clipPath","code","dd","defs","div","dl","dt","em","feComponentTransfer","feComposite","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feMorphology","feOffset","feMerge","feMergeNode","filter","h1","h2","h3","h4","h5","h6","hr","i","img","li","linearGradient","marker","ol","p","path","pattern","pre","rect","small","span","stop","strong","style","sub","sup","svg","table","text","textPath","thead","title","tbody","tspan","td","th","tr","u","ul","#text"],m.emptyHTML=g,m.bypassHTMLFiltering=!1,m}),i(e,"Core/Templating.js",[e["Core/Defaults.js"],e["Core/Utilities.js"]],function(t,e){let{defaultOptions:i,defaultTime:s}=t,{extend:r,getNestedProperty:o,isArray:n,isNumber:a,isObject:h,pick:l,pInt:d}=e,c={add:(t,e)=>t+e,divide:(t,e)=>0!==e?t/e:"",eq:(t,e)=>t==e,each:function(t){let e=arguments[arguments.length-1];return!!n(t)&&t.map((i,s)=>p(e.body,r(h(i)?i:{"@this":i},{"@index":s,"@first":0===s,"@last":s===t.length-1}))).join("")},ge:(t,e)=>t>=e,gt:(t,e)=>t>e,if:t=>!!t,le:(t,e)=>t<=e,lt:(t,e)=>tt*e,ne:(t,e)=>t!=e,subtract:(t,e)=>t-e,unless:t=>!t};function p(t="",e,r){let n=/\{([\w\:\.\,;\-\/<>%@"'’= #\(\)]+)\}/g,a=/\(([\w\:\.\,;\-\/<>%@"'= ]+)\)/g,h=[],d=/f$/,g=/\.(\d)/,f=i.lang,m=r&&r.time||s,x=r&&r.numberFormatter||u,y=(t="")=>{let i;return"true"===t||"false"!==t&&((i=Number(t)).toString()===t?i:o(t,e))},b,v,S=0,C;for(;null!==(b=n.exec(t));){let i=a.exec(b[1]);i&&(b=i,C=!0),v&&v.isBlock||(v={ctx:e,expression:b[1],find:b[0],isBlock:"#"===b[1].charAt(0),start:b.index,startInner:b.index+b[0].length,length:b[0].length});let s=b[1].split(" ")[0].replace("#","");c[s]&&(v.isBlock&&s===v.fn&&S++,v.fn||(v.fn=s));let r="else"===b[1];if(v.isBlock&&v.fn&&(b[1]===`/${v.fn}`||r)){if(S)!r&&S--;else{let e=v.startInner,i=t.substr(e,b.index-e);void 0===v.body?(v.body=i,v.startInner=b.index+b[0].length):v.elseBody=i,v.find+=i+b[0],r||(h.push(v),v=void 0)}}else v.isBlock||h.push(v);if(i&&!v?.isBlock)break}return h.forEach(i=>{let s,o;let{body:n,elseBody:a,expression:h,fn:u}=i;if(u){let t=[i],l=h.split(" ");for(o=c[u].length;o--;)t.unshift(y(l[o+1]));s=c[u].apply(e,t),i.isBlock&&"boolean"==typeof s&&(s=p(s?n:a,e,r))}else{let t=h.split(":");if(s=y(t.shift()||""),t.length&&"number"==typeof s){let e=t.join(":");if(d.test(e)){let t=parseInt((e.match(g)||["","-1"])[1],10);null!==s&&(s=x(s,t,f.decimalPoint,e.indexOf(",")>-1?f.thousandsSep:""))}else s=m.dateFormat(e,s)}}t=t.replace(i.find,l(s,""))}),C?p(t,e,r):t}function u(t,e,s,r){let o,n;t=+t||0,e=+e;let h=i.lang,c=(t.toString().split(".")[1]||"").split("e")[0].length,p=t.toString().split("e"),u=e;-1===e?e=Math.min(c,20):a(e)?e&&p[1]&&p[1]<0&&((n=e+ +p[1])>=0?(p[0]=(+p[0]).toExponential(n).split("e")[0],e=n):(p[0]=p[0].split(".")[0]||0,t=e<20?(p[0]*Math.pow(10,p[1])).toFixed(e):0,p[1]=0)):e=2;let g=(Math.abs(p[1]?p[0]:t)+Math.pow(10,-Math.max(e,c)-1)).toFixed(e),f=String(d(g)),m=f.length>3?f.length%3:0;return s=l(s,h.decimalPoint),r=l(r,h.thousandsSep),o=(t<0?"-":"")+(m?f.substr(0,m)+r:""),0>+p[1]&&!u?o="0":o+=f.substr(m).replace(/(\d{3})(?=\d)/g,"$1"+r),e?o+=s+g.slice(-e):0==+o&&(o="0"),p[1]&&0!=+o&&(o+="e"+p[1]),o}return{dateFormat:function(t,e,i){return s.dateFormat(t,e,i)},format:p,helpers:c,numberFormat:u}}),i(e,"Core/Renderer/RendererRegistry.js",[e["Core/Globals.js"]],function(t){var e,i;let s;return(i=e||(e={})).rendererTypes={},i.getRendererType=function(t=s){return i.rendererTypes[t]||i.rendererTypes[s]},i.registerRendererType=function(e,r,o){i.rendererTypes[e]=r,(!s||o)&&(s=e,t.Renderer=r)},e}),i(e,"Core/Renderer/RendererUtilities.js",[e["Core/Utilities.js"]],function(t){var e;let{clamp:i,pick:s,pushUnique:r,stableSort:o}=t;return(e||(e={})).distribute=function t(e,n,a){let h=e,l=h.reducedLen||n,d=(t,e)=>t.target-e.target,c=[],p=e.length,u=[],g=c.push,f,m,x,y=!0,b,v,S=0,C;for(f=p;f--;)S+=e[f].size;if(S>l){for(o(e,(t,e)=>(e.rank||0)-(t.rank||0)),x=(C=e[0].rank===e[e.length-1].rank)?p/2:-1,m=C?x:p-1;x&&S>l;)b=e[f=Math.floor(m)],r(u,f)&&(S-=b.size),m+=x,C&&m>=e.length&&(x/=2,m=x);u.sort((t,e)=>e-t).forEach(t=>g.apply(c,e.splice(t,1)))}for(o(e,d),e=e.map(t=>({size:t.size,targets:[t.target],align:s(t.align,.5)}));y;){for(f=e.length;f--;)b=e[f],v=(Math.min.apply(0,b.targets)+Math.max.apply(0,b.targets))/2,b.pos=i(v-b.size*b.align,0,n-b.size);for(f=e.length,y=!1;f--;)f>0&&e[f-1].pos+e[f-1].size>e[f].pos&&(e[f-1].size+=e[f].size,e[f-1].targets=e[f-1].targets.concat(e[f].targets),e[f-1].align=.5,e[f-1].pos+e[f-1].size>n&&(e[f-1].pos=n-e[f-1].size),e.splice(f,1),y=!0)}return g.apply(h,c),f=0,e.some(e=>{let i=0;return(e.targets||[]).some(()=>(h[f].pos=e.pos+i,void 0!==a&&Math.abs(h[f].pos-h[f].target)>a)?(h.slice(0,f+1).forEach(t=>delete t.pos),h.reducedLen=(h.reducedLen||n)-.1*n,h.reducedLen>.1*n&&t(h,n,a),!0):(i+=h[f].size,f++,!1))}),o(h,d),h},e}),i(e,"Core/Renderer/SVG/SVGElement.js",[e["Core/Animation/AnimationUtilities.js"],e["Core/Color/Color.js"],e["Core/Globals.js"],e["Core/Utilities.js"]],function(t,e,i,s){let{animate:r,animObject:o,stop:n}=t,{deg2rad:a,doc:h,svg:l,SVG_NS:d,win:c}=i,{addEvent:p,attr:u,createElement:g,crisp:f,css:m,defined:x,erase:y,extend:b,fireEvent:v,isArray:S,isFunction:C,isObject:k,isString:M,merge:w,objectEach:T,pick:A,pInt:P,pushUnique:L,replaceNested:O,syncTimeout:D,uniqueKey:E}=s;class I{_defaultGetter(t){let e=A(this[t+"Value"],this[t],this.element?this.element.getAttribute(t):null,0);return/^-?[\d\.]+$/.test(e)&&(e=parseFloat(e)),e}_defaultSetter(t,e,i){i.setAttribute(e,t)}add(t){let e;let i=this.renderer,s=this.element;return t&&(this.parentGroup=t),void 0!==this.textStr&&"text"===this.element.nodeName&&i.buildText(this),this.added=!0,(!t||t.handleZ||this.zIndex)&&(e=this.zIndexSetter()),e||(t?t.element:i.box).appendChild(s),this.onAdd&&this.onAdd(),this}addClass(t,e){let i=e?"":this.attr("class")||"";return(t=(t||"").split(/ /g).reduce(function(t,e){return -1===i.indexOf(e)&&t.push(e),t},i?[i]:[]).join(" "))!==i&&this.attr("class",t),this}afterSetters(){this.doTransform&&(this.updateTransform(),this.doTransform=!1)}align(t,e,i,s=!0){let r,o,n,a;let h={},l=this.renderer,d=l.alignedObjects,c=!!t;t?(this.alignOptions=t,this.alignByTranslate=e,this.alignTo=i):(t=this.alignOptions||{},e=this.alignByTranslate,i=this.alignTo);let p=!i||M(i)?i||"renderer":void 0;p&&(c&&L(d,this),i=void 0);let u=A(i,l[p],l),g=t.align,f=t.verticalAlign;return r=(u.x||0)+(t.x||0),o=(u.y||0)+(t.y||0),"right"===g?n=1:"center"===g&&(n=2),n&&(r+=((u.width||0)-(t.width||0))/n),h[e?"translateX":"x"]=Math.round(r),"bottom"===f?a=1:"middle"===f&&(a=2),a&&(o+=((u.height||0)-(t.height||0))/a),h[e?"translateY":"y"]=Math.round(o),s&&(this[this.placed?"animate":"attr"](h),this.placed=!0),this.alignAttr=h,this}alignSetter(t){let e={left:"start",center:"middle",right:"end"};e[t]&&(this.alignValue=t,this.element.setAttribute("text-anchor",e[t]))}animate(t,e,i){let s=o(A(e,this.renderer.globalAnimation,!0)),n=s.defer;return h.hidden&&(s.duration=0),0!==s.duration?(i&&(s.complete=i),D(()=>{this.element&&r(this,t,s)},n)):(this.attr(t,void 0,i||s.complete),T(t,function(t,e){s.step&&s.step.call(this,t,{prop:e,pos:1,elem:this})},this)),this}applyTextOutline(t){let e=this.element;-1!==t.indexOf("contrast")&&(t=t.replace(/contrast/g,this.renderer.getContrast(e.style.fill)));let s=t.split(" "),r=s[s.length-1],o=s[0];if(o&&"none"!==o&&i.svg){this.fakeTS=!0,o=o.replace(/(^[\d\.]+)(.*?)$/g,function(t,e,i){return 2*Number(e)+i}),this.removeTextOutline();let t=h.createElementNS(d,"tspan");u(t,{class:"highcharts-text-outline",fill:r,stroke:r,"stroke-width":o,"stroke-linejoin":"round"});let i=e.querySelector("textPath")||e;[].forEach.call(i.childNodes,e=>{let i=e.cloneNode(!0);i.removeAttribute&&["fill","stroke","stroke-width","stroke"].forEach(t=>i.removeAttribute(t)),t.appendChild(i)});let s=0;[].forEach.call(i.querySelectorAll("text tspan"),t=>{s+=Number(t.getAttribute("dy"))});let n=h.createElementNS(d,"tspan");n.textContent="​",u(n,{x:Number(e.getAttribute("x")),dy:-s}),t.appendChild(n),i.insertBefore(t,i.firstChild)}}attr(t,e,i,s){let{element:r}=this,o=I.symbolCustomAttribs,a,h,l=this,d;return"string"==typeof t&&void 0!==e&&(a=t,(t={})[a]=e),"string"==typeof t?l=(this[t+"Getter"]||this._defaultGetter).call(this,t,r):(T(t,function(e,i){d=!1,s||n(this,i),this.symbolName&&-1!==o.indexOf(i)&&(h||(this.symbolAttr(t),h=!0),d=!0),this.rotation&&("x"===i||"y"===i)&&(this.doTransform=!0),d||(this[i+"Setter"]||this._defaultSetter).call(this,e,i,r)},this),this.afterSetters()),i&&i.call(this),l}clip(t){if(t&&!t.clipPath){let e=E()+"-",i=this.renderer.createElement("clipPath").attr({id:e}).add(this.renderer.defs);b(t,{clipPath:i,id:e,count:0}),t.add(i)}return this.attr("clip-path",t?`url(${this.renderer.url}#${t.id})`:"none")}crisp(t,e){e=Math.round(e||t.strokeWidth||0);let i=t.x||this.x||0,s=t.y||this.y||0,r=(t.width||this.width||0)+i,o=(t.height||this.height||0)+s,n=f(i,e),a=f(s,e);return b(t,{x:n,y:a,width:f(r,e)-n,height:f(o,e)-a}),x(t.strokeWidth)&&(t.strokeWidth=e),t}complexColor(t,i,s){let r=this.renderer,o,n,a,h,l,d,c,p,u,g,f=[],m;v(this.renderer,"complexColor",{args:arguments},function(){if(t.radialGradient?n="radialGradient":t.linearGradient&&(n="linearGradient"),n){if(a=t[n],l=r.gradients,d=t.stops,u=s.radialReference,S(a)&&(t[n]=a={x1:a[0],y1:a[1],x2:a[2],y2:a[3],gradientUnits:"userSpaceOnUse"}),"radialGradient"===n&&u&&!x(a.gradientUnits)&&(h=a,a=w(a,r.getRadialAttr(u,h),{gradientUnits:"userSpaceOnUse"})),T(a,function(t,e){"id"!==e&&f.push(e,t)}),T(d,function(t){f.push(t)}),l[f=f.join(",")])g=l[f].attr("id");else{a.id=g=E();let t=l[f]=r.createElement(n).attr(a).add(r.defs);t.radAttr=h,t.stops=[],d.forEach(function(i){0===i[1].indexOf("rgba")?(c=(o=e.parse(i[1])).get("rgb"),p=o.get("a")):(c=i[1],p=1);let s=r.createElement("stop").attr({offset:i[0],"stop-color":c,"stop-opacity":p}).add(t);t.stops.push(s)})}m="url("+r.url+"#"+g+")",s.setAttribute(i,m),s.gradient=f,t.toString=function(){return m}}})}css(t){let e=this.styles,i={},s=this.element,r,o=!e;if(e&&T(t,function(t,s){e&&e[s]!==t&&(i[s]=t,o=!0)}),o){e&&(t=b(e,i)),null===t.width||"auto"===t.width?delete this.textWidth:"text"===s.nodeName.toLowerCase()&&t.width&&(r=this.textWidth=P(t.width)),b(this.styles,t),r&&!l&&this.renderer.forExport&&delete t.width;let o=w(t);s.namespaceURI===this.SVG_NS&&(["textOutline","textOverflow","width"].forEach(t=>o&&delete o[t]),o.color&&(o.fill=o.color)),m(s,o)}return this.added&&("text"===this.element.nodeName&&this.renderer.buildText(this),t.textOutline&&this.applyTextOutline(t.textOutline)),this}dashstyleSetter(t){let e,i=this["stroke-width"];if("inherit"===i&&(i=1),t=t&&t.toLowerCase()){let s=t.replace("shortdashdotdot","3,1,1,1,1,1,").replace("shortdashdot","3,1,1,1").replace("shortdot","1,1,").replace("shortdash","3,1,").replace("longdash","8,3,").replace(/dot/g,"1,3,").replace("dash","4,3,").replace(/,$/,"").split(",");for(e=s.length;e--;)s[e]=""+P(s[e])*A(i,NaN);t=s.join(",").replace(/NaN/g,"none"),this.element.setAttribute("stroke-dasharray",t)}}destroy(){let t=this,e=t.element||{},i=t.renderer,s=e.ownerSVGElement,r="SPAN"===e.nodeName&&t.parentGroup||void 0,o,a;if(e.onclick=e.onmouseout=e.onmouseover=e.onmousemove=e.point=null,n(t),t.clipPath&&s){let e=t.clipPath;[].forEach.call(s.querySelectorAll("[clip-path],[CLIP-PATH]"),function(t){t.getAttribute("clip-path").indexOf(e.element.id)>-1&&t.removeAttribute("clip-path")}),t.clipPath=e.destroy()}if(t.connector=t.connector?.destroy(),t.stops){for(a=0;ae&&e.join?(i?t+" ":"")+e.join(" "):(e||"").toString(),"")),/(NaN| {2}|^$)/.test(t)&&(t="M 0 0"),this[e]!==t&&(i.setAttribute(e,t),this[e]=t)}fillSetter(t,e,i){"string"==typeof t?i.setAttribute(e,t):t&&this.complexColor(t,e,i)}hrefSetter(t,e,i){i.setAttributeNS("http://www.w3.org/1999/xlink",e,t)}getBBox(t,e){let i,s,r,o;let{alignValue:n,element:a,renderer:h,styles:l,textStr:d}=this,{cache:c,cacheKeys:p}=h,u=a.namespaceURI===this.SVG_NS,g=A(e,this.rotation,0),f=h.styledMode?a&&I.prototype.getStyle.call(a,"font-size"):l.fontSize;if(x(d)&&(-1===(o=d.toString()).indexOf("<")&&(o=o.replace(/\d/g,"0")),o+=["",h.rootFontSize,f,g,this.textWidth,n,l.textOverflow,l.fontWeight].join(",")),o&&!t&&(i=c[o]),!i||i.polygon){if(u||h.forExport){try{r=this.fakeTS&&function(t){let e=a.querySelector(".highcharts-text-outline");e&&m(e,{display:t})},C(r)&&r("none"),i=a.getBBox?b({},a.getBBox()):{width:a.offsetWidth,height:a.offsetHeight,x:0,y:0},C(r)&&r("")}catch(t){}(!i||i.width<0)&&(i={x:0,y:0,width:0,height:0})}else i=this.htmlGetBBox();s=i.height,u&&(i.height=s=({"11px,17":14,"13px,20":16})[`${f||""},${Math.round(s)}`]||s),g&&(i=this.getRotatedBox(i,g));let t={bBox:i};v(this,"afterGetBBox",t),i=t.bBox}if(o&&(""===d||i.height>0)){for(;p.length>250;)delete c[p.shift()];c[o]||p.push(o),c[o]=i}return i}getRotatedBox(t,e){let{x:i,y:s,width:r,height:o}=t,{alignValue:n,translateY:h,rotationOriginX:l=0,rotationOriginY:d=0}=this,c={right:1,center:.5}[n||0]||0,p=Number(this.element.getAttribute("y")||0)-(h?0:s),u=e*a,g=(e-90)*a,f=Math.cos(u),m=Math.sin(u),x=r*f,y=r*m,b=Math.cos(g),v=Math.sin(g),[[S,C],[k,M]]=[l,d].map(t=>[t-t*f,t*m]),w=i+c*(r-x)+S+M+p*b,T=w+x,A=T-o*b,P=A-x,L=s+p-c*y-C+k+p*v,O=L+y,D=O-o*v,E=D-y,I=Math.min(w,T,A,P),j=Math.min(L,O,D,E),B=Math.max(w,T,A,P)-I,R=Math.max(L,O,D,E)-j;return{x:I,y:j,width:B,height:R,polygon:[[w,L],[T,O],[A,D],[P,E]]}}getStyle(t){return c.getComputedStyle(this.element||this,"").getPropertyValue(t)}hasClass(t){return -1!==(""+this.attr("class")).split(" ").indexOf(t)}hide(){return this.attr({visibility:"hidden"})}htmlGetBBox(){return{height:0,width:0,x:0,y:0}}constructor(t,e){this.onEvents={},this.opacity=1,this.SVG_NS=d,this.element="span"===e||"body"===e?g(e):h.createElementNS(this.SVG_NS,e),this.renderer=t,this.styles={},v(this,"afterInit")}on(t,e){let{onEvents:i}=this;return i[t]&&i[t](),i[t]=p(this.element,t,e),this}opacitySetter(t,e,i){let s=Number(Number(t).toFixed(3));this.opacity=s,i.setAttribute(e,s)}reAlign(){this.alignOptions?.width&&"left"!==this.alignOptions.align&&(this.alignOptions.width=this.getBBox().width,this.placed=!1,this.align())}removeClass(t){return this.attr("class",(""+this.attr("class")).replace(M(t)?RegExp(`(^| )${t}( |$)`):t," ").replace(/ +/g," ").trim())}removeTextOutline(){let t=this.element.querySelector("tspan.highcharts-text-outline");t&&this.safeRemoveChild(t)}safeRemoveChild(t){let e=t.parentNode;e&&e.removeChild(t)}setRadialReference(t){let e=this.element.gradient&&this.renderer.gradients[this.element.gradient];return this.element.radialReference=t,e&&e.radAttr&&e.animate(this.renderer.getRadialAttr(t,e.radAttr)),this}shadow(t){let{renderer:e}=this,i=w(this.parentGroup?.rotation===90?{offsetX:-1,offsetY:-1}:{},k(t)?t:{}),s=e.shadowDefinition(i);return this.attr({filter:t?`url(${e.url}#${s})`:"none"})}show(t=!0){return this.attr({visibility:t?"inherit":"visible"})}"stroke-widthSetter"(t,e,i){this[e]=t,i.setAttribute(e,t)}strokeWidth(){if(!this.renderer.styledMode)return this["stroke-width"]||0;let t=this.getStyle("stroke-width"),e=0,i;return/px$/.test(t)?e=P(t):""!==t&&(u(i=h.createElementNS(d,"rect"),{width:t,"stroke-width":0}),this.element.parentNode.appendChild(i),e=i.getBBox().width,i.parentNode.removeChild(i)),e}symbolAttr(t){let e=this;I.symbolCustomAttribs.forEach(function(i){e[i]=A(t[i],e[i])}),e.attr({d:e.renderer.symbols[e.symbolName](e.x,e.y,e.width,e.height,e)})}textSetter(t){t!==this.textStr&&(delete this.textPxLength,this.textStr=t,this.added&&this.renderer.buildText(this),this.reAlign())}titleSetter(t){let e=this.element,i=e.getElementsByTagName("title")[0]||h.createElementNS(this.SVG_NS,"title");e.insertBefore?e.insertBefore(i,e.firstChild):e.appendChild(i),i.textContent=O(A(t,""),[/<[^>]*>/g,""]).replace(/</g,"<").replace(/>/g,">")}toFront(){let t=this.element;return t.parentNode.appendChild(t),this}translate(t,e){return this.attr({translateX:t,translateY:e})}updateTransform(t="transform"){let{element:e,matrix:i,rotation:s=0,rotationOriginX:r,rotationOriginY:o,scaleX:n,scaleY:a,translateX:h=0,translateY:l=0}=this,d=["translate("+h+","+l+")"];x(i)&&d.push("matrix("+i.join(",")+")"),s&&(d.push("rotate("+s+" "+A(r,e.getAttribute("x"),0)+" "+A(o,e.getAttribute("y")||0)+")"),this.text?.element.tagName==="SPAN"&&this.text.attr({rotation:s,rotationOriginX:(r||0)-this.padding,rotationOriginY:(o||0)-this.padding})),(x(n)||x(a))&&d.push("scale("+A(n,1)+" "+A(a,1)+")"),d.length&&!(this.text||this).textPath&&e.setAttribute(t,d.join(" "))}visibilitySetter(t,e,i){"inherit"===t?i.removeAttribute(e):this[e]!==t&&i.setAttribute(e,t),this[e]=t}xGetter(t){return"circle"===this.element.nodeName&&("x"===t?t="cx":"y"===t&&(t="cy")),this._defaultGetter(t)}zIndexSetter(t,e){let i=this.renderer,s=this.parentGroup,r=(s||i).element||i.box,o=this.element,n=r===i.box,a,h,l,d=!1,c,p=this.added,u;if(x(t)?(o.setAttribute("data-z-index",t),t=+t,this[e]===t&&(p=!1)):x(this[e])&&o.removeAttribute("data-z-index"),this[e]=t,p){for((t=this.zIndex)&&s&&(s.handleZ=!0),u=(a=r.childNodes).length-1;u>=0&&!d;u--)c=!x(l=(h=a[u]).getAttribute("data-z-index")),h!==o&&(t<0&&c&&!n&&!u?(r.insertBefore(o,a[u]),d=!0):(P(l)<=t||c&&(!x(t)||t>=0))&&(r.insertBefore(o,a[u+1]),d=!0));d||(r.insertBefore(o,a[n?3:0]),d=!0)}return d}}return I.symbolCustomAttribs=["anchorX","anchorY","clockwise","end","height","innerR","r","start","width","x","y"],I.prototype.strokeSetter=I.prototype.fillSetter,I.prototype.yGetter=I.prototype.xGetter,I.prototype.matrixSetter=I.prototype.rotationOriginXSetter=I.prototype.rotationOriginYSetter=I.prototype.rotationSetter=I.prototype.scaleXSetter=I.prototype.scaleYSetter=I.prototype.translateXSetter=I.prototype.translateYSetter=I.prototype.verticalAlignSetter=function(t,e){this[e]=t,this.doTransform=!0},I}),i(e,"Core/Renderer/SVG/SVGLabel.js",[e["Core/Renderer/SVG/SVGElement.js"],e["Core/Utilities.js"]],function(t,e){let{defined:i,extend:s,isNumber:r,merge:o,pick:n,removeEvent:a}=e;class h extends t{constructor(t,e,i,s,r,o,n,a,l,d){let c;super(t,"g"),this.paddingLeftSetter=this.paddingSetter,this.paddingRightSetter=this.paddingSetter,this.doUpdate=!1,this.textStr=e,this.x=i,this.y=s,this.anchorX=o,this.anchorY=n,this.baseline=l,this.className=d,this.addClass("button"===d?"highcharts-no-tooltip":"highcharts-label"),d&&this.addClass("highcharts-"+d),this.text=t.text(void 0,0,0,a).attr({zIndex:1}),"string"==typeof r&&((c=/^url\((.*?)\)$/.test(r))||this.renderer.symbols[r])&&(this.symbolKey=r),this.bBox=h.emptyBBox,this.padding=3,this.baselineOffset=0,this.needsBox=t.styledMode||c,this.deferredAttr={},this.alignFactor=0}alignSetter(t){let e={left:0,center:.5,right:1}[t];e!==this.alignFactor&&(this.alignFactor=e,this.bBox&&r(this.xSetting)&&this.attr({x:this.xSetting}))}anchorXSetter(t,e){this.anchorX=t,this.boxAttr(e,Math.round(t)-this.getCrispAdjust()-this.xSetting)}anchorYSetter(t,e){this.anchorY=t,this.boxAttr(e,t-this.ySetting)}boxAttr(t,e){this.box?this.box.attr(t,e):this.deferredAttr[t]=e}css(e){if(e){let t={};e=o(e),h.textProps.forEach(i=>{void 0!==e[i]&&(t[i]=e[i],delete e[i])}),this.text.css(t),"fontSize"in t||"fontWeight"in t?this.updateTextPadding():("width"in t||"textOverflow"in t)&&this.updateBoxSize()}return t.prototype.css.call(this,e)}destroy(){a(this.element,"mouseenter"),a(this.element,"mouseleave"),this.text&&this.text.destroy(),this.box&&(this.box=this.box.destroy()),t.prototype.destroy.call(this)}fillSetter(t,e){t&&(this.needsBox=!0),this.fill=t,this.boxAttr(e,t)}getBBox(t,e){this.textStr&&0===this.bBox.width&&0===this.bBox.height&&this.updateBoxSize();let{padding:i,height:s=0,translateX:r=0,translateY:o=0,width:a=0}=this,h=n(this.paddingLeft,i),l=e??(this.rotation||0),d={width:a,height:s,x:r+this.bBox.x-h,y:o+this.bBox.y-i+this.baselineOffset};return l&&(d=this.getRotatedBox(d,l)),d}getCrispAdjust(){return(this.renderer.styledMode&&this.box?this.box.strokeWidth():this["stroke-width"]?parseInt(this["stroke-width"],10):0)%2/2}heightSetter(t){this.heightSetting=t,this.doUpdate=!0}afterSetters(){super.afterSetters(),this.doUpdate&&(this.updateBoxSize(),this.doUpdate=!1)}onAdd(){this.text.add(this),this.attr({text:n(this.textStr,""),x:this.x||0,y:this.y||0}),this.box&&i(this.anchorX)&&this.attr({anchorX:this.anchorX,anchorY:this.anchorY})}paddingSetter(t,e){r(t)?t!==this[e]&&(this[e]=t,this.updateTextPadding()):this[e]=void 0}rSetter(t,e){this.boxAttr(e,t)}strokeSetter(t,e){this.stroke=t,this.boxAttr(e,t)}"stroke-widthSetter"(t,e){t&&(this.needsBox=!0),this["stroke-width"]=t,this.boxAttr(e,t)}"text-alignSetter"(t){this.textAlign=t}textSetter(t){void 0!==t&&this.text.attr({text:t}),this.updateTextPadding(),this.reAlign()}updateBoxSize(){let t;let e=this.text,o={},n=this.padding,a=this.bBox=(!r(this.widthSetting)||!r(this.heightSetting)||this.textAlign)&&i(e.textStr)?e.getBBox(void 0,0):h.emptyBBox;this.width=this.getPaddedWidth(),this.height=(this.heightSetting||a.height||0)+2*n;let l=this.renderer.fontMetrics(e);if(this.baselineOffset=n+Math.min((this.text.firstLineMetrics||l).b,a.height||1/0),this.heightSetting&&(this.baselineOffset+=(this.heightSetting-l.h)/2),this.needsBox&&!e.textPath){if(!this.box){let t=this.box=this.symbolKey?this.renderer.symbol(this.symbolKey):this.renderer.rect();t.addClass(("button"===this.className?"":"highcharts-label-box")+(this.className?" highcharts-"+this.className+"-box":"")),t.add(this)}t=this.getCrispAdjust(),o.x=t,o.y=(this.baseline?-this.baselineOffset:0)+t,o.width=Math.round(this.width),o.height=Math.round(this.height),this.box.attr(s(o,this.deferredAttr)),this.deferredAttr={}}}updateTextPadding(){let t=this.text;if(!t.textPath){this.updateBoxSize();let e=this.baseline?0:this.baselineOffset,s=n(this.paddingLeft,this.padding);i(this.widthSetting)&&this.bBox&&("center"===this.textAlign||"right"===this.textAlign)&&(s+=({center:.5,right:1})[this.textAlign]*(this.widthSetting-this.bBox.width)),(s!==t.x||e!==t.y)&&(t.attr("x",s),t.hasBoxWidthChanged&&(this.bBox=t.getBBox(!0)),void 0!==e&&t.attr("y",e)),t.x=s,t.y=e}}widthSetter(t){this.widthSetting=r(t)?t:void 0,this.doUpdate=!0}getPaddedWidth(){let t=this.padding,e=n(this.paddingLeft,t),i=n(this.paddingRight,t);return(this.widthSetting||this.bBox.width||0)+e+i}xSetter(t){this.x=t,this.alignFactor&&(t-=this.alignFactor*this.getPaddedWidth(),this["forceAnimate:x"]=!0),this.xSetting=Math.round(t),this.attr("translateX",this.xSetting)}ySetter(t){this.ySetting=this.y=Math.round(t),this.attr("translateY",this.ySetting)}}return h.emptyBBox={width:0,height:0,x:0,y:0},h.textProps=["color","direction","fontFamily","fontSize","fontStyle","fontWeight","lineHeight","textAlign","textDecoration","textOutline","textOverflow","whiteSpace","width"],h}),i(e,"Core/Renderer/SVG/Symbols.js",[e["Core/Utilities.js"]],function(t){let{defined:e,isNumber:i,pick:s}=t;function r(t,i,r,o,n){let a=[];if(n){let h=n.start||0,l=s(n.r,r),d=s(n.r,o||r),c=2e-4/(n.borderRadius?1:Math.max(l,1)),p=Math.abs((n.end||0)-h-2*Math.PI)0&&d0)return c;if(t+l>s-h){if(d>e+h&&de+h&&d0){let i=dr&&lh&&c.splice(1,1,["L",l-6,e],["L",l,e-6],["L",l+6,e],["L",s-a,e]);return c},circle:function(t,e,i,s){return r(t+i/2,e+s/2,i/2,s/2,{start:.5*Math.PI,end:2.5*Math.PI,open:!1})},diamond:function(t,e,i,s){return[["M",t+i/2,e],["L",t+i,e+s/2],["L",t+i/2,e+s],["L",t,e+s/2],["Z"]]},rect:o,roundedRect:n,square:o,triangle:function(t,e,i,s){return[["M",t+i/2,e],["L",t+i,e+s],["L",t,e+s],["Z"]]},"triangle-down":function(t,e,i,s){return[["M",t,e],["L",t+i,e],["L",t+i/2,e+s],["Z"]]}}}),i(e,"Core/Renderer/SVG/TextBuilder.js",[e["Core/Renderer/HTML/AST.js"],e["Core/Globals.js"],e["Core/Utilities.js"]],function(t,e,i){let{doc:s,SVG_NS:r,win:o}=e,{attr:n,extend:a,fireEvent:h,isString:l,objectEach:d,pick:c}=i;return class{constructor(t){let e=t.styles;this.renderer=t.renderer,this.svgElement=t,this.width=t.textWidth,this.textLineHeight=e&&e.lineHeight,this.textOutline=e&&e.textOutline,this.ellipsis=!!(e&&"ellipsis"===e.textOverflow),this.noWrap=!!(e&&"nowrap"===e.whiteSpace)}buildSVG(){let e=this.svgElement,i=e.element,r=e.renderer,o=c(e.textStr,"").toString(),n=-1!==o.indexOf("<"),a=i.childNodes,h=!e.added&&r.box,d=[o,this.ellipsis,this.noWrap,this.textLineHeight,this.textOutline,e.getStyle("font-size"),this.width].join(",");if(d!==e.textCache){e.textCache=d,delete e.actualWidth;for(let t=a.length;t--;)i.removeChild(a[t]);if(n||this.ellipsis||this.width||e.textPath||-1!==o.indexOf(" ")&&(!this.noWrap||//g.test(o))){if(""!==o){h&&h.appendChild(i);let s=new t(o);this.modifyTree(s.nodes),s.addToDOM(i),this.modifyDOM(),this.ellipsis&&-1!==(i.textContent||"").indexOf("…")&&e.attr("title",this.unescapeEntities(e.textStr||"",["<",">"])),h&&h.removeChild(i)}}else i.appendChild(s.createTextNode(this.unescapeEntities(o)));l(this.textOutline)&&e.applyTextOutline&&e.applyTextOutline(this.textOutline)}}modifyDOM(){let t;let e=this.svgElement,i=n(e.element,"x");for(e.firstLineMetrics=void 0;t=e.element.firstChild;)if(/^[\s\u200B]*$/.test(t.textContent||" "))e.element.removeChild(t);else break;[].forEach.call(e.element.querySelectorAll("tspan.highcharts-br"),(t,s)=>{t.nextSibling&&t.previousSibling&&(0===s&&1===t.previousSibling.nodeType&&(e.firstLineMetrics=e.renderer.fontMetrics(t.previousSibling)),n(t,{dy:this.getLineHeight(t.nextSibling),x:i}))});let a=this.width||0;if(!a)return;let h=(t,o)=>{let h=t.textContent||"",l=h.replace(/([^\^])-/g,"$1- ").split(" "),d=!this.noWrap&&(l.length>1||e.element.childNodes.length>1),c=this.getLineHeight(o),p=0,u=e.actualWidth;if(this.ellipsis)h&&this.truncate(t,h,void 0,0,Math.max(0,a-.8*c),(t,e)=>t.substring(0,e)+"…");else if(d){let h=[],d=[];for(;o.firstChild&&o.firstChild!==t;)d.push(o.firstChild),o.removeChild(o.firstChild);for(;l.length;)l.length&&!this.noWrap&&p>0&&(h.push(t.textContent||""),t.textContent=l.join(" ").replace(/- /g,"-")),this.truncate(t,void 0,l,0===p&&u||0,a,(t,e)=>l.slice(0,e).join(" ").replace(/- /g,"-")),u=e.actualWidth,p++;d.forEach(e=>{o.insertBefore(e,t)}),h.forEach(e=>{o.insertBefore(s.createTextNode(e),t);let a=s.createElementNS(r,"tspan");a.textContent="​",n(a,{dy:c,x:i}),o.insertBefore(a,t)})}},l=t=>{[].slice.call(t.childNodes).forEach(i=>{i.nodeType===o.Node.TEXT_NODE?h(i,t):(-1!==i.className.baseVal.indexOf("highcharts-br")&&(e.actualWidth=0),l(i))})};l(e.element)}getLineHeight(t){let e=t.nodeType===o.Node.TEXT_NODE?t.parentElement:t;return this.textLineHeight?parseInt(this.textLineHeight.toString(),10):this.renderer.fontMetrics(e||this.svgElement.element).h}modifyTree(t){let e=(i,s)=>{let{attributes:r={},children:o,style:n={},tagName:h}=i,l=this.renderer.styledMode;if("b"===h||"strong"===h?l?r.class="highcharts-strong":n.fontWeight="bold":("i"===h||"em"===h)&&(l?r.class="highcharts-emphasized":n.fontStyle="italic"),n&&n.color&&(n.fill=n.color),"br"===h){r.class="highcharts-br",i.textContent="​";let e=t[s+1];e&&e.textContent&&(e.textContent=e.textContent.replace(/^ +/gm,""))}else"a"===h&&o&&o.some(t=>"#text"===t.tagName)&&(i.children=[{children:o,tagName:"tspan"}]);"#text"!==h&&"a"!==h&&(i.tagName="tspan"),a(i,{attributes:r,style:n}),o&&o.filter(t=>"#text"!==t.tagName).forEach(e)};t.forEach(e),h(this.svgElement,"afterModifyTree",{nodes:t})}truncate(t,e,i,s,r,o){let n,a;let h=this.svgElement,{rotation:l}=h,d=[],c=i?1:0,p=(e||i||"").length,u=p,g=function(e,r){let o=r||e,n=t.parentNode;if(n&&void 0===d[o]&&n.getSubStringLength)try{d[o]=s+n.getSubStringLength(0,i?o+1:o)}catch(t){}return d[o]};if(h.rotation=0,s+(a=g(t.textContent.length))>r){for(;c<=p;)u=Math.ceil((c+p)/2),i&&(n=o(i,u)),a=g(u,n&&n.length-1),c===p?c=p+1:a>r?p=u-1:c=u;0===p?t.textContent="":e&&p===e.length-1||(t.textContent=n||o(e||i,u))}i&&i.splice(0,u),h.actualWidth=a,h.rotation=l}unescapeEntities(t,e){return d(this.renderer.escapes,function(i,s){e&&-1!==e.indexOf(i)||(t=t.toString().replace(RegExp(i,"g"),s))}),t}}}),i(e,"Core/Renderer/SVG/SVGRenderer.js",[e["Core/Renderer/HTML/AST.js"],e["Core/Defaults.js"],e["Core/Color/Color.js"],e["Core/Globals.js"],e["Core/Renderer/RendererRegistry.js"],e["Core/Renderer/SVG/SVGElement.js"],e["Core/Renderer/SVG/SVGLabel.js"],e["Core/Renderer/SVG/Symbols.js"],e["Core/Renderer/SVG/TextBuilder.js"],e["Core/Utilities.js"]],function(t,e,i,s,r,o,n,a,h,l){let d;let{defaultOptions:c}=e,{charts:p,deg2rad:u,doc:g,isFirefox:f,isMS:m,isWebKit:x,noop:y,SVG_NS:b,symbolSizes:v,win:S}=s,{addEvent:C,attr:k,createElement:M,crisp:w,css:T,defined:A,destroyObjectProperties:P,extend:L,isArray:O,isNumber:D,isObject:E,isString:I,merge:j,pick:B,pInt:R,replaceNested:z,uniqueKey:N}=l;class W{constructor(t,e,i,s,r,o,n){let a,h;let l=this.createElement("svg").attr({version:"1.1",class:"highcharts-root"}),d=l.element;n||l.css(this.getStyle(s||{})),t.appendChild(d),k(t,"dir","ltr"),-1===t.innerHTML.indexOf("xmlns")&&k(d,"xmlns",this.SVG_NS),this.box=d,this.boxWrapper=l,this.alignedObjects=[],this.url=this.getReferenceURL(),this.createElement("desc").add().element.appendChild(g.createTextNode("Created with Highcharts 11.4.8")),this.defs=this.createElement("defs").add(),this.allowHTML=o,this.forExport=r,this.styledMode=n,this.gradients={},this.cache={},this.cacheKeys=[],this.imgCount=0,this.rootFontSize=l.getStyle("font-size"),this.setSize(e,i,!1),f&&t.getBoundingClientRect&&((a=function(){T(t,{left:0,top:0}),h=t.getBoundingClientRect(),T(t,{left:Math.ceil(h.left)-h.left+"px",top:Math.ceil(h.top)-h.top+"px"})})(),this.unSubPixelFix=C(S,"resize",a))}definition(e){return new t([e]).addToDOM(this.defs.element)}getReferenceURL(){if((f||x)&&g.getElementsByTagName("base").length){if(!A(d)){let e=N(),i=new t([{tagName:"svg",attributes:{width:8,height:8},children:[{tagName:"defs",children:[{tagName:"clipPath",attributes:{id:e},children:[{tagName:"rect",attributes:{width:4,height:4}}]}]},{tagName:"rect",attributes:{id:"hitme",width:8,height:8,"clip-path":`url(#${e})`,fill:"rgba(0,0,0,0.001)"}}]}]).addToDOM(g.body);T(i,{position:"fixed",top:0,left:0,zIndex:9e5});let s=g.elementFromPoint(6,6);d="hitme"===(s&&s.id),g.body.removeChild(i)}if(d)return z(S.location.href.split("#")[0],[/<[^>]*>/g,""],[/([\('\)])/g,"\\$1"],[/ /g,"%20"])}return""}getStyle(t){return this.style=L({fontFamily:"Helvetica, Arial, sans-serif",fontSize:"1rem"},t),this.style}setStyle(t){this.boxWrapper.css(this.getStyle(t))}isHidden(){return!this.boxWrapper.getBBox().width}destroy(){let t=this.defs;return this.box=null,this.boxWrapper=this.boxWrapper.destroy(),P(this.gradients||{}),this.gradients=null,this.defs=t.destroy(),this.unSubPixelFix&&this.unSubPixelFix(),this.alignedObjects=null,null}createElement(t){return new this.Element(this,t)}getRadialAttr(t,e){return{cx:t[0]-t[2]/2+(e.cx||0)*t[2],cy:t[1]-t[2]/2+(e.cy||0)*t[2],r:(e.r||0)*t[2]}}shadowDefinition(t){let e=[`highcharts-drop-shadow-${this.chartIndex}`,...Object.keys(t).map(e=>`${e}-${t[e]}`)].join("-").toLowerCase().replace(/[^a-z\d\-]/g,""),i=j({color:"#000000",offsetX:1,offsetY:1,opacity:.15,width:5},t);return this.defs.element.querySelector(`#${e}`)||this.definition({tagName:"filter",attributes:{id:e,filterUnits:i.filterUnits},children:this.getShadowFilterContent(i)}),e}getShadowFilterContent(t){return[{tagName:"feDropShadow",attributes:{dx:t.offsetX,dy:t.offsetY,"flood-color":t.color,"flood-opacity":Math.min(5*t.opacity,1),stdDeviation:t.width/2}}]}buildText(t){new h(t).buildSVG()}getContrast(t){let e=i.parse(t).rgba.map(t=>{let e=t/255;return e<=.03928?e/12.92:Math.pow((e+.055)/1.055,2.4)}),s=.2126*e[0]+.7152*e[1]+.0722*e[2];return 1.05/(s+.05)>(s+.05)/.05?"#FFFFFF":"#000000"}button(e,i,s,r,o={},n,a,h,l,d){let p=this.label(e,i,s,l,void 0,void 0,d,void 0,"button"),u=this.styledMode,g=arguments,f=0;o=j(c.global.buttonTheme,o),u&&(delete o.fill,delete o.stroke,delete o["stroke-width"]);let x=o.states||{},y=o.style||{};delete o.states,delete o.style;let b=[t.filterUserAttributes(o)],v=[y];return u||["hover","select","disabled"].forEach((e,i)=>{b.push(j(b[0],t.filterUserAttributes(g[i+5]||x[e]||{}))),v.push(b[i+1].style),delete b[i+1].style}),C(p.element,m?"mouseover":"mouseenter",function(){3!==f&&p.setState(1)}),C(p.element,m?"mouseout":"mouseleave",function(){3!==f&&p.setState(f)}),p.setState=(t=0)=>{if(1!==t&&(p.state=f=t),p.removeClass(/highcharts-button-(normal|hover|pressed|disabled)/).addClass("highcharts-button-"+["normal","hover","pressed","disabled"][t]),!u){p.attr(b[t]);let e=v[t];E(e)&&p.css(e)}},p.attr(b[0]),!u&&(p.css(L({cursor:"default"},y)),d&&p.text.css({pointerEvents:"none"})),p.on("touchstart",t=>t.stopPropagation()).on("click",function(t){3!==f&&r.call(p,t)})}crispLine(t,e){let[i,s]=t;return A(i[1])&&i[1]===s[1]&&(i[1]=s[1]=w(i[1],e)),A(i[2])&&i[2]===s[2]&&(i[2]=s[2]=w(i[2],e)),t}path(t){let e=this.styledMode?{}:{fill:"none"};return O(t)?e.d=t:E(t)&&L(e,t),this.createElement("path").attr(e)}circle(t,e,i){let s=E(t)?t:void 0===t?{}:{x:t,y:e,r:i},r=this.createElement("circle");return r.xSetter=r.ySetter=function(t,e,i){i.setAttribute("c"+e,t)},r.attr(s)}arc(t,e,i,s,r,o){let n;E(t)?(e=(n=t).y,i=n.r,s=n.innerR,r=n.start,o=n.end,t=n.x):n={innerR:s,start:r,end:o};let a=this.symbol("arc",t,e,i,i,n);return a.r=i,a}rect(t,e,i,s,r,o){let n=E(t)?t:void 0===t?{}:{x:t,y:e,r,width:Math.max(i||0,0),height:Math.max(s||0,0)},a=this.createElement("rect");return this.styledMode||(void 0!==o&&(n["stroke-width"]=o,L(n,a.crisp(n))),n.fill="none"),a.rSetter=function(t,e,i){a.r=t,k(i,{rx:t,ry:t})},a.rGetter=function(){return a.r||0},a.attr(n)}roundedRect(t){return this.symbol("roundedRect").attr(t)}setSize(t,e,i){this.width=t,this.height=e,this.boxWrapper.animate({width:t,height:e},{step:function(){this.attr({viewBox:"0 0 "+this.attr("width")+" "+this.attr("height")})},duration:B(i,!0)?void 0:0}),this.alignElements()}g(t){let e=this.createElement("g");return t?e.attr({class:"highcharts-"+t}):e}image(t,e,i,s,r,o){let n={preserveAspectRatio:"none"};D(e)&&(n.x=e),D(i)&&(n.y=i),D(s)&&(n.width=s),D(r)&&(n.height=r);let a=this.createElement("image").attr(n),h=function(e){a.attr({href:t}),o.call(a,e)};if(o){a.attr({href:"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="});let e=new S.Image;C(e,"load",h),e.src=t,e.complete&&h({})}else a.attr({href:t});return a}symbol(t,e,i,s,r,o){let n,a,h,l;let d=this,c=/^url\((.*?)\)$/,u=c.test(t),f=!u&&(this.symbols[t]?t:"circle"),m=f&&this.symbols[f];if(m)"number"==typeof e&&(a=m.call(this.symbols,e||0,i||0,s||0,r||0,o)),n=this.path(a),d.styledMode||n.attr("fill","none"),L(n,{symbolName:f||void 0,x:e,y:i,width:s,height:r}),o&&L(n,o);else if(u){h=t.match(c)[1];let s=n=this.image(h);s.imgwidth=B(o&&o.width,v[h]&&v[h].width),s.imgheight=B(o&&o.height,v[h]&&v[h].height),l=t=>t.attr({width:t.width,height:t.height}),["width","height"].forEach(t=>{s[`${t}Setter`]=function(t,e){this[e]=t;let{alignByTranslate:i,element:s,width:r,height:n,imgwidth:a,imgheight:h}=this,l="width"===e?a:h,d=1;o&&"within"===o.backgroundSize&&r&&n&&a&&h?(d=Math.min(r/a,n/h),k(s,{width:Math.round(a*d),height:Math.round(h*d)})):s&&l&&s.setAttribute(e,l),!i&&a&&h&&this.translate(((r||0)-a*d)/2,((n||0)-h*d)/2)}}),A(e)&&s.attr({x:e,y:i}),s.isImg=!0,s.symbolUrl=t,A(s.imgwidth)&&A(s.imgheight)?l(s):(s.attr({width:0,height:0}),M("img",{onload:function(){let t=p[d.chartIndex];0===this.width&&(T(this,{position:"absolute",top:"-999em"}),g.body.appendChild(this)),v[h]={width:this.width,height:this.height},s.imgwidth=this.width,s.imgheight=this.height,s.element&&l(s),this.parentNode&&this.parentNode.removeChild(this),d.imgCount--,d.imgCount||!t||t.hasLoaded||t.onload()},src:h}),this.imgCount++)}return n}clipRect(t,e,i,s){return this.rect(t,e,i,s,0)}text(t,e,i,s){let r={};if(s&&(this.allowHTML||!this.forExport))return this.html(t,e,i);r.x=Math.round(e||0),i&&(r.y=Math.round(i)),A(t)&&(r.text=t);let o=this.createElement("text").attr(r);return s&&(!this.forExport||this.allowHTML)||(o.xSetter=function(t,e,i){let s=i.getElementsByTagName("tspan"),r=i.getAttribute(e);for(let i=0,o;it.align())}}return L(W.prototype,{Element:o,SVG_NS:b,escapes:{"&":"&","<":"<",">":">","'":"'",'"':"""},symbols:a,draw:y}),r.registerRendererType("svg",W,!0),W}),i(e,"Core/Renderer/HTML/HTMLElement.js",[e["Core/Renderer/HTML/AST.js"],e["Core/Globals.js"],e["Core/Renderer/SVG/SVGElement.js"],e["Core/Utilities.js"]],function(t,e,i,s){let{composed:r}=e,{attr:o,css:n,createElement:a,defined:h,extend:l,pInt:d,pushUnique:c}=s;function p(t,e,s){let r=this.div?.style||s.style;i.prototype[`${e}Setter`].call(this,t,e,s),r&&(r[e]=t)}let u=(t,e)=>{if(!t.div){let s=o(t.element,"class"),r=t.css,n=a("div",s?{className:s}:void 0,{position:"absolute",left:`${t.translateX||0}px`,top:`${t.translateY||0}px`,...t.styles,display:t.display,opacity:t.opacity,visibility:t.visibility},t.parentGroup?.div||e);t.classSetter=(t,e,i)=>{i.setAttribute("class",t),n.className=t},t.translateXSetter=t.translateYSetter=(e,i)=>{t[i]=e,n.style["translateX"===i?"left":"top"]=`${e}px`,t.doTransform=!0},t.opacitySetter=t.visibilitySetter=p,t.css=e=>(r.call(t,e),e.cursor&&(n.style.cursor=e.cursor),e.pointerEvents&&(n.style.pointerEvents=e.pointerEvents),t),t.on=function(){return i.prototype.on.apply({element:n,onEvents:t.onEvents},arguments),t},t.div=n}return t.div};class g extends i{static compose(t){c(r,this.compose)&&(t.prototype.html=function(t,e,i){return new g(this,"span").attr({text:t,x:Math.round(e),y:Math.round(i)})})}constructor(t,e){super(t,e),this.css({position:"absolute",...t.styledMode?{}:{fontFamily:t.style.fontFamily,fontSize:t.style.fontSize}}),this.element.style.whiteSpace="nowrap"}getSpanCorrection(t,e,i){this.xCorr=-t*i,this.yCorr=-e}css(t){let e;let{element:i}=this,s="SPAN"===i.tagName&&t&&"width"in t,r=s&&t.width;return s&&(delete t.width,this.textWidth=d(r)||void 0,e=!0),t?.textOverflow==="ellipsis"&&(t.whiteSpace="nowrap",t.overflow="hidden"),l(this.styles,t),n(i,t),e&&this.updateTransform(),this}htmlGetBBox(){let{element:t}=this;return{x:t.offsetLeft,y:t.offsetTop,width:t.offsetWidth,height:t.offsetHeight}}updateTransform(){if(!this.added){this.alignOnAdd=!0;return}let{element:t,renderer:e,rotation:i,rotationOriginX:s,rotationOriginY:r,styles:o,textAlign:a="left",textWidth:l,translateX:d=0,translateY:c=0,x:p=0,y:u=0}=this,g={left:0,center:.5,right:1}[a],f=o.whiteSpace;if(n(t,{marginLeft:`${d}px`,marginTop:`${c}px`}),"SPAN"===t.tagName){let o=[i,a,t.innerHTML,l,this.textAlign].join(","),d=-(this.parentGroup?.padding*1)||0,c,m=!1;if(l!==this.oldTextWidth){let e=this.textPxLength?this.textPxLength:(n(t,{width:"",whiteSpace:f||"nowrap"}),t.offsetWidth),s=l||0;(s>this.oldTextWidth||e>s)&&(/[ \-]/.test(t.textContent||t.innerText)||"ellipsis"===t.style.textOverflow)&&(n(t,{width:e>s||i?l+"px":"auto",display:"block",whiteSpace:f||"normal"}),this.oldTextWidth=l,m=!0)}this.hasBoxWidthChanged=m,o!==this.cTT&&(c=e.fontMetrics(t).b,h(i)&&(i!==(this.oldRotation||0)||a!==this.oldAlign)&&this.setSpanRotation(i,d,d),this.getSpanCorrection(!h(i)&&this.textPxLength||t.offsetWidth,c,g));let{xCorr:x=0,yCorr:y=0}=this,b=(s??p)-x-p-d,v=(r??u)-y-u-d;n(t,{left:`${p+x}px`,top:`${u+y}px`,transformOrigin:`${b}px ${v}px`}),this.cTT=o,this.oldRotation=i,this.oldAlign=a}}setSpanRotation(t,e,i){n(this.element,{transform:`rotate(${t}deg)`,transformOrigin:`${e}% ${i}px`})}add(t){let e;let i=this.renderer.box.parentNode,s=[];if(this.parentGroup=t,t&&!(e=t.div)){let r=t;for(;r;)s.push(r),r=r.parentGroup;for(let t of s.reverse())e=u(t,i)}return(e||i).appendChild(this.element),this.added=!0,this.alignOnAdd&&this.updateTransform(),this}textSetter(e){e!==this.textStr&&(delete this.bBox,delete this.oldTextWidth,t.setElementHTML(this.element,e??""),this.textStr=e,this.doTransform=!0)}alignSetter(t){this.alignValue=this.textAlign=t,this.doTransform=!0}xSetter(t,e){this[e]=t,this.doTransform=!0}}let f=g.prototype;return f.visibilitySetter=f.opacitySetter=p,f.ySetter=f.rotationSetter=f.rotationOriginXSetter=f.rotationOriginYSetter=f.xSetter,g}),i(e,"Core/Axis/AxisDefaults.js",[],function(){var t,e;return(e=t||(t={})).xAxis={alignTicks:!0,allowDecimals:void 0,panningEnabled:!0,zIndex:2,zoomEnabled:!0,dateTimeLabelFormats:{millisecond:{main:"%H:%M:%S.%L",range:!1},second:{main:"%H:%M:%S",range:!1},minute:{main:"%H:%M",range:!1},hour:{main:"%H:%M",range:!1},day:{main:"%e %b"},week:{main:"%e %b"},month:{main:"%b '%y"},year:{main:"%Y"}},endOnTick:!1,gridLineDashStyle:"Solid",gridZIndex:1,labels:{autoRotationLimit:80,distance:15,enabled:!0,indentation:10,overflow:"justify",reserveSpace:void 0,rotation:void 0,staggerLines:0,step:0,useHTML:!1,zIndex:7,style:{color:"#333333",cursor:"default",fontSize:"0.8em"}},maxPadding:.01,minorGridLineDashStyle:"Solid",minorTickLength:2,minorTickPosition:"outside",minorTicksPerMajor:5,minPadding:.01,offset:void 0,reversed:void 0,reversedStacks:!1,showEmpty:!0,showFirstLabel:!0,showLastLabel:!0,startOfWeek:1,startOnTick:!1,tickLength:10,tickPixelInterval:100,tickmarkPlacement:"between",tickPosition:"outside",title:{align:"middle",useHTML:!1,x:0,y:0,style:{color:"#666666",fontSize:"0.8em"}},visible:!0,minorGridLineColor:"#f2f2f2",minorGridLineWidth:1,minorTickColor:"#999999",lineColor:"#333333",lineWidth:1,gridLineColor:"#e6e6e6",gridLineWidth:void 0,tickColor:"#333333"},e.yAxis={reversedStacks:!0,endOnTick:!0,maxPadding:.05,minPadding:.05,tickPixelInterval:72,showLastLabel:!0,labels:{x:void 0},startOnTick:!0,title:{text:"Values"},stackLabels:{animation:{},allowOverlap:!1,enabled:!1,crop:!0,overflow:"justify",formatter:function(){let{numberFormatter:t}=this.axis.chart;return t(this.total||0,-1)},style:{color:"#000000",fontSize:"0.7em",fontWeight:"bold",textOutline:"1px contrast"}},gridLineWidth:1,lineWidth:0},t}),i(e,"Core/Foundation.js",[e["Core/Utilities.js"]],function(t){var e;let{addEvent:i,isFunction:s,objectEach:r,removeEvent:o}=t;return(e||(e={})).registerEventOptions=function(t,e){t.eventOptions=t.eventOptions||{},r(e.events,function(e,r){t.eventOptions[r]!==e&&(t.eventOptions[r]&&(o(t,r,t.eventOptions[r]),delete t.eventOptions[r]),s(e)&&(t.eventOptions[r]=e,i(t,r,e,{order:0})))})},e}),i(e,"Core/Axis/Tick.js",[e["Core/Templating.js"],e["Core/Globals.js"],e["Core/Utilities.js"]],function(t,e,i){let{deg2rad:s}=e,{clamp:r,correctFloat:o,defined:n,destroyObjectProperties:a,extend:h,fireEvent:l,isNumber:d,merge:c,objectEach:p,pick:u}=i;return class{constructor(t,e,i,s,r){this.isNew=!0,this.isNewLabel=!0,this.axis=t,this.pos=e,this.type=i||"",this.parameters=r||{},this.tickmarkOffset=this.parameters.tickmarkOffset,this.options=this.parameters.options,l(this,"init"),i||s||this.addLabel()}addLabel(){let e=this,i=e.axis,s=i.options,r=i.chart,a=i.categories,c=i.logarithmic,p=i.names,g=e.pos,f=u(e.options&&e.options.labels,s.labels),m=i.tickPositions,x=g===m[0],y=g===m[m.length-1],b=(!f.step||1===f.step)&&1===i.tickInterval,v=m.info,S=e.label,C,k,M,w=this.parameters.category||(a?u(a[g],p[g],g):g);c&&d(w)&&(w=o(c.lin2log(w))),i.dateTime&&(v?C=(k=r.time.resolveDTLFormat(s.dateTimeLabelFormats[!s.grid&&v.higherRanks[g]||v.unitName])).main:d(w)&&(C=i.dateTime.getXDateFormat(w,s.dateTimeLabelFormats||{}))),e.isFirst=x,e.isLast=y;let T={axis:i,chart:r,dateTimeLabelFormat:C,isFirst:x,isLast:y,pos:g,tick:e,tickPositionInfo:v,value:w};l(this,"labelFormat",T);let A=e=>f.formatter?f.formatter.call(e,e):f.format?(e.text=i.defaultLabelFormatter.call(e),t.format(f.format,e,r)):i.defaultLabelFormatter.call(e),P=A.call(T,T),L=k&&k.list;L?e.shortenLabel=function(){for(M=0;M0&&r+c*p>h&&(v=Math.round((o-r)/Math.cos(d*s))):(y=r-c*p,b=r+(1-c)*p,yh&&(m=h-t.x+m*c,x=-1),(m=Math.min(g,m))m||e.autoRotation&&(l.styles||{}).width)&&(v=m)),v&&(this.shortenLabel?this.shortenLabel():(f.width=Math.floor(v)+"px",(i.style||{}).textOverflow||(f.textOverflow="ellipsis"),l.css(f)))}moveLabel(t,e){let i=this,s=i.label,r=i.axis,o=!1,n;s&&s.textStr===t?(i.movedLabel=s,o=!0,delete i.label):p(r.ticks,function(e){o||e.isNew||e===i||!e.label||e.label.textStr!==t||(i.movedLabel=e.label,o=!0,e.labelPos=i.movedLabel.xy,delete e.label)}),!o&&(i.labelPos||s)&&(n=i.labelPos||s.xy,i.movedLabel=i.createLabel(t,e,n),i.movedLabel&&i.movedLabel.attr({opacity:0}))}render(t,e,i){let s=this.axis,r=s.horiz,n=this.pos,a=u(this.tickmarkOffset,s.tickmarkOffset),h=this.getPosition(r,n,a,e),d=h.x,c=h.y,p=s.pos,g=p+s.len,f=r?d:c;!s.chart.polar&&this.isNew&&(o(f)g)&&(i=0);let m=u(i,this.label&&this.label.newOpacity,1);i=u(i,1),this.isActive=!0,this.renderGridLine(e,i),this.renderMark(h,i),this.renderLabel(h,e,m,t),this.isNew=!1,l(this,"afterRender")}renderGridLine(t,e){let i=this.axis,s=i.options,r={},o=this.pos,n=this.type,a=u(this.tickmarkOffset,i.tickmarkOffset),h=i.chart.renderer,l=this.gridLine,d,c=s.gridLineWidth,p=s.gridLineColor,g=s.gridLineDashStyle;"minor"===this.type&&(c=s.minorGridLineWidth,p=s.minorGridLineColor,g=s.minorGridLineDashStyle),l||(i.chart.styledMode||(r.stroke=p,r["stroke-width"]=c||0,r.dashstyle=g),n||(r.zIndex=1),t&&(e=0),this.gridLine=l=h.path().attr(r).addClass("highcharts-"+(n?n+"-":"")+"grid-line").add(i.gridGroup)),l&&(d=i.getPlotLinePath({value:o+a,lineWidth:l.strokeWidth(),force:"pass",old:t,acrossPanes:!1}))&&l[t||this.isNew?"attr":"animate"]({d:d,opacity:e})}renderMark(t,e){let i=this.axis,s=i.options,r=i.chart.renderer,o=this.type,n=i.tickSize(o?o+"Tick":"tick"),a=t.x,h=t.y,l=u(s["minor"!==o?"tickWidth":"minorTickWidth"],!o&&i.isXAxis?1:0),d=s["minor"!==o?"tickColor":"minorTickColor"],c=this.mark,p=!c;n&&(i.opposite&&(n[0]=-n[0]),c||(this.mark=c=r.path().addClass("highcharts-"+(o?o+"-":"")+"tick").add(i.axisGroup),i.chart.styledMode||c.attr({stroke:d,"stroke-width":l})),c[p?"attr":"animate"]({d:this.getMarkPath(a,h,n[0],c.strokeWidth(),i.horiz,r),opacity:e}))}renderLabel(t,e,i,s){let r=this.axis,o=r.horiz,n=r.options,a=this.label,h=n.labels,l=h.step,c=u(this.tickmarkOffset,r.tickmarkOffset),p=t.x,g=t.y,f=!0;a&&d(p)&&(a.xy=t=this.getLabelPosition(p,g,a,o,h,c,s,l),(!this.isFirst||this.isLast||n.showFirstLabel)&&(!this.isLast||this.isFirst||n.showLastLabel)?!o||h.step||h.rotation||e||0===i||this.handleOverflow(t):f=!1,l&&s%l&&(f=!1),f&&d(t.y)?(t.opacity=i,a[this.isNewLabel?"attr":"animate"](t).show(!0),this.isNewLabel=!1):(a.hide(),this.isNewLabel=!0))}replaceMovedLabel(){let t=this.label,e=this.axis;t&&!this.isNew&&(t.animate({opacity:0},void 0,t.destroy),delete this.label),e.isDirty=!0,this.label=this.movedLabel,delete this.movedLabel}}}),i(e,"Core/Axis/Axis.js",[e["Core/Animation/AnimationUtilities.js"],e["Core/Axis/AxisDefaults.js"],e["Core/Color/Color.js"],e["Core/Defaults.js"],e["Core/Foundation.js"],e["Core/Globals.js"],e["Core/Axis/Tick.js"],e["Core/Utilities.js"]],function(t,e,i,s,r,o,n,a){let{animObject:h}=t,{xAxis:l,yAxis:d}=e,{defaultOptions:c}=s,{registerEventOptions:p}=r,{deg2rad:u}=o,{arrayMax:g,arrayMin:f,clamp:m,correctFloat:x,defined:y,destroyObjectProperties:b,erase:v,error:S,extend:C,fireEvent:k,getClosestDistance:M,insertItem:w,isArray:T,isNumber:A,isString:P,merge:L,normalizeTickInterval:O,objectEach:D,pick:E,relativeLength:I,removeEvent:j,splat:B,syncTimeout:R}=a,z=(t,e)=>O(e,void 0,void 0,E(t.options.allowDecimals,e<.5||void 0!==t.tickAmount),!!t.tickAmount);C(c,{xAxis:l,yAxis:L(l,d)});class N{constructor(t,e,i){this.init(t,e,i)}init(t,e,i=this.coll){let s="xAxis"===i,r=this.isZAxis||(t.inverted?!s:s);this.chart=t,this.horiz=r,this.isXAxis=s,this.coll=i,k(this,"init",{userOptions:e}),this.opposite=E(e.opposite,this.opposite),this.side=E(e.side,this.side,r?this.opposite?0:2:this.opposite?1:3),this.setOptions(e);let o=this.options,n=o.labels;this.type??(this.type=o.type||"linear"),this.uniqueNames??(this.uniqueNames=o.uniqueNames??!0),k(this,"afterSetType"),this.userOptions=e,this.minPixelPadding=0,this.reversed=E(o.reversed,this.reversed),this.visible=o.visible,this.zoomEnabled=o.zoomEnabled,this.hasNames="category"===this.type||!0===o.categories,this.categories=T(o.categories)&&o.categories||(this.hasNames?[]:void 0),this.names||(this.names=[],this.names.keys={}),this.plotLinesAndBandsGroups={},this.positiveValuesOnly=!!this.logarithmic,this.isLinked=y(o.linkedTo),this.ticks={},this.labelEdge=[],this.minorTicks={},this.plotLinesAndBands=[],this.alternateBands={},this.len??(this.len=0),this.minRange=this.userMinRange=o.minRange||o.maxZoom,this.range=o.range,this.offset=o.offset||0,this.max=void 0,this.min=void 0;let a=E(o.crosshair,B(t.options.tooltip.crosshairs)[s?0:1]);this.crosshair=!0===a?{}:a,-1===t.axes.indexOf(this)&&(s?t.axes.splice(t.xAxis.length,0,this):t.axes.push(this),w(this,t[this.coll])),t.orderItems(this.coll),this.series=this.series||[],t.inverted&&!this.isZAxis&&s&&!y(this.reversed)&&(this.reversed=!0),this.labelRotation=A(n.rotation)?n.rotation:void 0,p(this,o),k(this,"afterInit")}setOptions(t){let e=this.horiz?{labels:{autoRotation:[-45],padding:4},margin:15}:{labels:{padding:1},title:{rotation:90*this.side}};this.options=L(e,c[this.coll],t),k(this,"afterSetOptions",{userOptions:t})}defaultLabelFormatter(){let t=this.axis,{numberFormatter:e}=this.chart,i=A(this.value)?this.value:NaN,s=t.chart.time,r=t.categories,o=this.dateTimeLabelFormat,n=c.lang,a=n.numericSymbols,h=n.numericSymbolMagnitude||1e3,l=t.logarithmic?Math.abs(i):t.tickInterval,d=a&&a.length,p,u;if(r)u=`${this.value}`;else if(o)u=s.dateFormat(o,i);else if(d&&a&&l>=1e3)for(;d--&&void 0===u;)l>=(p=Math.pow(h,d+1))&&10*i%p==0&&null!==a[d]&&0!==i&&(u=e(i/p,-1)+a[d]);return void 0===u&&(u=Math.abs(i)>=1e4?e(i,-1):e(i,-1,void 0,"")),u}getSeriesExtremes(){let t;let e=this;k(this,"getSeriesExtremes",null,function(){e.hasVisibleSeries=!1,e.dataMin=e.dataMax=e.threshold=void 0,e.softThreshold=!e.isXAxis,e.series.forEach(i=>{if(i.reserveSpace()){let s=i.options,r,o=s.threshold,n,a;if(e.hasVisibleSeries=!0,e.positiveValuesOnly&&0>=(o||0)&&(o=void 0),e.isXAxis)(r=i.xData)&&r.length&&(r=e.logarithmic?r.filter(t=>t>0):r,n=(t=i.getXExtremes(r)).min,a=t.max,A(n)||n instanceof Date||(r=r.filter(A),n=(t=i.getXExtremes(r)).min,a=t.max),r.length&&(e.dataMin=Math.min(E(e.dataMin,n),n),e.dataMax=Math.max(E(e.dataMax,a),a)));else{let t=i.applyExtremes();A(t.dataMin)&&(n=t.dataMin,e.dataMin=Math.min(E(e.dataMin,n),n)),A(t.dataMax)&&(a=t.dataMax,e.dataMax=Math.max(E(e.dataMax,a),a)),y(o)&&(e.threshold=o),(!s.softThreshold||e.positiveValuesOnly)&&(e.softThreshold=!1)}}})}),k(this,"afterGetSeriesExtremes")}translate(t,e,i,s,r,o){let n=this.linkedParent||this,a=s&&n.old?n.old.min:n.min;if(!A(a))return NaN;let h=n.minPixelPadding,l=(n.isOrdinal||n.brokenAxis?.hasBreaks||n.logarithmic&&r)&&n.lin2val,d=1,c=0,p=s&&n.old?n.old.transA:n.transA,u=0;return p||(p=n.transA),i&&(d*=-1,c=n.len),n.reversed&&(d*=-1,c-=d*(n.sector||n.len)),e?(u=(t=t*d+c-h)/p+a,l&&(u=n.lin2val(u))):(l&&(t=n.val2lin(t)),u=d*(t-a)*p+c+d*h+(A(o)?p*o:0),n.isRadial||(u=x(u))),u}toPixels(t,e){return this.translate(t,!1,!this.horiz,void 0,!0)+(e?0:this.pos)}toValue(t,e){return this.translate(t-(e?0:this.pos),!0,!this.horiz,void 0,!0)}getPlotLinePath(t){let e=this,i=e.chart,s=e.left,r=e.top,o=t.old,n=t.value,a=t.lineWidth,h=o&&i.oldChartHeight||i.chartHeight,l=o&&i.oldChartWidth||i.chartWidth,d=e.transB,c=t.translatedValue,p=t.force,u,g,f,x,y;function b(t,e,i){return"pass"!==p&&(ti)&&(p?t=m(t,e,i):y=!0),t}let v={value:n,lineWidth:a,old:o,force:p,acrossPanes:t.acrossPanes,translatedValue:c};return k(this,"getPlotLinePath",v,function(t){u=f=(c=m(c=E(c,e.translate(n,void 0,void 0,o)),-1e9,1e9))+d,g=x=h-c-d,A(c)?e.horiz?(g=r,x=h-e.bottom+(e.options.isInternal?0:i.scrollablePixelsY||0),u=f=b(u,s,s+e.width)):(u=s,f=l-e.right+(i.scrollablePixelsX||0),g=x=b(g,r,r+e.height)):(y=!0,p=!1),t.path=y&&!p?void 0:i.renderer.crispLine([["M",u,g],["L",f,x]],a||1)}),v.path}getLinearTickPositions(t,e,i){let s,r,o;let n=x(Math.floor(e/t)*t),a=x(Math.ceil(i/t)*t),h=[];if(x(n+t)===n&&(o=20),this.single)return[e];for(s=n;s<=a&&(h.push(s),(s=x(s+t,o))!==r);)r=s;return h}getMinorTickInterval(){let{minorTicks:t,minorTickInterval:e}=this.options;return!0===t?E(e,"auto"):!1!==t?e:void 0}getMinorTickPositions(){let t=this.options,e=this.tickPositions,i=this.minorTickInterval,s=this.pointRangePadding||0,r=(this.min||0)-s,o=(this.max||0)+s,n=o-r,a=[],h;if(n&&n/i(t.xIncrement?t.xData?.slice(0,2):t.xData)||[]))||0),this.dataMax-this.dataMin)),A(i)&&A(s)&&A(r)&&i-s=r,o=(r-i+s)/2,a=[s-o,E(t.min,s-o)],n&&(a[2]=e?e.log2lin(this.dataMin):this.dataMin),h=[(s=g(a))+r,E(t.max,s+r)],n&&(h[2]=e?e.log2lin(this.dataMax):this.dataMax),(i=f(h))-st-e),t=M([i]))}return t&&e?Math.min(t,e):t||e}nameToX(t){let e=T(this.options.categories),i=e?this.categories:this.names,s=t.options.x,r;return t.series.requireSorting=!1,y(s)||(s=this.uniqueNames&&i?e?i.indexOf(t.name):E(i.keys[t.name],-1):t.series.autoIncrement()),-1===s?!e&&i&&(r=i.length):r=s,void 0!==r?(this.names[r]=t.name,this.names.keys[t.name]=r):t.x&&(r=t.x),r}updateNames(){let t=this,e=this.names;e.length>0&&(Object.keys(e.keys).forEach(function(t){delete e.keys[t]}),e.length=0,this.minRange=this.userMinRange,(this.series||[]).forEach(e=>{e.xIncrement=null,(!e.points||e.isDirtyData)&&(t.max=Math.max(t.max,e.xData.length-1),e.processData(),e.generatePoints()),e.data.forEach(function(i,s){let r;i?.options&&void 0!==i.name&&void 0!==(r=t.nameToX(i))&&r!==i.x&&(i.x=r,e.xData[s]=r)})}))}setAxisTranslation(){let t=this,e=t.max-t.min,i=t.linkedParent,s=!!t.categories,r=t.isXAxis,o=t.axisPointRange||0,n,a=0,h=0,l,d=t.transA;(r||s||o)&&(n=t.getClosest(),i?(a=i.minPointOffset,h=i.pointRangePadding):t.series.forEach(function(e){let i=s?1:r?E(e.options.pointRange,n,0):t.axisPointRange||0,l=e.options.pointPlacement;if(o=Math.max(o,i),!t.single||s){let t=e.is("xrange")?!r:r;a=Math.max(a,t&&P(l)?0:i/2),h=Math.max(h,t&&"on"===l?0:i)}}),l=t.ordinal&&t.ordinal.slope&&n?t.ordinal.slope/n:1,t.minPointOffset=a*=l,t.pointRangePadding=h*=l,t.pointRange=Math.min(o,t.single&&s?1:e),r&&n&&(t.closestPointRange=n)),t.translationSlope=t.transA=d=t.staticScale||t.len/(e+h||1),t.transB=t.horiz?t.left:t.bottom,t.minPixelPadding=d*a,k(this,"afterSetAxisTranslation")}minFromRange(){let{max:t,min:e}=this;return A(t)&&A(e)&&t-e||void 0}setTickInterval(t){let{categories:e,chart:i,dataMax:s,dataMin:r,dateTime:o,isXAxis:n,logarithmic:a,options:h,softThreshold:l}=this,d=A(this.threshold)?this.threshold:void 0,c=this.minRange||0,{ceiling:p,floor:u,linkedTo:g,softMax:f,softMin:m}=h,b=A(g)&&i[this.coll]?.[g],v=h.tickPixelInterval,C=h.maxPadding,M=h.minPadding,w=0,T,P=A(h.tickInterval)&&h.tickInterval>=0?h.tickInterval:void 0,L,O,D,I;if(o||e||b||this.getTickAmount(),D=E(this.userMin,h.min),I=E(this.userMax,h.max),b?(this.linkedParent=b,T=b.getExtremes(),this.min=E(T.min,T.dataMin),this.max=E(T.max,T.dataMax),this.type!==b.type&&S(11,!0,i)):(l&&y(d)&&A(s)&&A(r)&&(r>=d?(L=d,M=0):s<=d&&(O=d,C=0)),this.min=E(D,L,r),this.max=E(I,O,s)),A(this.max)&&A(this.min)&&(a&&(this.positiveValuesOnly&&!t&&0>=Math.min(this.min,E(r,this.min))&&S(10,!0,i),this.min=x(a.log2lin(this.min),16),this.max=x(a.log2lin(this.max),16)),this.range&&A(r)&&(this.userMin=this.min=D=Math.max(r,this.minFromRange()||0),this.userMax=I=this.max,this.range=void 0)),k(this,"foundExtremes"),this.adjustForMinRange(),A(this.min)&&A(this.max)){if(!A(this.userMin)&&A(m)&&mthis.max&&(this.max=I=f),e||this.axisPointRange||this.stacking?.usePercentage||b||!(w=this.max-this.min)||(!y(D)&&M&&(this.min-=w*M),y(I)||!C||(this.max+=w*C)),!A(this.userMin)&&A(u)&&(this.min=Math.max(this.min,u)),!A(this.userMax)&&A(p)&&(this.max=Math.min(this.max,p)),l&&A(r)&&A(s)){let t=d||0;!y(D)&&this.min=t?this.min=h.minRange?Math.min(t,this.max-c):t:!y(I)&&this.max>t&&s<=t&&(this.max=h.minRange?Math.max(t,this.min+c):t)}!i.polar&&this.min>this.max&&(y(h.min)?this.max=this.min:y(h.max)&&(this.min=this.max)),w=this.max-this.min}if(this.min!==this.max&&A(this.min)&&A(this.max)?b&&!P&&v===b.options.tickPixelInterval?this.tickInterval=P=b.tickInterval:this.tickInterval=E(P,this.tickAmount?w/Math.max(this.tickAmount-1,1):void 0,e?1:w*v/Math.max(this.len,v)):this.tickInterval=1,n&&!t){let t=this.min!==this.old?.min||this.max!==this.old?.max;this.series.forEach(function(e){e.forceCrop=e.forceCropping?.(),e.processData(t)}),k(this,"postProcessData",{hasExtremesChanged:t})}this.setAxisTranslation(),k(this,"initialAxisTranslation"),this.pointRange&&!P&&(this.tickInterval=Math.max(this.pointRange,this.tickInterval));let j=E(h.minTickInterval,o&&!this.series.some(t=>t.noSharedTooltip)?this.closestPointRange:0);!P&&this.tickIntervalMath.max(2*this.len,200))a=[this.min,this.max],S(19,!1,this.chart);else if(this.dateTime)a=this.getTimeTicks(this.dateTime.normalizeTimeTickInterval(this.tickInterval,t.units),this.min,this.max,t.startOfWeek,this.ordinal?.positions,this.closestPointRange,!0);else if(this.logarithmic)a=this.logarithmic.getLogTickPositions(this.tickInterval,this.min,this.max);else{let t=this.tickInterval,e=t;for(;e<=2*t;)if(a=this.getLinearTickPositions(this.tickInterval,this.min,this.max),this.tickAmount&&a.length>this.tickAmount)this.tickInterval=z(this,e*=1.1);else break}a.length>this.len&&(a=[a[0],a[a.length-1]])[0]===a[1]&&(a.length=1),i&&(this.tickPositions=a,(h=i.apply(this,[this.min,this.max]))&&(a=h))}this.tickPositions=a,this.minorTickInterval="auto"===s&&this.tickInterval?this.tickInterval/t.minorTicksPerMajor:s,this.paddedTicks=a.slice(0),this.trimTicks(a,o,n),!this.isLinked&&A(this.min)&&A(this.max)&&(this.single&&a.length<2&&!this.categories&&!this.series.some(t=>t.is("heatmap")&&"between"===t.options.pointPlacement)&&(this.min-=.5,this.max+=.5),e||h||this.adjustTickAmount()),k(this,"afterSetTickPositions")}trimTicks(t,e,i){let s=t[0],r=t[t.length-1],o=!this.isOrdinal&&this.minPointOffset||0;if(k(this,"trimTicks"),!this.isLinked){if(e&&s!==-1/0)this.min=s;else for(;this.min-o>t[0];)t.shift();if(i)this.max=r;else for(;this.max+o{let{horiz:e,options:i}=t;return[e?i.left:i.top,i.width,i.height,i.pane].join(",")},o=r(this);i[this.coll].forEach(function(i){let{series:n}=i;n.length&&n.some(t=>t.visible)&&i!==e&&r(i)===o&&(t=!0,s.push(i))})}if(t&&n){s.forEach(t=>{let i=t.getThresholdAlignment(e);A(i)&&a.push(i)});let t=a.length>1?a.reduce((t,e)=>t+=e,0)/a.length:void 0;s.forEach(e=>{e.thresholdAlignment=t})}return t}getThresholdAlignment(t){if((!A(this.dataMin)||this!==t&&this.series.some(t=>t.isDirty||t.isDirtyData))&&this.getSeriesExtremes(),A(this.threshold)){let t=m((this.threshold-(this.dataMin||0))/((this.dataMax||0)-(this.dataMin||0)),0,1);return this.options.reversed&&(t=1-t),t}}getTickAmount(){let t=this.options,e=t.tickPixelInterval,i=t.tickAmount;y(t.tickInterval)||i||!(this.leno.push(x(o[o.length-1]+p)),f=()=>o.unshift(x(o[0]-p));if(A(a)&&(u=a<.5?Math.ceil(a*(n-1)):Math.floor(a*(n-1)),r.reversed&&(u=n-1-u)),t.hasData()&&A(s)&&A(i)){let a=()=>{t.transA*=(h-1)/(n-1),t.min=r.startOnTick?o[0]:Math.min(s,o[0]),t.max=r.endOnTick?o[o.length-1]:Math.max(i,o[o.length-1])};if(A(u)&&A(t.threshold)){for(;o[u]!==l||o.length!==n||o[0]>s||o[o.length-1]t.threshold?f():g();if(p>8*t.tickInterval)break;p*=2}a()}else if(h0&&c{i=i||t.isDirtyData||t.isDirty,s=s||t.xAxis&&t.xAxis.isDirty||!1}),this.setAxisSize();let r=this.len!==(this.old&&this.old.len);r||i||s||this.isLinked||this.forceRedraw||this.userMin!==(this.old&&this.old.userMin)||this.userMax!==(this.old&&this.old.userMax)||this.alignToOthers()?(e&&"yAxis"===t&&e.buildStacks(),this.forceRedraw=!1,this.userMinRange||(this.minRange=void 0),this.getSeriesExtremes(),this.setTickInterval(),e&&"xAxis"===t&&e.buildStacks(),this.isDirty||(this.isDirty=r||this.min!==this.old?.min||this.max!==this.old?.max)):e&&e.cleanStacks(),i&&delete this.allExtremes,k(this,"afterSetScale")}setExtremes(t,e,i=!0,s,r){this.series.forEach(t=>{delete t.kdTree}),k(this,"setExtremes",r=C(r,{min:t,max:e}),t=>{this.userMin=t.min,this.userMax=t.max,this.eventArgs=t,i&&this.chart.redraw(s)})}setAxisSize(){let t=this.chart,e=this.options,i=e.offsets||[0,0,0,0],s=this.horiz,r=this.width=Math.round(I(E(e.width,t.plotWidth-i[3]+i[1]),t.plotWidth)),o=this.height=Math.round(I(E(e.height,t.plotHeight-i[0]+i[2]),t.plotHeight)),n=this.top=Math.round(I(E(e.top,t.plotTop+i[0]),t.plotHeight,t.plotTop)),a=this.left=Math.round(I(E(e.left,t.plotLeft+i[3]),t.plotWidth,t.plotLeft));this.bottom=t.chartHeight-o-n,this.right=t.chartWidth-r-a,this.len=Math.max(s?r:o,0),this.pos=s?a:n}getExtremes(){let t=this.logarithmic;return{min:t?x(t.lin2log(this.min)):this.min,max:t?x(t.lin2log(this.max)):this.max,dataMin:this.dataMin,dataMax:this.dataMax,userMin:this.userMin,userMax:this.userMax}}getThreshold(t){let e=this.logarithmic,i=e?e.lin2log(this.min):this.min,s=e?e.lin2log(this.max):this.max;return null===t||t===-1/0?t=i:t===1/0?t=s:i>t?t=i:s15&&e<165?t.align="right":e>195&&e<345&&(t.align="left")}),i.align}tickSize(t){let e=this.options,i=E(e["tick"===t?"tickWidth":"minorTickWidth"],"tick"===t&&this.isXAxis&&!this.categories?1:0),s=e["tick"===t?"tickLength":"minorTickLength"],r;i&&s&&("inside"===e[t+"Position"]&&(s=-s),r=[s,i]);let o={tickSize:r};return k(this,"afterTickSize",o),o.tickSize}labelMetrics(){let t=this.chart.renderer,e=this.ticks,i=e[Object.keys(e)[0]]||{};return this.chart.renderer.fontMetrics(i.label||i.movedLabel||t.box)}unsquish(){let t=this.options.labels,e=t.padding||0,i=this.horiz,s=this.tickInterval,r=this.len/(((this.categories?1:0)+this.max-this.min)/s),o=t.rotation,n=x(.8*this.labelMetrics().h),a=Math.max(this.max-this.min,0),h=function(t){let i=(t+2*e)/(r||1);return(i=i>1?Math.ceil(i):1)*s>a&&t!==1/0&&r!==1/0&&a&&(i=Math.ceil(a/s)),x(i*s)},l=s,d,c=Number.MAX_VALUE,p;if(i){if(!t.staggerLines&&(A(o)?p=[o]:r=-90&&i<=90)&&(e=(t=h(Math.abs(n/Math.sin(u*i))))+Math.abs(i/360))g&&(g=e.label.textPxLength)}),this.maxLabelLength=g,this.autoRotation)g>h&&g>d.h?l.rotation=this.labelRotation:this.labelRotation=0;else if(a&&(p=h,!c))for(u="clip",m=i.length;!n&&m--;)(f=s[i[m]].label)&&("ellipsis"===f.styles.textOverflow?f.css({textOverflow:"clip"}):f.textPxLength>a&&f.css({width:a+"px"}),f.getBBox().height>this.len/i.length-(d.h-d.f)&&(f.specificTextOverflow="ellipsis"));l.rotation&&(p=g>.5*t.chartHeight?.33*t.chartHeight:g,c||(u="ellipsis")),this.labelAlign=r.align||this.autoLabelAlign(this.labelRotation),this.labelAlign&&(l.align=this.labelAlign),i.forEach(function(t){let e=s[t],i=e&&e.label,r=o.width,n={};i&&(i.attr(l),e.shortenLabel?e.shortenLabel():p&&!r&&"nowrap"!==o.whiteSpace&&(pr.g(e).attr({zIndex:n}).addClass(`highcharts-${i.toLowerCase()}${o} `+(this.isRadial?`highcharts-radial-axis${o} `:"")+(s.className||"")).add(t);this.axisGroup||(this.gridGroup=o("grid","-grid",s.gridZIndex),this.axisGroup=o("axis","",s.zIndex),this.labelGroup=o("axis-labels","-labels",s.labels.zIndex))}getOffset(){let t=this,{chart:e,horiz:i,options:s,side:r,ticks:o,tickPositions:n,coll:a}=t,h=e.inverted&&!t.isZAxis?[1,0,3,2][r]:r,l=t.hasData(),d=s.title,c=s.labels,p=A(s.crossing),u=e.axisOffset,g=e.clipOffset,f=[-1,1,1,-1][r],m,x=0,b,v=0,S=0,C,M;if(t.showAxis=m=l||s.showEmpty,t.staggerLines=t.horiz&&c.staggerLines||void 0,t.createGroups(),l||t.isLinked?(n.forEach(function(e){t.generateTick(e)}),t.renderUnsquish(),t.reserveSpaceDefault=0===r||2===r||({1:"left",3:"right"})[r]===t.labelAlign,E(c.reserveSpace,!p&&null,"center"===t.labelAlign||null,t.reserveSpaceDefault)&&n.forEach(function(t){S=Math.max(o[t].getLabelSize(),S)}),t.staggerLines&&(S*=t.staggerLines),t.labelOffset=S*(t.opposite?-1:1)):D(o,function(t,e){t.destroy(),delete o[e]}),d?.text&&!1!==d.enabled&&(t.addTitle(m),m&&!p&&!1!==d.reserveSpace&&(t.titleOffset=x=t.axisTitle.getBBox()[i?"height":"width"],v=y(b=d.offset)?0:E(d.margin,i?5:10))),t.renderLine(),t.offset=f*E(s.offset,u[r]?u[r]+(s.margin||0):0),t.tickRotCorr=t.tickRotCorr||{x:0,y:0},M=0===r?-t.labelMetrics().h:2===r?t.tickRotCorr.y:0,C=Math.abs(S)+v,S&&(C-=M,C+=f*(i?E(c.y,t.tickRotCorr.y+f*c.distance):E(c.x,f*c.distance))),t.axisTitleMargin=E(b,C),t.getMaxLabelDimensions&&(t.maxLabelDimensions=t.getMaxLabelDimensions(o,n)),"colorAxis"!==a&&g){let e=this.tickSize("tick");u[r]=Math.max(u[r],(t.axisTitleMargin||0)+x+f*t.offset,C,n&&n.length&&e?e[0]+f*t.offset:0);let i=!t.axisLine||s.offset?0:t.axisLine.strokeWidth()/2;g[h]=Math.max(g[h],i)}k(this,"afterGetOffset")}getLinePath(t){let e=this.chart,i=this.opposite,s=this.offset,r=this.horiz,o=this.left+(i?this.width:0)+s,n=e.chartHeight-this.bottom-(i?this.height:0)+s;return i&&(t*=-1),e.renderer.crispLine([["M",r?this.left:o,r?n:this.top],["L",r?e.chartWidth-this.right:o,r?n:e.chartHeight-this.bottom]],t)}renderLine(){this.axisLine||(this.axisLine=this.chart.renderer.path().addClass("highcharts-axis-line").add(this.axisGroup),this.chart.styledMode||this.axisLine.attr({stroke:this.options.lineColor,"stroke-width":this.options.lineWidth,zIndex:7}))}getTitlePosition(t){let e=this.horiz,i=this.left,s=this.top,r=this.len,o=this.options.title,n=e?i:s,a=this.opposite,h=this.offset,l=o.x,d=o.y,c=this.chart.renderer.fontMetrics(t),p=t?Math.max(t.getBBox(!1,0).height-c.h-1,0):0,u={low:n+(e?0:r),middle:n+r/2,high:n+(e?r:0)}[o.align],g=(e?s+this.height:i)+(e?1:-1)*(a?-1:1)*(this.axisTitleMargin||0)+[-p,p,c.f,-p][this.side],f={x:e?u+l:g+(a?this.width:0)+h+l,y:e?g+d-(a?this.height:0)+h:u+d};return k(this,"afterGetTitlePosition",{titlePosition:f}),f}renderMinorTick(t,e){let i=this.minorTicks;i[t]||(i[t]=new n(this,t,"minor")),e&&i[t].isNew&&i[t].render(null,!0),i[t].render(null,!1,1)}renderTick(t,e,i){let s=this.isLinked,r=this.ticks;(!s||t>=this.min&&t<=this.max||this.grid&&this.grid.isColumn)&&(r[t]||(r[t]=new n(this,t)),i&&r[t].isNew&&r[t].render(e,!0,-1),r[t].render(e))}render(){let t,e;let i=this,s=i.chart,r=i.logarithmic,a=s.renderer,l=i.options,d=i.isLinked,c=i.tickPositions,p=i.axisTitle,u=i.ticks,g=i.minorTicks,f=i.alternateBands,m=l.stackLabels,x=l.alternateGridColor,y=l.crossing,b=i.tickmarkOffset,v=i.axisLine,S=i.showAxis,C=h(a.globalAnimation);if(i.labelEdge.length=0,i.overlap=!1,[u,g,f].forEach(function(t){D(t,function(t){t.isActive=!1})}),A(y)){let t=this.isXAxis?s.yAxis[0]:s.xAxis[0],e=[1,-1,-1,1][this.side];if(t){let s=t.toPixels(y,!0);i.horiz&&(s=t.len-s),i.offset=e*s}}if(i.hasData()||d){let a=i.chart.hasRendered&&i.old&&A(i.old.min);i.minorTickInterval&&!i.categories&&i.getMinorTickPositions().forEach(function(t){i.renderMinorTick(t,a)}),c.length&&(c.forEach(function(t,e){i.renderTick(t,e,a)}),b&&(0===i.min||i.single)&&(u[-1]||(u[-1]=new n(i,-1,null,!0)),u[-1].render(-1))),x&&c.forEach(function(n,a){e=void 0!==c[a+1]?c[a+1]+b:i.max-b,a%2==0&&n=.5)t=Math.round(t),l=n.getLinearTickPositions(t,e,i);else if(t>=.08){let s,r,n,a,h,d,c;let p=Math.floor(e);for(s=t>.3?[1,2,4]:t>.15?[1,2,4,6,8]:[1,2,3,4,5,6,7,8,9],r=p;re&&(!o||d<=i)&&void 0!==d&&l.push(d),d>i&&(c=!0),d=h}else{let d=this.lin2log(e),c=this.lin2log(i),p=o?n.getMinorTickInterval():h.tickInterval,u=h.tickPixelInterval/(o?5:1),g=o?a/n.tickPositions.length:a;t=s(t=r("auto"===p?null:p,this.minorAutoInterval,(c-d)*u/(g||1))),l=n.getLinearTickPositions(t,d,c).map(this.log2lin),o||(this.minorAutoInterval=t/5)}return o||(n.tickInterval=t),l}lin2log(t){return Math.pow(10,t)}log2lin(t){return Math.log(t)/Math.LN10}}t.Additions=n}(e||(e={})),e}),i(e,"Core/Axis/PlotLineOrBand/PlotLineOrBandAxis.js",[e["Core/Utilities.js"]],function(t){var e;let{erase:i,extend:s,isNumber:r}=t;return function(t){let e;function o(t){return this.addPlotBandOrLine(t,"plotBands")}function n(t,i){let s=this.userOptions,r=new e(this,t);if(this.visible&&(r=r.render()),r){if(this._addedPlotLB||(this._addedPlotLB=!0,(s.plotLines||[]).concat(s.plotBands||[]).forEach(t=>{this.addPlotBandOrLine(t)})),i){let e=s[i]||[];e.push(t),s[i]=e}this.plotLinesAndBands.push(r)}return r}function a(t){return this.addPlotBandOrLine(t,"plotLines")}function h(t,e,i){i=i||this.options;let s=this.getPlotLinePath({value:e,force:!0,acrossPanes:i.acrossPanes}),o=[],n=this.horiz,a=!r(this.min)||!r(this.max)||tthis.max&&e>this.max,h=this.getPlotLinePath({value:t,force:!0,acrossPanes:i.acrossPanes}),l,d=1,c;if(h&&s)for(a&&(c=h.toString()===s.toString(),d=0),l=0;l{let t=[];for(let e of this.axes)for(let{label:i,options:s}of e.plotLinesAndBands)i&&!s?.label?.allowOverlap&&t.push(i);return t})}),t.compose(p,s)}constructor(t,e){this.axis=t,this.options=e,this.id=e.id}render(){h(this,"render");let{axis:t,options:e}=this,{horiz:i,logarithmic:s}=t,{color:r,events:n,zIndex:a=0}=e,p={},u=t.chart.renderer,g=e.to,f=e.from,m=e.value,x=e.borderWidth,y=e.label,{label:b,svgElem:v}=this,S=[],C,k=o(f)&&o(g),M=o(m),w=!v,T={class:"highcharts-plot-"+(k?"band ":"line ")+(e.className||"")},A=k?"bands":"lines";if(!t.chart.styledMode&&(M?(T.stroke=r||"#999999",T["stroke-width"]=c(e.width,1),e.dashStyle&&(T.dashstyle=e.dashStyle)):k&&(T.fill=r||"#e6e9ff",x&&(T.stroke=e.borderColor,T["stroke-width"]=x))),p.zIndex=a,A+="-"+a,(C=t.plotLinesAndBandsGroups[A])||(t.plotLinesAndBandsGroups[A]=C=u.g("plot-"+A).attr(p).add()),v||(this.svgElem=v=u.path().attr(T).add(C)),o(m))S=t.getPlotLinePath({value:s?.log2lin(m)??m,lineWidth:v.strokeWidth(),acrossPanes:e.acrossPanes});else{if(!(o(f)&&o(g)))return;S=t.getPlotBandPath(s?.log2lin(f)??f,s?.log2lin(g)??g,e)}return!this.eventsAdded&&n&&(d(n,(t,e)=>{v?.on(e,t=>{n[e].apply(this,[t])})}),this.eventsAdded=!0),(w||!v.d)&&S?.length?v.attr({d:S}):v&&(S?(v.show(),v.animate({d:S})):v.d&&(v.hide(),b&&(this.label=b=b.destroy()))),y&&(o(y.text)||o(y.formatter))&&S?.length&&t.width>0&&t.height>0&&!S.isFlat?(y=l({align:i&&k?"center":void 0,x:i?!k&&4:10,verticalAlign:!i&&k?"middle":void 0,y:i?k?16:10:k?6:-4,rotation:i&&!k?90:0,...k?{inside:!0}:{}},y),this.renderLabel(y,S,k,a)):b&&b.hide(),this}renderLabel(t,e,i,n){let a=this.axis,h=a.chart.renderer,d=t.inside,c=this.label;c||(this.label=c=h.text(this.getLabelText(t),0,0,t.useHTML).attr({align:t.textAlign||t.align,rotation:t.rotation,class:"highcharts-plot-"+(i?"band":"line")+"-label "+(t.className||""),zIndex:n}),a.chart.styledMode||c.css(l({fontSize:"0.8em",textOverflow:i&&!d?"":"ellipsis"},t.style)),c.add());let p=e.xBounds||[e[0][1],e[1][1],i?e[2][1]:e[0][1]],u=e.yBounds||[e[0][2],e[1][2],i?e[2][2]:e[0][2]],g=r(p),f=r(u),m=s(p)-g;c.align(t,!1,{x:g,y:f,width:m,height:s(u)-f}),(!c.alignValue||"left"===c.alignValue||o(d))&&c.css({width:(t.style?.width||(i&&d?m:90===c.rotation?a.height-(c.alignAttr.y-a.top):(t.clip?a.width:a.chart.chartWidth)-(c.alignAttr.x-a.left)))+"px"}),c.show(!0)}getLabelText(t){return o(t.formatter)?t.formatter.call(this):t.text}destroy(){a(this.axis.plotLinesAndBands,this),delete this.axis,n(this)}}return p}),i(e,"Core/Tooltip.js",[e["Core/Animation/AnimationUtilities.js"],e["Core/Templating.js"],e["Core/Globals.js"],e["Core/Renderer/RendererUtilities.js"],e["Core/Renderer/RendererRegistry.js"],e["Core/Utilities.js"]],function(t,e,i,s,r,o){var n;let{animObject:a}=t,{format:h}=e,{composed:l,doc:d,isSafari:c}=i,{distribute:p}=s,{addEvent:u,clamp:g,css:f,discardElement:m,extend:x,fireEvent:y,isArray:b,isNumber:v,isString:S,merge:C,pick:k,pushUnique:M,splat:w,syncTimeout:T}=o;class A{constructor(t,e,i){this.allowShared=!0,this.crosshairs=[],this.distance=0,this.isHidden=!0,this.isSticky=!1,this.options={},this.outside=!1,this.chart=t,this.init(t,e),this.pointer=i}bodyFormatter(t){return t.map(function(t){let e=t.series.tooltipOptions;return(e[(t.point.formatPrefix||"point")+"Formatter"]||t.point.tooltipFormatter).call(t.point,e[(t.point.formatPrefix||"point")+"Format"]||"")})}cleanSplit(t){this.chart.series.forEach(function(e){let i=e&&e.tt;i&&(!i.isActive||t?e.tt=i.destroy():i.isActive=!1)})}defaultFormatter(t){let e;let i=this.points||w(this);return(e=(e=[t.tooltipFooterHeaderFormatter(i[0])]).concat(t.bodyFormatter(i))).push(t.tooltipFooterHeaderFormatter(i[0],!0)),e}destroy(){this.label&&(this.label=this.label.destroy()),this.split&&(this.cleanSplit(!0),this.tt&&(this.tt=this.tt.destroy())),this.renderer&&(this.renderer=this.renderer.destroy(),m(this.container)),o.clearTimeout(this.hideTimer)}getAnchor(t,e){let i;let{chart:s,pointer:r}=this,o=s.inverted,n=s.plotTop,a=s.plotLeft;if((t=w(t))[0].series&&t[0].series.yAxis&&!t[0].series.yAxis.options.reversedStacks&&(t=t.slice().reverse()),this.followPointer&&e)void 0===e.chartX&&(e=r.normalize(e)),i=[e.chartX-a,e.chartY-n];else if(t[0].tooltipPos)i=t[0].tooltipPos;else{let s=0,r=0;t.forEach(function(t){let e=t.pos(!0);e&&(s+=e[0],r+=e[1])}),s/=t.length,r/=t.length,this.shared&&t.length>1&&e&&(o?s=e.chartX:r=e.chartY),i=[s-a,r-n]}return i.map(Math.round)}getClassName(t,e,i){let s=this.options,r=t.series,o=r.options;return[s.className,"highcharts-label",i&&"highcharts-tooltip-header",e?"highcharts-tooltip-box":"highcharts-tooltip",!i&&"highcharts-color-"+k(t.colorIndex,r.colorIndex),o&&o.className].filter(S).join(" ")}getLabel({anchorX:t,anchorY:e}={anchorX:0,anchorY:0}){let s=this,o=this.chart.styledMode,n=this.options,a=this.split&&this.allowShared,h=this.container,l=this.chart.renderer;if(this.label){let t=!this.label.hasClass("highcharts-label");(!a&&t||a&&!t)&&this.destroy()}if(!this.label){if(this.outside){let t=this.chart.options.chart.style,e=r.getRendererType();this.container=h=i.doc.createElement("div"),h.className="highcharts-tooltip-container",f(h,{position:"absolute",top:"1px",pointerEvents:"none",zIndex:Math.max(this.options.style.zIndex||0,(t&&t.zIndex||0)+3)}),this.renderer=l=new e(h,0,0,t,void 0,void 0,l.styledMode)}if(a?this.label=l.g("tooltip"):(this.label=l.label("",t,e,n.shape,void 0,void 0,n.useHTML,void 0,"tooltip").attr({padding:n.padding,r:n.borderRadius}),o||this.label.attr({fill:n.backgroundColor,"stroke-width":n.borderWidth||0}).css(n.style).css({pointerEvents:n.style.pointerEvents||(this.shouldStickOnContact()?"auto":"none")})),s.outside){let t=this.label;[t.xSetter,t.ySetter].forEach((e,i)=>{t[i?"ySetter":"xSetter"]=r=>{e.call(t,s.distance),t[i?"y":"x"]=r,h&&(h.style[i?"top":"left"]=`${r}px`)}})}this.label.attr({zIndex:8}).shadow(n.shadow).add()}return h&&!h.parentElement&&i.doc.body.appendChild(h),this.label}getPlayingField(){let{body:t,documentElement:e}=d,{chart:i,distance:s,outside:r}=this;return{width:r?Math.max(t.scrollWidth,e.scrollWidth,t.offsetWidth,e.offsetWidth,e.clientWidth)-2*s:i.chartWidth,height:r?Math.max(t.scrollHeight,e.scrollHeight,t.offsetHeight,e.offsetHeight,e.clientHeight):i.chartHeight}}getPosition(t,e,i){let{distance:s,chart:r,outside:o,pointer:n}=this,{inverted:a,plotLeft:h,plotTop:l,polar:d}=r,{plotX:c=0,plotY:p=0}=i,u={},g=a&&i.h||0,{height:f,width:m}=this.getPlayingField(),x=n.getChartPosition(),y=t=>t*x.scaleX,b=t=>t*x.scaleY,v=i=>{let n="x"===i;return[i,n?m:f,n?t:e].concat(o?[n?y(t):b(e),n?x.left-s+y(c+h):x.top-s+b(p+l),0,n?m:f]:[n?t:e,n?c+h:p+l,n?h:l,n?h+r.plotWidth:l+r.plotHeight])},S=v("y"),C=v("x"),M,w=!!i.negative;!d&&r.hoverSeries?.yAxis?.reversed&&(w=!w);let T=!this.followPointer&&k(i.ttBelow,!d&&!a===w),A=function(t,e,i,r,n,a,h){let l=o?"y"===t?b(s):y(s):s,d=(i-r)/2,c=re?m:m+g)}},P=function(t,e,i,r,o){if(oe-s)return!1;oe-r/2?u[t]=e-r-2:u[t]=o-i/2},L=function(t){[S,C]=[C,S],M=t},O=()=>{!1!==A.apply(0,S)?!1!==P.apply(0,C)||M||(L(!0),O()):M?u.x=u.y=0:(L(!0),O())};return(a&&!d||this.len>1)&&L(),O(),u}hide(t){let e=this;o.clearTimeout(this.hideTimer),t=k(t,this.options.hideDelay),this.isHidden||(this.hideTimer=T(function(){let i=e.getLabel();e.getLabel().animate({opacity:0},{duration:t?150:t,complete:()=>{i.hide(),e.container&&e.container.remove()}}),e.isHidden=!0},t))}init(t,e){this.chart=t,this.options=e,this.crosshairs=[],this.isHidden=!0,this.split=e.split&&!t.inverted&&!t.polar,this.shared=e.shared||this.split,this.outside=k(e.outside,!!(t.scrollablePixelsX||t.scrollablePixelsY))}shouldStickOnContact(t){return!!(!this.followPointer&&this.options.stickOnContact&&(!t||this.pointer.inClass(t.target,"highcharts-tooltip")))}move(t,e,i,s){let r=this,o=a(!r.isHidden&&r.options.animation),n=r.followPointer||(r.len||0)>1,h={x:t,y:e};n||(h.anchorX=i,h.anchorY=s),o.step=()=>r.drawTracker(),r.getLabel().animate(h,o)}refresh(t,e){let{chart:i,options:s,pointer:r,shared:n}=this,a=w(t),l=a[0],d=[],c=s.format,p=s.formatter||this.defaultFormatter,u=i.styledMode,f={},m=this.allowShared;if(!s.enabled||!l.series)return;o.clearTimeout(this.hideTimer),this.allowShared=!(!b(t)&&t.series&&t.series.noSharedTooltip),m=m&&!this.allowShared,this.followPointer=!this.split&&l.series.tooltipOptions.followPointer;let x=this.getAnchor(t,e),v=x[0],C=x[1];n&&this.allowShared?(r.applyInactiveState(a),a.forEach(function(t){t.setState("hover"),d.push(t.getLabelConfig())}),(f=l.getLabelConfig()).points=d):f=l.getLabelConfig(),this.len=d.length;let M=S(c)?h(c,f,i):p.call(f,this),T=l.series;if(this.distance=k(T.tooltipOptions.distance,16),!1===M)this.hide();else{if(this.split&&this.allowShared)this.renderSplit(M,a);else{let t=v,o=C;if(e&&r.isDirectTouch&&(t=e.chartX-i.plotLeft,o=e.chartY-i.plotTop),i.polar||!1===T.options.clip||a.some(e=>r.isDirectTouch||e.series.shouldShowTooltip(t,o))){let t=this.getLabel(m&&this.tt||{});(!s.style.width||u)&&t.css({width:(this.outside?this.getPlayingField():i.spacingBox).width+"px"}),t.attr({class:this.getClassName(l),text:M&&M.join?M.join(""):M}),this.outside&&t.attr({x:g(t.x||0,0,this.getPlayingField().width-(t.width||0))}),u||t.attr({stroke:s.borderColor||l.color||T.color||"#666666"}),this.updatePosition({plotX:v,plotY:C,negative:l.negative,ttBelow:l.ttBelow,h:x[2]||0})}else{this.hide();return}}this.isHidden&&this.label&&this.label.attr({opacity:1}).show(),this.isHidden=!1}y(this,"refresh")}renderSplit(t,e){let i=this,{chart:s,chart:{chartWidth:r,chartHeight:o,plotHeight:n,plotLeft:a,plotTop:h,scrollablePixelsY:l=0,scrollablePixelsX:u,styledMode:f},distance:m,options:y,options:{positioner:b},pointer:v}=i,{scrollLeft:C=0,scrollTop:M=0}=s.scrollablePlotArea?.scrollingContainer||{},w=i.outside&&"number"!=typeof u?d.documentElement.getBoundingClientRect():{left:C,right:C+r,top:M,bottom:M+o},T=i.getLabel(),A=this.renderer||s.renderer,P=!!(s.xAxis[0]&&s.xAxis[0].opposite),{left:L,top:O}=v.getChartPosition(),D=h+M,E=0,I=n-l;function j(t,e,s,r,o=!0){let n,a;return s?(n=P?0:I,a=g(t-r/2,w.left,w.right-r-(i.outside?L:0))):(n=e-D,a=g(a=o?t-r-m:t+m,o?a:w.left,w.right)),{x:a,y:n}}S(t)&&(t=[!1,t]);let B=t.slice(0,e.length+1).reduce(function(t,s,r){if(!1!==s&&""!==s){let o=e[r-1]||{isHeader:!0,plotX:e[0].plotX,plotY:n,series:{}},l=o.isHeader,d=l?i:o.series,c=d.tt=function(t,e,s){let r=t,{isHeader:o,series:n}=e;if(!r){let t={padding:y.padding,r:y.borderRadius};f||(t.fill=y.backgroundColor,t["stroke-width"]=y.borderWidth??1),r=A.label("",0,0,y[o?"headerShape":"shape"],void 0,void 0,y.useHTML).addClass(i.getClassName(e,!0,o)).attr(t).add(T)}return r.isActive=!0,r.attr({text:s}),f||r.css(y.style).attr({stroke:y.borderColor||e.color||n.color||"#333333"}),r}(d.tt,o,s.toString()),p=c.getBBox(),u=p.width+c.strokeWidth();l&&(E=p.height,I+=E,P&&(D-=E));let{anchorX:x,anchorY:v}=function(t){let e,i;let{isHeader:s,plotX:r=0,plotY:o=0,series:l}=t;if(s)e=Math.max(a+r,a),i=h+n/2;else{let{xAxis:t,yAxis:s}=l;e=t.pos+g(r,-m,t.len+m),l.shouldShowTooltip(0,s.pos-h+o,{ignoreX:!0})&&(i=s.pos+o)}return{anchorX:e=g(e,w.left-m,w.right+m),anchorY:i}}(o);if("number"==typeof v){let e=p.height+1,s=b?b.call(i,u,e,o):j(x,v,l,u);t.push({align:b?0:void 0,anchorX:x,anchorY:v,boxWidth:u,point:o,rank:k(s.rank,l?1:0),size:e,target:s.y,tt:c,x:s.x})}else c.isActive=!1}return t},[]);!b&&B.some(t=>{let{outside:e}=i,s=(e?L:0)+t.anchorX;return ss})&&(B=B.map(t=>{let{x:e,y:i}=j(t.anchorX,t.anchorY,t.point.isHeader,t.boxWidth,!1);return x(t,{target:i,x:e})})),i.cleanSplit(),p(B,I);let R={left:L,right:L};B.forEach(function(t){let{x:e,boxWidth:s,isHeader:r}=t;!r&&(i.outside&&L+eR.right&&(R.right=L+e))}),B.forEach(function(t){let{x:e,anchorX:s,anchorY:r,pos:o,point:{isHeader:n}}=t,a={visibility:void 0===o?"hidden":"inherit",x:e,y:(o||0)+D,anchorX:s,anchorY:r};if(i.outside&&e0&&(n||(a.x=e+t,a.anchorX=s+t),n&&(a.x=(R.right-R.left)/2,a.anchorX=s+t))}t.tt.attr(a)});let{container:z,outside:N,renderer:W}=i;if(N&&z&&W){let{width:t,height:e,x:i,y:s}=T.getBBox();W.setSize(t+i,e+s,!1),z.style.left=R.left+"px",z.style.top=O+"px"}c&&T.attr({opacity:1===T.opacity?.999:1})}drawTracker(){if(!this.shouldStickOnContact()){this.tracker&&(this.tracker=this.tracker.destroy());return}let t=this.chart,e=this.label,i=this.shared?t.hoverPoints:t.hoverPoint;if(!e||!i)return;let s={x:0,y:0,width:0,height:0},r=this.getAnchor(i),o=e.getBBox();r[0]+=t.plotLeft-(e.translateX||0),r[1]+=t.plotTop-(e.translateY||0),s.x=Math.min(0,r[0]),s.y=Math.min(0,r[1]),s.width=r[0]<0?Math.max(Math.abs(r[0]),o.width-r[0]):Math.max(Math.abs(r[0]),o.width),s.height=r[1]<0?Math.max(Math.abs(r[1]),o.height-Math.abs(r[1])):Math.max(Math.abs(r[1]),o.height),this.tracker?this.tracker.attr(s):(this.tracker=e.renderer.rect(s).addClass("highcharts-tracker").add(e),t.styledMode||this.tracker.attr({fill:"rgba(0,0,0,0)"}))}styledModeFormat(t){return t.replace('style="font-size: 0.8em"','class="highcharts-header"').replace(/style="color:{(point|series)\.color}"/g,'class="highcharts-color-{$1.colorIndex} {series.options.className} {point.options.className}"')}tooltipFooterHeaderFormatter(t,e){let i=t.series,s=i.tooltipOptions,r=i.xAxis,o=r&&r.dateTime,n={isFooter:e,labelConfig:t},a=s.xDateFormat,l=s[e?"footerFormat":"headerFormat"];return y(this,"headerFormatter",n,function(e){o&&!a&&v(t.key)&&(a=o.getXDateFormat(t.key,s.dateTimeLabelFormats)),o&&a&&(t.point&&t.point.tooltipDateKeys||["key"]).forEach(function(t){l=l.replace("{point."+t+"}","{point."+t+":"+a+"}")}),i.chart.styledMode&&(l=this.styledModeFormat(l)),e.text=h(l,{point:t,series:i},this.chart)}),n.text}update(t){this.destroy(),this.init(this.chart,C(!0,this.options,t))}updatePosition(t){let{chart:e,container:i,distance:s,options:r,pointer:o,renderer:n}=this,{height:a=0,width:h=0}=this.getLabel(),{left:l,top:d,scaleX:c,scaleY:p}=o.getChartPosition(),u=(r.positioner||this.getPosition).call(this,h,a,t),g=(t.plotX||0)+e.plotLeft,m=(t.plotY||0)+e.plotTop,x;n&&i&&(r.positioner&&(u.x+=l-s,u.y+=d-s),x=(r.borderWidth||0)+2*s+2,n.setSize(h+x,a+x,!1),(1!==c||1!==p)&&(f(i,{transform:`scale(${c}, ${p})`}),g*=c,m*=p),g+=l-u.x,m+=d-u.y),this.move(Math.round(u.x),Math.round(u.y||0),g,m)}}return(n=A||(A={})).compose=function(t){M(l,"Core.Tooltip")&&u(t,"afterInit",function(){let t=this.chart;t.options.tooltip&&(t.tooltip=new n(t,t.options.tooltip,this))})},A}),i(e,"Core/Series/Point.js",[e["Core/Renderer/HTML/AST.js"],e["Core/Animation/AnimationUtilities.js"],e["Core/Defaults.js"],e["Core/Templating.js"],e["Core/Utilities.js"]],function(t,e,i,s,r){let{animObject:o}=e,{defaultOptions:n}=i,{format:a}=s,{addEvent:h,crisp:l,erase:d,extend:c,fireEvent:p,getNestedProperty:u,isArray:g,isFunction:f,isNumber:m,isObject:x,merge:y,pick:b,syncTimeout:v,removeEvent:S,uniqueKey:C}=r;class k{animateBeforeDestroy(){let t=this,e={x:t.startXPos,opacity:0},i=t.getGraphicalProps();i.singular.forEach(function(i){t[i]=t[i].animate("dataLabel"===i?{x:t[i].startXPos,y:t[i].startYPos,opacity:0}:e)}),i.plural.forEach(function(e){t[e].forEach(function(e){e.element&&e.animate(c({x:t.startXPos},e.startYPos?{x:e.startXPos,y:e.startYPos}:{}))})})}applyOptions(t,e){let i=this.series,s=i.options.pointValKey||i.pointValKey;return c(this,t=k.prototype.optionsToObject.call(this,t)),this.options=this.options?c(this.options,t):t,t.group&&delete this.group,t.dataLabels&&delete this.dataLabels,s&&(this.y=k.prototype.getNestedProperty.call(this,s)),this.selected&&(this.state="select"),"name"in this&&void 0===e&&i.xAxis&&i.xAxis.hasNames&&(this.x=i.xAxis.nameToX(this)),void 0===this.x&&i?this.x=e??i.autoIncrement():m(t.x)&&i.options.relativeXValue&&(this.x=i.autoIncrement(t.x)),this.isNull=this.isValid&&!this.isValid(),this.formatPrefix=this.isNull?"null":"point",this}destroy(){if(!this.destroyed){let t=this,e=t.series,i=e.chart,s=e.options.dataSorting,r=i.hoverPoints,n=o(t.series.chart.renderer.globalAnimation),a=()=>{for(let e in(t.graphic||t.graphics||t.dataLabel||t.dataLabels)&&(S(t),t.destroyElements()),t)delete t[e]};t.legendItem&&i.legend.destroyItem(t),r&&(t.setState(),d(r,t),r.length||(i.hoverPoints=null)),t===i.hoverPoint&&t.onMouseOut(),s&&s.enabled?(this.animateBeforeDestroy(),v(a,n.duration)):a(),i.pointCount--}this.destroyed=!0}destroyElements(t){let e=this,i=e.getGraphicalProps(t);i.singular.forEach(function(t){e[t]=e[t].destroy()}),i.plural.forEach(function(t){e[t].forEach(function(t){t&&t.element&&t.destroy()}),delete e[t]})}firePointEvent(t,e,i){let s=this,r=this.series.options;s.manageEvent(t),"click"===t&&r.allowPointSelect&&(i=function(t){!s.destroyed&&s.select&&s.select(null,t.ctrlKey||t.metaKey||t.shiftKey)}),p(s,t,e,i)}getClassName(){return"highcharts-point"+(this.selected?" highcharts-point-select":"")+(this.negative?" highcharts-negative":"")+(this.isNull?" highcharts-null-point":"")+(void 0!==this.colorIndex?" highcharts-color-"+this.colorIndex:"")+(this.options.className?" "+this.options.className:"")+(this.zone&&this.zone.className?" "+this.zone.className.replace("highcharts-negative",""):"")}getGraphicalProps(t){let e,i;let s=this,r=[],o={singular:[],plural:[]};for((t=t||{graphic:1,dataLabel:1}).graphic&&r.push("graphic","connector"),t.dataLabel&&r.push("dataLabel","dataLabelPath","dataLabelUpper"),i=r.length;i--;)s[e=r[i]]&&o.singular.push(e);return["graphic","dataLabel"].forEach(function(e){let i=e+"s";t[e]&&s[i]&&o.plural.push(i)}),o}getLabelConfig(){return{x:this.category,y:this.y,color:this.color,colorIndex:this.colorIndex,key:this.name||this.category,series:this.series,point:this,percentage:this.percentage,total:this.total||this.stackTotal}}getNestedProperty(t){return t?0===t.indexOf("custom.")?u(t,this.options):this[t]:void 0}getZone(){let t=this.series,e=t.zones,i=t.zoneAxis||"y",s,r=0;for(s=e[0];this[i]>=s.value;)s=e[++r];return this.nonZonedColor||(this.nonZonedColor=this.color),s&&s.color&&!this.options.color?this.color=s.color:this.color=this.nonZonedColor,s}hasNewShapeType(){return(this.graphic&&(this.graphic.symbolName||this.graphic.element.nodeName))!==this.shapeType}constructor(t,e,i){this.formatPrefix="point",this.visible=!0,this.series=t,this.applyOptions(e,i),this.id??(this.id=C()),this.resolveColor(),t.chart.pointCount++,p(this,"afterInit")}isValid(){return(m(this.x)||this.x instanceof Date)&&m(this.y)}optionsToObject(t){let e=this.series,i=e.options.keys,s=i||e.pointArrayMap||["y"],r=s.length,o={},n,a=0,h=0;if(m(t)||null===t)o[s[0]]=t;else if(g(t))for(!i&&t.length>r&&("string"==(n=typeof t[0])?o.name=t[0]:"number"===n&&(o.x=t[0]),a++);h0?k.prototype.setNestedProperty(o,t[a],s[h]):o[s[h]]=t[a]),a++,h++;else"object"==typeof t&&(o=t,t.dataLabels&&(e.hasDataLabels=()=>!0),t.marker&&(e._hasPointMarkers=!0));return o}pos(t,e=this.plotY){if(!this.destroyed){let{plotX:i,series:s}=this,{chart:r,xAxis:o,yAxis:n}=s,a=0,h=0;if(m(i)&&m(e))return t&&(a=o?o.pos:r.plotLeft,h=n?n.pos:r.plotTop),r.inverted&&o&&n?[n.len-e+h,o.len-i+a]:[i+a,e+h]}}resolveColor(){let t=this.series,e=t.chart.options.chart,i=t.chart.styledMode,s,r,o=e.colorCount,n;delete this.nonZonedColor,t.options.colorByPoint?(i||(s=(r=t.options.colors||t.chart.options.colors)[t.colorCounter],o=r.length),n=t.colorCounter,t.colorCounter++,t.colorCounter===o&&(t.colorCounter=0)):(i||(s=t.color),n=t.colorIndex),this.colorIndex=b(this.options.colorIndex,n),this.color=b(this.options.color,s)}setNestedProperty(t,e,i){return i.split(".").reduce(function(t,i,s,r){let o=r.length-1===s;return t[i]=o?e:x(t[i],!0)?t[i]:{},t[i]},t),t}shouldDraw(){return!this.isNull}tooltipFormatter(t){let e=this.series,i=e.tooltipOptions,s=b(i.valueDecimals,""),r=i.valuePrefix||"",o=i.valueSuffix||"";return e.chart.styledMode&&(t=e.chart.tooltip.styledModeFormat(t)),(e.pointArrayMap||["y"]).forEach(function(e){e="{point."+e,(r||o)&&(t=t.replace(RegExp(e+"}","g"),r+e+"}"+o)),t=t.replace(RegExp(e+"}","g"),e+":,."+s+"f}")}),a(t,{point:this,series:this.series},e.chart)}update(t,e,i,s){let r;let o=this,n=o.series,a=o.graphic,h=n.chart,l=n.options;function d(){o.applyOptions(t);let s=a&&o.hasMockGraphic,d=null===o.y?!s:s;a&&d&&(o.graphic=a.destroy(),delete o.hasMockGraphic),x(t,!0)&&(a&&a.element&&t&&t.marker&&void 0!==t.marker.symbol&&(o.graphic=a.destroy()),t?.dataLabels&&o.dataLabel&&(o.dataLabel=o.dataLabel.destroy())),r=o.index,n.updateParallelArrays(o,r),l.data[r]=x(l.data[r],!0)||x(t,!0)?o.options:b(t,l.data[r]),n.isDirty=n.isDirtyData=!0,!n.fixedBox&&n.hasCartesianSeries&&(h.isDirtyBox=!0),"point"===l.legendType&&(h.isDirtyLegend=!0),e&&h.redraw(i)}e=b(e,!0),!1===s?d():o.firePointEvent("update",{options:t},d)}remove(t,e){this.series.removePoint(this.series.data.indexOf(this),t,e)}select(t,e){let i=this,s=i.series,r=s.chart;t=b(t,!i.selected),this.selectedStaging=t,i.firePointEvent(t?"select":"unselect",{accumulate:e},function(){i.selected=i.options.selected=t,s.options.data[s.data.indexOf(i)]=i.options,i.setState(t&&"select"),e||r.getSelectedPoints().forEach(function(t){let e=t.series;t.selected&&t!==i&&(t.selected=t.options.selected=!1,e.options.data[e.data.indexOf(t)]=t.options,t.setState(r.hoverPoints&&e.options.inactiveOtherPoints?"inactive":""),t.firePointEvent("unselect"))})}),delete this.selectedStaging}onMouseOver(t){let{inverted:e,pointer:i}=this.series.chart;i&&(t=t?i.normalize(t):i.getChartCoordinatesFromPoint(this,e),i.runPointActions(t,this))}onMouseOut(){let t=this.series.chart;this.firePointEvent("mouseOut"),this.series.options.inactiveOtherPoints||(t.hoverPoints||[]).forEach(function(t){t.setState()}),t.hoverPoints=t.hoverPoint=null}manageEvent(t){let e=y(this.series.options.point,this.options),i=e.events?.[t];f(i)&&(!this.hcEvents?.[t]||this.hcEvents?.[t]?.map(t=>t.fn).indexOf(i)===-1)?(this.importedUserEvent?.(),this.importedUserEvent=h(this,t,i)):this.importedUserEvent&&!i&&this.hcEvents?.[t]&&(S(this,t),delete this.hcEvents[t],Object.keys(this.hcEvents)||delete this.importedUserEvent)}setState(e,i){let s=this.series,r=this.state,o=s.options.states[e||"normal"]||{},a=n.plotOptions[s.type].marker&&s.options.marker,h=a&&!1===a.enabled,l=a&&a.states&&a.states[e||"normal"]||{},d=!1===l.enabled,u=this.marker||{},g=s.chart,f=a&&s.markerAttribs,x=s.halo,y,v,S,C=s.stateMarkerGraphic,k;if((e=e||"")===this.state&&!i||this.selected&&"select"!==e||!1===o.enabled||e&&(d||h&&!1===l.enabled)||e&&u.states&&u.states[e]&&!1===u.states[e].enabled)return;if(this.state=e,f&&(y=s.markerAttribs(this,e)),this.graphic&&!this.hasMockGraphic){if(r&&this.graphic.removeClass("highcharts-point-"+r),e&&this.graphic.addClass("highcharts-point-"+e),!g.styledMode){v=s.pointAttribs(this,e),S=b(g.options.chart.animation,o.animation);let t=v.opacity;s.options.inactiveOtherPoints&&m(t)&&(this.dataLabels||[]).forEach(function(e){e&&!e.hasClass("highcharts-data-label-hidden")&&(e.animate({opacity:t},S),e.connector&&e.connector.animate({opacity:t},S))}),this.graphic.animate(v,S)}y&&this.graphic.animate(y,b(g.options.chart.animation,l.animation,a.animation)),C&&C.hide()}else e&&l&&(k=u.symbol||s.symbol,C&&C.currentSymbol!==k&&(C=C.destroy()),y&&(C?C[i?"animate":"attr"]({x:y.x,y:y.y}):k&&(s.stateMarkerGraphic=C=g.renderer.symbol(k,y.x,y.y,y.width,y.height).add(s.markerGroup),C.currentSymbol=k)),!g.styledMode&&C&&"inactive"!==this.state&&C.attr(s.pointAttribs(this,e))),C&&(C[e&&this.isInside?"show":"hide"](),C.element.point=this,C.addClass(this.getClassName(),!0));let M=o.halo,w=this.graphic||C,T=w&&w.visibility||"inherit";M&&M.size&&w&&"hidden"!==T&&!this.isCluster?(x||(s.halo=x=g.renderer.path().add(w.parentGroup)),x.show()[i?"animate":"attr"]({d:this.haloPath(M.size)}),x.attr({class:"highcharts-halo highcharts-color-"+b(this.colorIndex,s.colorIndex)+(this.className?" "+this.className:""),visibility:T,zIndex:-1}),x.point=this,g.styledMode||x.attr(c({fill:this.color||s.color,"fill-opacity":M.opacity},t.filterUserAttributes(M.attributes||{})))):x?.point?.haloPath&&!x.point.destroyed&&x.animate({d:x.point.haloPath(0)},null,x.hide),p(this,"afterSetState",{state:e})}haloPath(t){let e=this.pos();return e?this.series.chart.renderer.symbols.circle(l(e[0],1)-t,e[1]-t,2*t,2*t):[]}}return k}),i(e,"Core/Pointer.js",[e["Core/Color/Color.js"],e["Core/Globals.js"],e["Core/Utilities.js"]],function(t,e,i){var s;let{parse:r}=t,{charts:o,composed:n,isTouchDevice:a}=e,{addEvent:h,attr:l,css:d,extend:c,find:p,fireEvent:u,isNumber:g,isObject:f,objectEach:m,offset:x,pick:y,pushUnique:b,splat:v}=i;class S{applyInactiveState(t){let e=[],i;(t||[]).forEach(function(t){i=t.series,e.push(i),i.linkedParent&&e.push(i.linkedParent),i.linkedSeries&&(e=e.concat(i.linkedSeries)),i.navigatorSeries&&e.push(i.navigatorSeries)}),this.chart.series.forEach(function(t){-1===e.indexOf(t)?t.setState("inactive",!0):t.options.inactiveOtherPoints&&t.setAllPointsToState("inactive")})}destroy(){let t=this;this.eventsToUnbind.forEach(t=>t()),this.eventsToUnbind=[],!e.chartCount&&(S.unbindDocumentMouseUp&&S.unbindDocumentMouseUp.forEach(t=>t()),S.unbindDocumentTouchEnd&&(S.unbindDocumentTouchEnd=S.unbindDocumentTouchEnd())),clearInterval(t.tooltipTimeout),m(t,function(e,i){t[i]=void 0})}getSelectionMarkerAttrs(t,e){let i={args:{chartX:t,chartY:e},attrs:{},shapeType:"rect"};return u(this,"getSelectionMarkerAttrs",i,i=>{let s;let{chart:r,zoomHor:o,zoomVert:n}=this,{mouseDownX:a=0,mouseDownY:h=0}=r,l=i.attrs;l.x=r.plotLeft,l.y=r.plotTop,l.width=o?1:r.plotWidth,l.height=n?1:r.plotHeight,o&&(s=t-a,l.width=Math.max(1,Math.abs(s)),l.x=(s>0?0:s)+a),n&&(s=e-h,l.height=Math.max(1,Math.abs(s)),l.y=(s>0?0:s)+h)}),i}drag(t){let{chart:e}=this,{mouseDownX:i=0,mouseDownY:s=0}=e,{panning:o,panKey:n,selectionMarkerFill:a}=e.options.chart,h=e.plotLeft,l=e.plotTop,d=e.plotWidth,c=e.plotHeight,p=f(o)?o.enabled:o,u=n&&t[`${n}Key`],g=t.chartX,m=t.chartY,x,y=this.selectionMarker;if((!y||!y.touch)&&(gh+d&&(g=h+d),ml+c&&(m=l+c),this.hasDragged=Math.sqrt(Math.pow(i-g,2)+Math.pow(s-m,2)),this.hasDragged>10)){x=e.isInsidePlot(i-h,s-l,{visiblePlotOnly:!0});let{shapeType:n,attrs:d}=this.getSelectionMarkerAttrs(g,m);(e.hasCartesianSeries||e.mapView)&&this.hasZoom&&x&&!u&&!y&&(this.selectionMarker=y=e.renderer[n](),y.attr({class:"highcharts-selection-marker",zIndex:7}).add(),e.styledMode||y.attr({fill:a||r("#334eff").setOpacity(.25).get()})),y&&y.attr(d),x&&!y&&p&&e.pan(t,o)}}dragStart(t){let e=this.chart;e.mouseIsDown=t.type,e.cancelClick=!1,e.mouseDownX=t.chartX,e.mouseDownY=t.chartY}getSelectionBox(t){let e={args:{marker:t},result:t.getBBox()};return u(this,"getSelectionBox",e),e.result}drop(t){let e;let{chart:i,selectionMarker:s}=this;for(let t of i.axes)t.isPanning&&(t.isPanning=!1,(t.options.startOnTick||t.options.endOnTick||t.series.some(t=>t.boosted))&&(t.forceRedraw=!0,t.setExtremes(t.userMin,t.userMax,!1),e=!0));if(e&&i.redraw(),s&&t){if(this.hasDragged){let e=this.getSelectionBox(s);i.transform({axes:i.axes.filter(t=>t.zoomEnabled&&("xAxis"===t.coll&&this.zoomX||"yAxis"===t.coll&&this.zoomY)),selection:{originalEvent:t,xAxis:[],yAxis:[],...e},from:e})}g(i.index)&&(this.selectionMarker=s.destroy())}i&&g(i.index)&&(d(i.container,{cursor:i._cursor}),i.cancelClick=this.hasDragged>10,i.mouseIsDown=!1,this.hasDragged=0,this.pinchDown=[])}findNearestKDPoint(t,e,i){let s;return t.forEach(function(t){let r=!(t.noSharedTooltip&&e)&&0>t.options.findNearestPointBy.indexOf("y"),o=t.searchPoint(i,r);f(o,!0)&&o.series&&(!f(s,!0)||function(t,i){let s=t.distX-i.distX,r=t.dist-i.dist,o=i.series.group?.zIndex-t.series.group?.zIndex;return 0!==s&&e?s:0!==r?r:0!==o?o:t.series.index>i.series.index?-1:1}(s,o)>0)&&(s=o)}),s}getChartCoordinatesFromPoint(t,e){let{xAxis:i,yAxis:s}=t.series,r=t.shapeArgs;if(i&&s){let o=t.clientX??t.plotX??0,n=t.plotY||0;return t.isNode&&r&&g(r.x)&&g(r.y)&&(o=r.x,n=r.y),e?{chartX:s.len+s.pos-n,chartY:i.len+i.pos-o}:{chartX:o+i.pos,chartY:n+s.pos}}if(r&&r.x&&r.y)return{chartX:r.x,chartY:r.y}}getChartPosition(){if(this.chartPosition)return this.chartPosition;let{container:t}=this.chart,e=x(t);this.chartPosition={left:e.left,top:e.top,scaleX:1,scaleY:1};let{offsetHeight:i,offsetWidth:s}=t;return s>2&&i>2&&(this.chartPosition.scaleX=e.width/s,this.chartPosition.scaleY=e.height/i),this.chartPosition}getCoordinates(t){let e={xAxis:[],yAxis:[]};for(let i of this.chart.axes)e[i.isXAxis?"xAxis":"yAxis"].push({axis:i,value:i.toValue(t[i.horiz?"chartX":"chartY"])});return e}getHoverData(t,e,i,s,r,o){let n=[],a=function(t){return t.visible&&!(!r&&t.directTouch)&&y(t.options.enableMouseTracking,!0)},h=e,l,d={chartX:o?o.chartX:void 0,chartY:o?o.chartY:void 0,shared:r};u(this,"beforeGetHoverData",d),l=h&&!h.stickyTracking?[h]:i.filter(t=>t.stickyTracking&&(d.filter||a)(t));let c=s&&t||!o?t:this.findNearestKDPoint(l,r,o);return h=c&&c.series,c&&(r&&!h.noSharedTooltip?(l=i.filter(function(t){return d.filter?d.filter(t):a(t)&&!t.noSharedTooltip})).forEach(function(t){let e=p(t.points,function(t){return t.x===c.x&&!t.isNull});f(e)&&(t.boosted&&t.boost&&(e=t.boost.getPoint(e)),n.push(e))}):n.push(c)),u(this,"afterGetHoverData",d={hoverPoint:c}),{hoverPoint:d.hoverPoint,hoverSeries:h,hoverPoints:n}}getPointFromEvent(t){let e=t.target,i;for(;e&&!i;)i=e.point,e=e.parentNode;return i}onTrackerMouseOut(t){let e=this.chart,i=t.relatedTarget,s=e.hoverSeries;this.isDirectTouch=!1,!s||!i||s.stickyTracking||this.inClass(i,"highcharts-tooltip")||this.inClass(i,"highcharts-series-"+s.index)&&this.inClass(i,"highcharts-tracker")||s.onMouseOut()}inClass(t,e){let i=t,s;for(;i;){if(s=l(i,"class")){if(-1!==s.indexOf(e))return!0;if(-1!==s.indexOf("highcharts-container"))return!1}i=i.parentElement}}constructor(t,e){this.hasDragged=0,this.pointerCaptureEventsToUnbind=[],this.eventsToUnbind=[],this.options=e,this.chart=t,this.runChartClick=!!e.chart.events?.click,this.pinchDown=[],this.setDOMEvents(),u(this,"afterInit")}normalize(t,e){let i=t.touches,s=i?i.length?i.item(0):y(i.changedTouches,t.changedTouches)[0]:t;e||(e=this.getChartPosition());let r=s.pageX-e.left,o=s.pageY-e.top;return c(t,{chartX:Math.round(r/=e.scaleX),chartY:Math.round(o/=e.scaleY)})}onContainerClick(t){let e=this.chart,i=e.hoverPoint,s=this.normalize(t),r=e.plotLeft,o=e.plotTop;!e.cancelClick&&(i&&this.inClass(s.target,"highcharts-tracker")?(u(i.series,"click",c(s,{point:i})),e.hoverPoint&&i.firePointEvent("click",s)):(c(s,this.getCoordinates(s)),e.isInsidePlot(s.chartX-r,s.chartY-o,{visiblePlotOnly:!0})&&u(e,"click",s)))}onContainerMouseDown(t){let i=(1&(t.buttons||t.button))==1;t=this.normalize(t),e.isFirefox&&0!==t.button&&this.onContainerMouseMove(t),(void 0===t.button||i)&&(this.zoomOption(t),i&&t.preventDefault?.(),this.dragStart(t))}onContainerMouseLeave(t){let{pointer:e}=o[y(S.hoverChartIndex,-1)]||{};t=this.normalize(t),this.onContainerMouseMove(t),e&&!this.inClass(t.relatedTarget,"highcharts-tooltip")&&(e.reset(),e.chartPosition=void 0)}onContainerMouseEnter(){delete this.chartPosition}onContainerMouseMove(t){let e=this.chart,i=e.tooltip,s=this.normalize(t);this.setHoverChartIndex(t),("mousedown"===e.mouseIsDown||this.touchSelect(s))&&this.drag(s),!e.openMenu&&(this.inClass(s.target,"highcharts-tracker")||e.isInsidePlot(s.chartX-e.plotLeft,s.chartY-e.plotTop,{visiblePlotOnly:!0}))&&!(i&&i.shouldStickOnContact(s))&&(this.inClass(s.target,"highcharts-no-tooltip")?this.reset(!1,0):this.runPointActions(s))}onDocumentTouchEnd(t){this.onDocumentMouseUp(t)}onContainerTouchMove(t){this.touchSelect(t)?this.onContainerMouseMove(t):this.touch(t)}onContainerTouchStart(t){this.touchSelect(t)?this.onContainerMouseDown(t):(this.zoomOption(t),this.touch(t,!0))}onDocumentMouseMove(t){let e=this.chart,i=e.tooltip,s=this.chartPosition,r=this.normalize(t,s);!s||e.isInsidePlot(r.chartX-e.plotLeft,r.chartY-e.plotTop,{visiblePlotOnly:!0})||i&&i.shouldStickOnContact(r)||r.target!==e.container.ownerDocument&&this.inClass(r.target,"highcharts-tracker")||this.reset()}onDocumentMouseUp(t){o[y(S.hoverChartIndex,-1)]?.pointer?.drop(t)}pinch(t){let e=this,{chart:i,hasZoom:s,lastTouches:r}=e,o=[].map.call(t.touches||[],t=>e.normalize(t)),n=o.length,a=1===n&&(e.inClass(t.target,"highcharts-tracker")&&i.runTrackerClick||e.runChartClick),h=i.tooltip,l=1===n&&y(h?.options.followTouchMove,!0);n>1?e.initiated=!0:l&&(e.initiated=!1),s&&e.initiated&&!a&&!1!==t.cancelable&&t.preventDefault(),"touchstart"===t.type?(e.pinchDown=o,e.res=!0,i.mouseDownX=t.chartX):l?this.runPointActions(e.normalize(t)):r&&(u(i,"touchpan",{originalEvent:t,touches:o},()=>{let e=t=>{let e=t[0],i=t[1]||e;return{x:e.chartX,y:e.chartY,width:i.chartX-e.chartX,height:i.chartY-e.chartY}};i.transform({axes:i.axes.filter(t=>t.zoomEnabled&&(this.zoomHor&&t.horiz||this.zoomVert&&!t.horiz)),to:e(o),from:e(r),trigger:t.type})}),e.res&&(e.res=!1,this.reset(!1,0))),e.lastTouches=o}reset(t,e){let i=this.chart,s=i.hoverSeries,r=i.hoverPoint,o=i.hoverPoints,n=i.tooltip,a=n&&n.shared?o:r;t&&a&&v(a).forEach(function(e){e.series.isCartesian&&void 0===e.plotX&&(t=!1)}),t?n&&a&&v(a).length&&(n.refresh(a),n.shared&&o?o.forEach(function(t){t.setState(t.state,!0),t.series.isCartesian&&(t.series.xAxis.crosshair&&t.series.xAxis.drawCrosshair(null,t),t.series.yAxis.crosshair&&t.series.yAxis.drawCrosshair(null,t))}):r&&(r.setState(r.state,!0),i.axes.forEach(function(t){t.crosshair&&r.series[t.coll]===t&&t.drawCrosshair(null,r)}))):(r&&r.onMouseOut(),o&&o.forEach(function(t){t.setState()}),s&&s.onMouseOut(),n&&n.hide(e),this.unDocMouseMove&&(this.unDocMouseMove=this.unDocMouseMove()),i.axes.forEach(function(t){t.hideCrosshair()}),i.hoverPoints=i.hoverPoint=void 0)}runPointActions(t,e,i){let s=this.chart,r=s.series,n=s.tooltip&&s.tooltip.options.enabled?s.tooltip:void 0,a=!!n&&n.shared,l=e||s.hoverPoint,d=l&&l.series||s.hoverSeries,c=(!t||"touchmove"!==t.type)&&(!!e||d&&d.directTouch&&this.isDirectTouch),u=this.getHoverData(l,d,r,c,a,t);l=u.hoverPoint,d=u.hoverSeries;let g=u.hoverPoints,f=d&&d.tooltipOptions.followPointer&&!d.tooltipOptions.split,m=a&&d&&!d.noSharedTooltip;if(l&&(i||l!==s.hoverPoint||n&&n.isHidden)){if((s.hoverPoints||[]).forEach(function(t){-1===g.indexOf(t)&&t.setState()}),s.hoverSeries!==d&&d.onMouseOver(),this.applyInactiveState(g),(g||[]).forEach(function(t){t.setState("hover")}),s.hoverPoint&&s.hoverPoint.firePointEvent("mouseOut"),!l.series)return;s.hoverPoints=g,s.hoverPoint=l,l.firePointEvent("mouseOver",void 0,()=>{n&&l&&n.refresh(m?g:l,t)})}else if(f&&n&&!n.isHidden){let e=n.getAnchor([{}],t);s.isInsidePlot(e[0],e[1],{visiblePlotOnly:!0})&&n.updatePosition({plotX:e[0],plotY:e[1]})}this.unDocMouseMove||(this.unDocMouseMove=h(s.container.ownerDocument,"mousemove",t=>o[S.hoverChartIndex??-1]?.pointer?.onDocumentMouseMove(t)),this.eventsToUnbind.push(this.unDocMouseMove)),s.axes.forEach(function(e){let i;let r=y((e.crosshair||{}).snap,!0);!r||(i=s.hoverPoint)&&i.series[e.coll]===e||(i=p(g,t=>t.series&&t.series[e.coll]===e)),i||!r?e.drawCrosshair(t,i):e.hideCrosshair()})}setDOMEvents(){let t=this.chart.container,e=t.ownerDocument;t.onmousedown=this.onContainerMouseDown.bind(this),t.onmousemove=this.onContainerMouseMove.bind(this),t.onclick=this.onContainerClick.bind(this),this.eventsToUnbind.push(h(t,"mouseenter",this.onContainerMouseEnter.bind(this)),h(t,"mouseleave",this.onContainerMouseLeave.bind(this))),S.unbindDocumentMouseUp||(S.unbindDocumentMouseUp=[]),S.unbindDocumentMouseUp.push(h(e,"mouseup",this.onDocumentMouseUp.bind(this)));let i=this.chart.renderTo.parentElement;for(;i&&"BODY"!==i.tagName;)this.eventsToUnbind.push(h(i,"scroll",()=>{delete this.chartPosition})),i=i.parentElement;this.eventsToUnbind.push(h(t,"touchstart",this.onContainerTouchStart.bind(this),{passive:!1}),h(t,"touchmove",this.onContainerTouchMove.bind(this),{passive:!1})),S.unbindDocumentTouchEnd||(S.unbindDocumentTouchEnd=h(e,"touchend",this.onDocumentTouchEnd.bind(this),{passive:!1})),this.setPointerCapture(),h(this.chart,"redraw",this.setPointerCapture.bind(this))}setPointerCapture(){if(!a)return;let t=this.pointerCaptureEventsToUnbind,e=this.chart,i=e.container,s=y(e.options.tooltip?.followTouchMove,!0)&&e.series.some(t=>t.options.findNearestPointBy.indexOf("y")>-1);!this.hasPointerCapture&&s?(t.push(h(i,"pointerdown",t=>{t.target?.hasPointerCapture(t.pointerId)&&t.target?.releasePointerCapture(t.pointerId)}),h(i,"pointermove",t=>{e.pointer?.getPointFromEvent(t)?.onMouseOver(t)})),e.styledMode||d(i,{"touch-action":"none"}),i.className+=" highcharts-no-touch-action",this.hasPointerCapture=!0):this.hasPointerCapture&&!s&&(t.forEach(t=>t()),t.length=0,e.styledMode||d(i,{"touch-action":y(e.options.chart.style?.["touch-action"],"manipulation")}),i.className=i.className.replace(" highcharts-no-touch-action",""),this.hasPointerCapture=!1)}setHoverChartIndex(t){let i=this.chart,s=e.charts[y(S.hoverChartIndex,-1)];if(s&&s!==i){let e={relatedTarget:i.container};t&&!t?.relatedTarget&&(t={...e,...t}),s.pointer?.onContainerMouseLeave(t||e)}s&&s.mouseIsDown||(S.hoverChartIndex=i.index)}touch(t,e){let i;let{chart:s,pinchDown:r=[]}=this;this.setHoverChartIndex(),1===(t=this.normalize(t)).touches.length?s.isInsidePlot(t.chartX-s.plotLeft,t.chartY-s.plotTop,{visiblePlotOnly:!0})&&!s.openMenu?(e&&this.runPointActions(t),"touchmove"===t.type&&(i=!!r[0]&&Math.pow(r[0].chartX-t.chartX,2)+Math.pow(r[0].chartY-t.chartY,2)>=16),y(i,!0)&&this.pinch(t)):e&&this.reset():2===t.touches.length&&this.pinch(t)}touchSelect(t){return!!(this.chart.zooming.singleTouch&&t.touches&&1===t.touches.length)}zoomOption(t){let e=this.chart,i=e.inverted,s=e.zooming.type||"",r,o;/touch/.test(t.type)&&(s=y(e.zooming.pinchType,s)),this.zoomX=r=/x/.test(s),this.zoomY=o=/y/.test(s),this.zoomHor=r&&!i||o&&i,this.zoomVert=o&&!i||r&&i,this.hasZoom=r||o}}return(s=S||(S={})).compose=function(t){b(n,"Core.Pointer")&&h(t,"beforeRender",function(){this.pointer=new s(this,this.options)})},S}),i(e,"Core/Legend/LegendSymbol.js",[e["Core/Utilities.js"]],function(t){var e;let{extend:i,merge:s,pick:r}=t;return function(t){function e(t,e,o){let n=this.legendItem=this.legendItem||{},{chart:a,options:h}=this,{baseline:l=0,symbolWidth:d,symbolHeight:c}=t,p=this.symbol||"circle",u=c/2,g=a.renderer,f=n.group,m=l-Math.round((t.fontMetrics?.b||c)*(o?.4:.3)),x={},y,b=h.marker,v=0;if(a.styledMode||(x["stroke-width"]=Math.min(h.lineWidth||0,24),h.dashStyle?x.dashstyle=h.dashStyle:"square"===h.linecap||(x["stroke-linecap"]="round")),n.line=g.path().addClass("highcharts-graph").attr(x).add(f),o&&(n.area=g.path().addClass("highcharts-area").add(f)),x["stroke-linecap"]&&(v=Math.min(n.line.strokeWidth(),d)/2),d){let t=[["M",v,m],["L",d-v,m]];n.line.attr({d:t}),n.area?.attr({d:[...t,["L",d-v,l],["L",v,l]]})}if(b&&!1!==b.enabled&&d){let t=Math.min(r(b.radius,u),u);0===p.indexOf("url")&&(b=s(b,{width:c,height:c}),t=0),n.symbol=y=g.symbol(p,d/2-t,m-t,2*t,2*t,i({context:"legend"},b)).addClass("highcharts-point").add(f),y.isMarker=!0}}t.areaMarker=function(t,i){e.call(this,t,i,!0)},t.lineMarker=e,t.rectangle=function(t,e){let i=e.legendItem||{},s=t.options,o=t.symbolHeight,n=s.squareSymbol,a=n?o:t.symbolWidth;i.symbol=this.chart.renderer.rect(n?(t.symbolWidth-o)/2:0,t.baseline-o+1,a,o,r(t.options.symbolRadius,o/2)).addClass("highcharts-point").attr({zIndex:3}).add(i.group)}}(e||(e={})),e}),i(e,"Core/Series/SeriesDefaults.js",[],function(){return{lineWidth:2,allowPointSelect:!1,crisp:!0,showCheckbox:!1,animation:{duration:1e3},enableMouseTracking:!0,events:{},marker:{enabledThreshold:2,lineColor:"#ffffff",lineWidth:0,radius:4,states:{normal:{animation:!0},hover:{animation:{duration:150},enabled:!0,radiusPlus:2,lineWidthPlus:1},select:{fillColor:"#cccccc",lineColor:"#000000",lineWidth:2}}},point:{events:{}},dataLabels:{animation:{},align:"center",borderWidth:0,defer:!0,formatter:function(){let{numberFormatter:t}=this.series.chart;return"number"!=typeof this.y?"":t(this.y,-1)},padding:5,style:{fontSize:"0.7em",fontWeight:"bold",color:"contrast",textOutline:"1px contrast"},verticalAlign:"bottom",x:0,y:0},cropThreshold:300,opacity:1,pointRange:0,softThreshold:!0,states:{normal:{animation:!0},hover:{animation:{duration:150},lineWidthPlus:1,marker:{},halo:{size:10,opacity:.25}},select:{animation:{duration:0}},inactive:{animation:{duration:150},opacity:.2}},stickyTracking:!0,turboThreshold:1e3,findNearestPointBy:"x"}}),i(e,"Core/Series/SeriesRegistry.js",[e["Core/Globals.js"],e["Core/Defaults.js"],e["Core/Series/Point.js"],e["Core/Utilities.js"]],function(t,e,i,s){var r;let{defaultOptions:o}=e,{extend:n,extendClass:a,merge:h}=s;return function(e){function s(t,s){let r=o.plotOptions||{},n=s.defaultOptions,a=s.prototype;return a.type=t,a.pointClass||(a.pointClass=i),!e.seriesTypes[t]&&(n&&(r[t]=n),e.seriesTypes[t]=s,!0)}e.seriesTypes=t.seriesTypes,e.registerSeriesType=s,e.seriesType=function(t,r,l,d,c){let p=o.plotOptions||{};if(r=r||"",p[t]=h(p[r],l),delete e.seriesTypes[t],s(t,a(e.seriesTypes[r]||function(){},d)),e.seriesTypes[t].prototype.type=t,c){class s extends i{}n(s.prototype,c),e.seriesTypes[t].prototype.pointClass=s}return e.seriesTypes[t]}}(r||(r={})),r}),i(e,"Core/Series/Series.js",[e["Core/Animation/AnimationUtilities.js"],e["Core/Defaults.js"],e["Core/Foundation.js"],e["Core/Globals.js"],e["Core/Legend/LegendSymbol.js"],e["Core/Series/Point.js"],e["Core/Series/SeriesDefaults.js"],e["Core/Series/SeriesRegistry.js"],e["Core/Renderer/SVG/SVGElement.js"],e["Core/Utilities.js"]],function(t,e,i,s,r,o,n,a,h,l){let{animObject:d,setAnimation:c}=t,{defaultOptions:p}=e,{registerEventOptions:u}=i,{svg:g,win:f}=s,{seriesTypes:m}=a,{arrayMax:x,arrayMin:y,clamp:b,correctFloat:v,crisp:S,defined:C,destroyObjectProperties:k,diffObjects:M,erase:w,error:T,extend:A,find:P,fireEvent:L,getClosestDistance:O,getNestedProperty:D,insertItem:E,isArray:I,isNumber:j,isString:B,merge:R,objectEach:z,pick:N,removeEvent:W,splat:G,syncTimeout:H}=l;class X{constructor(){this.zoneAxis="y"}init(t,e){let i;L(this,"init",{options:e});let s=this,r=t.series;this.eventsToUnbind=[],s.chart=t,s.options=s.setOptions(e);let o=s.options,n=!1!==o.visible;s.linkedSeries=[],s.bindAxes(),A(s,{name:o.name,state:"",visible:n,selected:!0===o.selected}),u(this,o);let a=o.events;(a&&a.click||o.point&&o.point.events&&o.point.events.click||o.allowPointSelect)&&(t.runTrackerClick=!0),s.getColor(),s.getSymbol(),s.parallelArrays.forEach(function(t){s[t+"Data"]||(s[t+"Data"]=[])}),s.isCartesian&&(t.hasCartesianSeries=!0),r.length&&(i=r[r.length-1]),s._i=N(i&&i._i,-1)+1,s.opacity=s.options.opacity,t.orderItems("series",E(this,r)),o.dataSorting&&o.dataSorting.enabled?s.setDataSortingOptions():s.points||s.data||s.setData(o.data,!1),L(this,"afterInit")}is(t){return m[t]&&this instanceof m[t]}bindAxes(){let t;let e=this,i=e.options,s=e.chart;L(this,"bindAxes",null,function(){(e.axisTypes||[]).forEach(function(r){(s[r]||[]).forEach(function(s){t=s.options,(N(i[r],0)===s.index||void 0!==i[r]&&i[r]===t.id)&&(E(e,s.series),e[r]=s,s.isDirty=!0)}),e[r]||e.optionalAxis===r||T(18,!0,s)})}),L(this,"afterBindAxes")}updateParallelArrays(t,e,i){let s=t.series,r=j(e)?function(i){let r="y"===i&&s.toYData?s.toYData(t):t[i];s[i+"Data"][e]=r}:function(t){Array.prototype[e].apply(s[t+"Data"],i)};s.parallelArrays.forEach(r)}hasData(){return this.visible&&void 0!==this.dataMax&&void 0!==this.dataMin||this.visible&&this.yData&&this.yData.length>0}hasMarkerChanged(t,e){let i=t.marker,s=e.marker||{};return i&&(s.enabled&&!i.enabled||s.symbol!==i.symbol||s.height!==i.height||s.width!==i.width)}autoIncrement(t){let e=this.options,i=e.pointIntervalUnit,s=e.relativeXValue,r=this.chart.time,o=this.xIncrement,n,a;return(o=N(o,e.pointStart,0),this.pointInterval=a=N(this.pointInterval,e.pointInterval,1),s&&j(t)&&(a*=t),i&&(n=new r.Date(o),"day"===i?r.set("Date",n,r.get("Date",n)+a):"month"===i?r.set("Month",n,r.get("Month",n)+a):"year"===i&&r.set("FullYear",n,r.get("FullYear",n)+a),a=n.getTime()-o),s&&j(t))?o+a:(this.xIncrement=o+a,o)}setDataSortingOptions(){let t=this.options;A(this,{requireSorting:!1,sorted:!1,enabledDataSorting:!0,allowDG:!1}),C(t.pointRange)||(t.pointRange=1)}setOptions(t){let e;let i=this.chart,s=i.options.plotOptions,r=i.userOptions||{},o=R(t),n=i.styledMode,a={plotOptions:s,userOptions:o};L(this,"setOptions",a);let h=a.plotOptions[this.type],l=r.plotOptions||{},d=l.series||{},c=p.plotOptions[this.type]||{},u=l[this.type]||{};this.userOptions=a.userOptions;let g=R(h,s.series,u,o);this.tooltipOptions=R(p.tooltip,p.plotOptions.series?.tooltip,c?.tooltip,i.userOptions.tooltip,l.series?.tooltip,u.tooltip,o.tooltip),this.stickyTracking=N(o.stickyTracking,u.stickyTracking,d.stickyTracking,!!this.tooltipOptions.shared&&!this.noSharedTooltip||g.stickyTracking),null===h.marker&&delete g.marker,this.zoneAxis=g.zoneAxis||"y";let f=this.zones=(g.zones||[]).map(t=>({...t}));return(g.negativeColor||g.negativeFillColor)&&!g.zones&&(e={value:g[this.zoneAxis+"Threshold"]||g.threshold||0,className:"highcharts-negative"},n||(e.color=g.negativeColor,e.fillColor=g.negativeFillColor),f.push(e)),f.length&&C(f[f.length-1].value)&&f.push(n?{}:{color:this.color,fillColor:this.fillColor}),L(this,"afterSetOptions",{options:g}),g}getName(){return N(this.options.name,"Series "+(this.index+1))}getCyclic(t,e,i){let s,r;let o=this.chart,n=`${t}Index`,a=`${t}Counter`,h=i?.length||o.options.chart.colorCount;!e&&(C(r=N("color"===t?this.options.colorIndex:void 0,this[n]))?s=r:(o.series.length||(o[a]=0),s=o[a]%h,o[a]+=1),i&&(e=i[s])),void 0!==s&&(this[n]=s),this[t]=e}getColor(){this.chart.styledMode?this.getCyclic("color"):this.options.colorByPoint?this.color="#cccccc":this.getCyclic("color",this.options.color||p.plotOptions[this.type].color,this.chart.options.colors)}getPointsCollection(){return(this.hasGroupedData?this.points:this.data)||[]}getSymbol(){let t=this.options.marker;this.getCyclic("symbol",t.symbol,this.chart.options.symbols)}findPointIndex(t,e){let i,s,r;let n=t.id,a=t.x,h=this.points,l=this.options.dataSorting;if(n){let t=this.chart.get(n);t instanceof o&&(i=t)}else if(this.linkedParent||this.enabledDataSorting||this.options.relativeXValue){let e=e=>!e.touched&&e.index===t.index;if(l&&l.matchByName?e=e=>!e.touched&&e.name===t.name:this.options.relativeXValue&&(e=e=>!e.touched&&e.options.x===t.x),!(i=P(h,e)))return}return i&&void 0!==(r=i&&i.index)&&(s=!0),void 0===r&&j(a)&&(r=this.xData.indexOf(a,e)),-1!==r&&void 0!==r&&this.cropped&&(r=r>=this.cropStart?r-this.cropStart:r),!s&&j(r)&&h[r]&&h[r].touched&&(r=void 0),r}updateData(t,e){let i=this.options,s=i.dataSorting,r=this.points,o=[],n=this.requireSorting,a=t.length===r.length,h,l,d,c,p=!0;if(this.xIncrement=null,t.forEach(function(t,e){let l;let d=C(t)&&this.pointClass.prototype.optionsToObject.call({series:this},t)||{},p=d.x;d.id||j(p)?(-1===(l=this.findPointIndex(d,c))||void 0===l?o.push(t):r[l]&&t!==i.data[l]?(r[l].update(t,!1,null,!1),r[l].touched=!0,n&&(c=l+1)):r[l]&&(r[l].touched=!0),(!a||e!==l||s&&s.enabled||this.hasDerivedData)&&(h=!0)):o.push(t)},this),h)for(l=r.length;l--;)(d=r[l])&&!d.touched&&d.remove&&d.remove(!1,e);else!a||s&&s.enabled?p=!1:(t.forEach(function(t,e){t===r[e].y||r[e].destroyed||r[e].update(t,!1,null,!1)}),o.length=0);return r.forEach(function(t){t&&(t.touched=!1)}),!!p&&(o.forEach(function(t){this.addPoint(t,!1,null,null,!1)},this),null===this.xIncrement&&this.xData&&this.xData.length&&(this.xIncrement=x(this.xData),this.autoIncrement()),!0)}setData(t,e=!0,i,s){let r=this,o=r.points,n=o&&o.length||0,a=r.options,h=r.chart,l=a.dataSorting,d=r.xAxis,c=a.turboThreshold,p=this.xData,u=this.yData,g=r.pointArrayMap,f=g&&g.length,m=a.keys,x,y,b,v=0,S=1,C;h.options.chart.allowMutatingData||(a.data&&delete r.options.data,r.userOptions.data&&delete r.userOptions.data,C=R(!0,t));let k=(t=C||t||[]).length;if(l&&l.enabled&&(t=this.sortData(t)),h.options.chart.allowMutatingData&&!1!==s&&k&&n&&!r.cropped&&!r.hasGroupedData&&r.visible&&!r.boosted&&(b=this.updateData(t,i)),!b){r.xIncrement=null,r.colorCounter=0,this.parallelArrays.forEach(function(t){r[t+"Data"].length=0});let e=c&&k>c;if(e){let i=r.getFirstValidPoint(t),s=r.getFirstValidPoint(t,k-1,-1),o=t=>!!(I(t)&&(m||j(t[0])));if(j(i)&&j(s))for(x=0;x=0?v:0,S=S>=0?S:1),1===i.length&&(S=0),v===S)for(x=0;x{let s=D(i,t),r=D(i,e);return rs?1:0}).forEach(function(t,e){t.x=e},this),e.linkedSeries&&e.linkedSeries.forEach(function(e){let i=e.options,r=i.data;i.dataSorting&&i.dataSorting.enabled||!r||(r.forEach(function(i,o){r[o]=s(e,i),t[o]&&(r[o].x=t[o].x,r[o].index=o)}),e.setData(r,!1))}),t}getProcessedData(t){let e=this,i=e.xAxis,s=e.options.cropThreshold,r=i?.logarithmic,o=e.isCartesian,n,a,h=0,l,d,c,p=e.xData,u=e.yData,g=!1,f=p.length;i&&(d=(l=i.getExtremes()).min,c=l.max,g=!!(i.categories&&!i.names.length)),o&&e.sorted&&!t&&(!s||f>s||e.forceCrop)&&(p[f-1]c?(p=[],u=[]):e.yData&&(p[0]c)&&(p=(n=this.cropData(e.xData,e.yData,d,c)).xData,u=n.yData,h=n.start,a=!0));let m=O([r?p.map(r.log2lin):p],()=>e.requireSorting&&!g&&T(15,!1,e.chart));return{xData:p,yData:u,cropped:a,cropStart:h,closestPointRange:m}}processData(t){let e=this.xAxis;if(this.isCartesian&&!this.isDirty&&!e.isDirty&&!this.yAxis.isDirty&&!t)return!1;let i=this.getProcessedData();this.cropped=i.cropped,this.cropStart=i.cropStart,this.processedXData=i.xData,this.processedYData=i.yData,this.closestPointRange=this.basePointRange=i.closestPointRange,L(this,"afterProcessData")}cropData(t,e,i,s){let r=t.length,o,n,a=0,h=r;for(o=0;o=i){a=Math.max(0,o-1);break}for(n=o;ns){h=n+1;break}return{xData:t.slice(a,h),yData:e.slice(a,h),start:a,end:h}}generatePoints(){let t=this.options,e=this.processedData||t.data,i=this.processedXData,s=this.processedYData,r=this.pointClass,o=i.length,n=this.cropStart||0,a=this.hasGroupedData,h=t.keys,l=[],d=t.dataGrouping&&t.dataGrouping.groupAll?n:0,c,p,u,g,f=this.data;if(!f&&!a){let t=[];t.length=e.length,f=this.data=t}for(h&&a&&(this.options.keys=!1),g=0;g0:g.length)||!n),p=e||this.getExtremesFromAll||this.options.getExtremesFromAll||this.cropped||!i||(k[f+o]||u)>=b&&(k[f-o]||u)<=v,c&&p){if(m=g.length)for(;m--;)j(g[m])&&(r[S++]=g[m]);else r[S++]=g}let M={activeYData:r,dataMin:y(r),dataMax:x(r)};return L(this,"afterGetExtremes",{dataExtremes:M}),M}applyExtremes(){let t=this.getExtremes();return this.dataMin=t.dataMin,this.dataMax=t.dataMax,t}getFirstValidPoint(t,e=0,i=1){let s=t.length,r=e;for(;r>=0&&r1)&&(o.step=function(t,e){i&&i.apply(e,arguments),"width"===e.prop&&h?.element&&h.attr(r?"height":"width",t+99)}),a.addClass("highcharts-animating").animate(t,o)}}afterAnimate(){this.setClip(),z(this.chart.sharedClips,(t,e,i)=>{t&&!this.chart.container.querySelector(`[clip-path="url(#${t.id})"]`)&&(t.destroy(),delete i[e])}),this.finishedAnimating=!0,L(this,"afterAnimate")}drawPoints(t=this.points){let e,i,s,r,o,n,a;let h=this.chart,l=h.styledMode,{colorAxis:d,options:c}=this,p=c.marker,u=this[this.specialGroup||"markerGroup"],g=this.xAxis,f=N(p.enabled,!g||!!g.isRadial||null,this.closestPointRangePx>=p.enabledThreshold*p.radius);if(!1!==p.enabled||this._hasPointMarkers)for(e=0;e0||i.hasImage)&&(i.graphic=s=h.renderer.symbol(t,a.x,a.y,a.width,a.height,n?o:p).add(u),this.enabledDataSorting&&h.hasRendered&&(s.attr({x:i.startXPos}),r="animate")),s&&"animate"===r&&s[e?"show":"hide"](e).animate(a),s){let t=this.pointAttribs(i,l||!i.selected?void 0:"select");l?d&&s.css({fill:t.fill}):s[r](t)}s&&s.addClass(i.getClassName(),!0)}else s&&(i.graphic=s.destroy())}markerAttribs(t,e){let i=this.options,s=i.marker,r=t.marker||{},o=r.symbol||s.symbol,n={},a,h,l=N(r.radius,s&&s.radius);e&&(a=s.states[e],l=N((h=r.states&&r.states[e])&&h.radius,a&&a.radius,l&&l+(a&&a.radiusPlus||0))),t.hasImage=o&&0===o.indexOf("url"),t.hasImage&&(l=0);let d=t.pos();return j(l)&&d&&(i.crisp&&(d[0]=S(d[0],t.hasImage?0:"rect"===o?s?.lineWidth||0:1)),n.x=d[0]-l,n.y=d[1]-l),l&&(n.width=n.height=2*l),n}pointAttribs(t,e){let i=this.options.marker,s=t&&t.options,r=s&&s.marker||{},o=s&&s.color,n=t&&t.color,a=t&&t.zone&&t.zone.color,h,l,d=this.color,c,p,u=N(r.lineWidth,i.lineWidth),g=1;return d=o||a||n||d,c=r.fillColor||i.fillColor||d,p=r.lineColor||i.lineColor||d,e=e||"normal",h=i.states[e]||{},u=N((l=r.states&&r.states[e]||{}).lineWidth,h.lineWidth,u+N(l.lineWidthPlus,h.lineWidthPlus,0)),c=l.fillColor||h.fillColor||c,{stroke:p=l.lineColor||h.lineColor||p,"stroke-width":u,fill:c,opacity:g=N(l.opacity,h.opacity,g)}}destroy(t){let e,i,s;let r=this,o=r.chart,n=/AppleWebKit\/533/.test(f.navigator.userAgent),a=r.data||[];for(L(r,"destroy",{keepEventsForUpdate:t}),this.removeEvents(t),(r.axisTypes||[]).forEach(function(t){(s=r[t])&&s.series&&(w(s.series,r),s.isDirty=s.forceRedraw=!0)}),r.legendItem&&r.chart.legend.destroyItem(r),e=a.length;e--;)(i=a[e])&&i.destroy&&i.destroy();for(let t of r.zones)k(t,void 0,!0);l.clearTimeout(r.animationTimeout),z(r,function(t,e){t instanceof h&&!t.survive&&t[n&&"group"===e?"hide":"destroy"]()}),o.hoverSeries===r&&(o.hoverSeries=void 0),w(o.series,r),o.orderItems("series"),z(r,function(e,i){t&&"hcEvents"===i||delete r[i]})}applyZones(){let{area:t,chart:e,graph:i,zones:s,points:r,xAxis:o,yAxis:n,zoneAxis:a}=this,{inverted:h,renderer:l}=e,d=this[`${a}Axis`],{isXAxis:c,len:p=0}=d||{},u=(i?.strokeWidth()||0)/2+1,g=(t,e=0,i=0)=>{h&&(i=p-i);let{translated:s=0,lineClip:r}=t,o=i-s;r?.push(["L",e,Math.abs(o){t.forEach((e,i)=>{("M"===e[0]||"L"===e[0])&&(t[i]=[e[0],c?p-e[1]:e[1],c?e[2]:p-e[2]])})};if(s.forEach(t=>{t.lineClip=[],t.translated=b(d.toPixels(N(t.value,e),!0)||0,0,p)}),i&&!this.showLine&&i.hide(),t&&t.hide(),"y"===a&&r.length{let s=e.lineClip||[],r=Math.round(e.translated||0);o.reversed&&s.reverse();let{clip:a,simpleClip:d}=e,p=0,g=0,x=o.len,y=n.len;c?(p=r,x=m):(g=r,y=m);let b=[["M",p,g],["L",x,g],["L",x,y],["L",p,y],["Z"]],v=[b[0],...s,b[1],b[2],...f,b[3],b[4]];f=s.reverse(),m=r,h&&(u(v),t&&u(b)),a?(a.animate({d:v}),d?.animate({d:b})):(a=e.clip=l.path(v),t&&(d=e.simpleClip=l.path(b))),i&&e.graph?.clip(a),t&&e.area?.clip(d)})}else this.visible&&(i&&i.show(),t&&t.show())}plotGroup(t,e,i,s,r){let o=this[t],n=!o,a={visibility:i,zIndex:s||.1};return C(this.opacity)&&!this.chart.styledMode&&"inactive"!==this.state&&(a.opacity=this.opacity),o||(this[t]=o=this.chart.renderer.g().add(r)),o.addClass("highcharts-"+e+" highcharts-series-"+this.index+" highcharts-"+this.type+"-series "+(C(this.colorIndex)?"highcharts-color-"+this.colorIndex+" ":"")+(this.options.className||"")+(o.hasClass("highcharts-tracker")?" highcharts-tracker":""),!0),o.attr(a)[n?"attr":"animate"](this.getPlotBox(e)),o}getPlotBox(t){let e=this.xAxis,i=this.yAxis,s=this.chart,r=s.inverted&&!s.polar&&e&&this.invertible&&"series"===t;return s.inverted&&(e=i,i=this.xAxis),{translateX:e?e.left:s.plotLeft,translateY:i?i.top:s.plotTop,rotation:r?90:0,rotationOriginX:r?(e.len-i.len)/2:0,rotationOriginY:r?(e.len+i.len)/2:0,scaleX:r?-1:1,scaleY:1}}removeEvents(t){let{eventsToUnbind:e}=this;t||W(this),e.length&&(e.forEach(t=>{t()}),e.length=0)}render(){let t=this,{chart:e,options:i,hasRendered:s}=t,r=d(i.animation),o=t.visible?"inherit":"hidden",n=i.zIndex,a=e.seriesGroup,h=t.finishedAnimating?0:r.duration;L(this,"render"),t.plotGroup("group","series",o,n,a),t.markerGroup=t.plotGroup("markerGroup","markers",o,n,a),!1!==i.clip&&t.setClip(),h&&t.animate?.(!0),t.drawGraph&&(t.drawGraph(),t.applyZones()),t.visible&&t.drawPoints(),t.drawDataLabels?.(),t.redrawPoints?.(),i.enableMouseTracking&&t.drawTracker?.(),h&&t.animate?.(),s||(h&&r.defer&&(h+=r.defer),t.animationTimeout=H(()=>{t.afterAnimate()},h||0)),t.isDirty=!1,t.hasRendered=!0,L(t,"afterRender")}redraw(){let t=this.isDirty||this.isDirtyData;this.translate(),this.render(),t&&delete this.kdTree}reserveSpace(){return this.visible||!this.chart.options.chart.ignoreHiddenSeries}searchPoint(t,e){let{xAxis:i,yAxis:s}=this,r=this.chart.inverted;return this.searchKDTree({clientX:r?i.len-t.chartY+i.pos:t.chartX-i.pos,plotY:r?s.len-t.chartX+s.pos:t.chartY-s.pos},e,t)}buildKDTree(t){this.buildingKdTree=!0;let e=this,i=e.options.findNearestPointBy.indexOf("y")>-1?2:1;delete e.kdTree,H(function(){e.kdTree=function t(i,s,r){let o,n;let a=i?.length;if(a)return o=e.kdAxisArray[s%r],i.sort((t,e)=>(t[o]||0)-(e[o]||0)),{point:i[n=Math.floor(a/2)],left:t(i.slice(0,n),s+1,r),right:t(i.slice(n+1),s+1,r)}}(e.getValidPoints(void 0,!e.directTouch),i,i),e.buildingKdTree=!1},e.options.kdNow||t?.type==="touchstart"?0:1)}searchKDTree(t,e,i){let s=this,[r,o]=this.kdAxisArray,n=e?"distX":"dist",a=(s.options.findNearestPointBy||"").indexOf("y")>-1?2:1,h=!!s.isBubble;if(this.kdTree||this.buildingKdTree||this.buildKDTree(i),this.kdTree)return function t(e,i,a,l){let d=i.point,c=s.kdAxisArray[a%l],p,u,g=d;!function(t,e){let i=t[r],s=e[r],n=C(i)&&C(s)?i-s:null,a=t[o],l=e[o],d=C(a)&&C(l)?a-l:0,c=h&&e.marker?.radius||0;e.dist=Math.sqrt((n&&n*n||0)+d*d)-c,e.distX=C(n)?Math.abs(n)-c:Number.MAX_VALUE}(e,d);let f=(e[c]||0)-(d[c]||0)+(h&&d.marker?.radius||0),m=f<0?"left":"right",x=f<0?"right":"left";return i[m]&&(g=(p=t(e,i[m],a+1,l))[n]=0&&o<=(s?s.len:e.plotHeight)&&r>=0&&r<=(i?i.len:e.plotWidth)}drawTracker(){let t=this,e=t.options,i=e.trackByArea,s=[].concat((i?t.areaPath:t.graphPath)||[]),r=t.chart,o=r.pointer,n=r.renderer,a=r.options.tooltip?.snap||0,h=()=>{e.enableMouseTracking&&r.hoverSeries!==t&&t.onMouseOver()},l="rgba(192,192,192,"+(g?1e-4:.002)+")",d=t.tracker;d?d.attr({d:s}):t.graph&&(t.tracker=d=n.path(s).attr({visibility:t.visible?"inherit":"hidden",zIndex:2}).addClass(i?"highcharts-tracker-area":"highcharts-tracker-line").add(t.group),r.styledMode||d.attr({"stroke-linecap":"round","stroke-linejoin":"round",stroke:l,fill:i?l:"none","stroke-width":t.graph.strokeWidth()+(i?0:2*a)}),[t.tracker,t.markerGroup,t.dataLabelsGroup].forEach(t=>{t&&(t.addClass("highcharts-tracker").on("mouseover",h).on("mouseout",t=>{o?.onTrackerMouseOut(t)}),e.cursor&&!r.styledMode&&t.css({cursor:e.cursor}),t.on("touchstart",h))})),L(this,"afterDrawTracker")}addPoint(t,e,i,s,r){let o,n;let a=this.options,h=this.data,l=this.chart,d=this.xAxis,c=d&&d.hasNames&&d.names,p=a.data,u=this.xData;e=N(e,!0);let g={series:this};this.pointClass.prototype.applyOptions.apply(g,[t]);let f=g.x;if(n=u.length,this.requireSorting&&ff;)n--;this.updateParallelArrays(g,"splice",[n,0,0]),this.updateParallelArrays(g,n),c&&g.name&&(c[f]=g.name),p.splice(n,0,t),(o||this.processedData)&&(this.data.splice(n,0,null),this.processData()),"point"===a.legendType&&this.generatePoints(),i&&(h[0]&&h[0].remove?h[0].remove(!1):(h.shift(),this.updateParallelArrays(g,"shift"),p.shift())),!1!==r&&L(this,"addPoint",{point:g}),this.isDirty=!0,this.isDirtyData=!0,e&&l.redraw(s)}removePoint(t,e,i){let s=this,r=s.data,o=r[t],n=s.points,a=s.chart,h=function(){n&&n.length===r.length&&n.splice(t,1),r.splice(t,1),s.options.data.splice(t,1),s.updateParallelArrays(o||{series:s},"splice",[t,1]),o&&o.destroy(),s.isDirty=!0,s.isDirtyData=!0,e&&a.redraw()};c(i,a),e=N(e,!0),o?o.firePointEvent("remove",null,h):h()}remove(t,e,i,s){let r=this,o=r.chart;function n(){r.destroy(s),o.isDirtyLegend=o.isDirtyBox=!0,o.linkSeries(s),N(t,!0)&&o.redraw(e)}!1!==i?L(r,"remove",null,n):n()}update(t,e){L(this,"update",{options:t=M(t,this.userOptions)});let i=this,s=i.chart,r=i.userOptions,o=i.initialType||i.type,n=s.options.plotOptions,a=m[o].prototype,h=i.finishedAnimating&&{animation:!1},l={},d,c,p=["colorIndex","eventOptions","navigatorSeries","symbolIndex","baseSeries"],u=t.type||r.type||s.options.chart.type,g=!(this.hasDerivedData||u&&u!==this.type||void 0!==t.pointStart||void 0!==t.pointInterval||void 0!==t.relativeXValue||t.joinBy||t.mapData||["dataGrouping","pointStart","pointInterval","pointIntervalUnit","keys"].some(t=>i.hasOptionChanged(t)));u=u||o,g&&(p.push("data","isDirtyData","isDirtyCanvas","points","processedData","processedXData","processedYData","xIncrement","cropped","_hasPointMarkers","hasDataLabels","nodes","layout","level","mapMap","mapData","minY","maxY","minX","maxX","transformGroups"),!1!==t.visible&&p.push("area","graph"),i.parallelArrays.forEach(function(t){p.push(t+"Data")}),t.data&&(t.dataSorting&&A(i.options.dataSorting,t.dataSorting),this.setData(t.data,!1))),t=R(r,{index:void 0===r.index?i.index:r.index,pointStart:n?.series?.pointStart??r.pointStart??i.xData?.[0]},!g&&{data:i.options.data},t,h),g&&t.data&&(t.data=i.options.data),(p=["group","markerGroup","dataLabelsGroup","transformGroup"].concat(p)).forEach(function(t){p[t]=i[t],delete i[t]});let f=!1;if(m[u]){if(f=u!==i.type,i.remove(!1,!1,!1,!0),f){if(s.propFromSeries(),Object.setPrototypeOf)Object.setPrototypeOf(i,m[u].prototype);else{let t=Object.hasOwnProperty.call(i,"hcEvents")&&i.hcEvents;for(c in a)i[c]=void 0;A(i,m[u].prototype),t?i.hcEvents=t:delete i.hcEvents}}}else T(17,!0,s,{missingModuleFor:u});if(p.forEach(function(t){i[t]=p[t]}),i.init(s,t),g&&this.points)for(let t of(!1===(d=i.options).visible?(l.graphic=1,l.dataLabel=1):(this.hasMarkerChanged(d,r)&&(l.graphic=1),i.hasDataLabels?.()||(l.dataLabel=1)),this.points))t&&t.series&&(t.resolveColor(),Object.keys(l).length&&t.destroyElements(l),!1===d.showInLegend&&t.legendItem&&s.legend.destroyItem(t));i.initialType=o,s.linkSeries(),s.setSortedData(),f&&i.linkedSeries.length&&(i.isDirtyData=!0),L(this,"afterUpdate"),N(e,!0)&&s.redraw(!!g&&void 0)}setName(t){this.name=this.options.name=this.userOptions.name=t,this.chart.isDirtyLegend=!0}hasOptionChanged(t){let e=this.chart,i=this.options[t],s=e.options.plotOptions,r=this.userOptions[t],o=N(s?.[this.type]?.[t],s?.series?.[t]);return r&&!C(o)?i!==r:i!==N(o,i)}onMouseOver(){let t=this.chart,e=t.hoverSeries,i=t.pointer;i?.setHoverChartIndex(),e&&e!==this&&e.onMouseOut(),this.options.events.mouseOver&&L(this,"mouseOver"),this.setState("hover"),t.hoverSeries=this}onMouseOut(){let t=this.options,e=this.chart,i=e.tooltip,s=e.hoverPoint;e.hoverSeries=null,s&&s.onMouseOut(),this&&t.events.mouseOut&&L(this,"mouseOut"),i&&!this.stickyTracking&&(!i.shared||this.noSharedTooltip)&&i.hide(),e.series.forEach(function(t){t.setState("",!0)})}setState(t,e){let i=this,s=i.options,r=i.graph,o=s.inactiveOtherPoints,n=s.states,a=N(n[t||"normal"]&&n[t||"normal"].animation,i.chart.options.chart.animation),h=s.lineWidth,l=s.opacity;if(t=t||"",i.state!==t&&([i.group,i.markerGroup,i.dataLabelsGroup].forEach(function(e){e&&(i.state&&e.removeClass("highcharts-series-"+i.state),t&&e.addClass("highcharts-series-"+t))}),i.state=t,!i.chart.styledMode)){if(n[t]&&!1===n[t].enabled)return;if(t&&(h=n[t].lineWidth||h+(n[t].lineWidthPlus||0),l=N(n[t].opacity,l)),r&&!r.dashstyle&&j(h))for(let t of[r,...this.zones.map(t=>t.graph)])t?.animate({"stroke-width":h},a);o||[i.group,i.markerGroup,i.dataLabelsGroup,i.labelBySeries].forEach(function(t){t&&t.animate({opacity:l},a)})}e&&o&&i.points&&i.setAllPointsToState(t||void 0)}setAllPointsToState(t){this.points.forEach(function(e){e.setState&&e.setState(t)})}setVisible(t,e){let i=this,s=i.chart,r=s.options.chart.ignoreHiddenSeries,o=i.visible;i.visible=t=i.options.visible=i.userOptions.visible=void 0===t?!o:t;let n=t?"show":"hide";["group","dataLabelsGroup","markerGroup","tracker","tt"].forEach(t=>{i[t]?.[n]()}),(s.hoverSeries===i||s.hoverPoint?.series===i)&&i.onMouseOut(),i.legendItem&&s.legend.colorizeItem(i,t),i.isDirty=!0,i.options.stacking&&s.series.forEach(t=>{t.options.stacking&&t.visible&&(t.isDirty=!0)}),i.linkedSeries.forEach(e=>{e.setVisible(t,!1)}),r&&(s.isDirtyBox=!0),L(i,n),!1!==e&&s.redraw()}show(){this.setVisible(!0)}hide(){this.setVisible(!1)}select(t){this.selected=t=this.options.selected=void 0===t?!this.selected:t,this.checkbox&&(this.checkbox.checked=t),L(this,t?"select":"unselect")}shouldShowTooltip(t,e,i={}){return i.series=this,i.visiblePlotOnly=!0,this.chart.isInsidePlot(t,e,i)}drawLegendSymbol(t,e){r[this.options.legendSymbol||"rectangle"]?.call(this,t,e)}}return X.defaultOptions=n,X.types=a.seriesTypes,X.registerType=a.registerSeriesType,A(X.prototype,{axisTypes:["xAxis","yAxis"],coll:"series",colorCounter:0,directTouch:!1,invertible:!0,isCartesian:!0,kdAxisArray:["clientX","plotY"],parallelArrays:["x","y"],pointClass:o,requireSorting:!0,sorted:!0}),a.series=X,X}),i(e,"Core/Legend/Legend.js",[e["Core/Animation/AnimationUtilities.js"],e["Core/Foundation.js"],e["Core/Globals.js"],e["Core/Series/Series.js"],e["Core/Series/Point.js"],e["Core/Renderer/RendererUtilities.js"],e["Core/Templating.js"],e["Core/Utilities.js"]],function(t,e,i,s,r,o,n,a){var h;let{animObject:l,setAnimation:d}=t,{registerEventOptions:c}=e,{composed:p,marginNames:u}=i,{distribute:g}=o,{format:f}=n,{addEvent:m,createElement:x,css:y,defined:b,discardElement:v,find:S,fireEvent:C,isNumber:k,merge:M,pick:w,pushUnique:T,relativeLength:A,stableSort:P,syncTimeout:L}=a;class O{constructor(t,e){this.allItems=[],this.initialItemY=0,this.itemHeight=0,this.itemMarginBottom=0,this.itemMarginTop=0,this.itemX=0,this.itemY=0,this.lastItemY=0,this.lastLineHeight=0,this.legendHeight=0,this.legendWidth=0,this.maxItemWidth=0,this.maxLegendWidth=0,this.offsetWidth=0,this.padding=0,this.pages=[],this.symbolHeight=0,this.symbolWidth=0,this.titleHeight=0,this.totalItemWidth=0,this.widthOption=0,this.chart=t,this.setOptions(e),e.enabled&&(this.render(),c(this,e),m(this.chart,"endResize",function(){this.legend.positionCheckboxes()})),m(this.chart,"render",()=>{this.options.enabled&&this.proximate&&(this.proximatePositions(),this.positionItems())})}setOptions(t){let e=w(t.padding,8);this.options=t,this.chart.styledMode||(this.itemStyle=t.itemStyle,this.itemHiddenStyle=M(this.itemStyle,t.itemHiddenStyle)),this.itemMarginTop=t.itemMarginTop,this.itemMarginBottom=t.itemMarginBottom,this.padding=e,this.initialItemY=e-5,this.symbolWidth=w(t.symbolWidth,16),this.pages=[],this.proximate="proximate"===t.layout&&!this.chart.inverted,this.baseline=void 0}update(t,e){let i=this.chart;this.setOptions(M(!0,this.options,t)),"events"in this.options&&c(this,this.options),this.destroy(),i.isDirtyLegend=i.isDirtyBox=!0,w(e,!0)&&i.redraw(),C(this,"afterUpdate",{redraw:e})}colorizeItem(t,e){let{area:i,group:s,label:r,line:o,symbol:n}=t.legendItem||{};if(s?.[e?"removeClass":"addClass"]("highcharts-legend-item-hidden"),!this.chart.styledMode){let{itemHiddenStyle:s={}}=this,a=s.color,{fillColor:h,fillOpacity:l,lineColor:d,marker:c}=t.options,p=t=>(!e&&(t.fill&&(t.fill=a),t.stroke&&(t.stroke=a)),t);r?.css(M(e?this.itemStyle:s)),o?.attr(p({stroke:d||t.color})),n&&n.attr(p(c&&n.isMarker?t.pointAttribs():{fill:t.color})),i?.attr(p({fill:h||t.color,"fill-opacity":h?1:l??.75}))}C(this,"afterColorizeItem",{item:t,visible:e})}positionItems(){this.allItems.forEach(this.positionItem,this),this.chart.isResizing||this.positionCheckboxes()}positionItem(t){let{group:e,x:i=0,y:s=0}=t.legendItem||{},r=this.options,o=r.symbolPadding,n=!r.rtl,a=t.checkbox;if(e&&e.element){let r={translateX:n?i:this.legendWidth-i-2*o-4,translateY:s};e[b(e.translateY)?"animate":"attr"](r,void 0,()=>{C(this,"afterPositionItem",{item:t})})}a&&(a.x=i,a.y=s)}destroyItem(t){let e=t.checkbox,i=t.legendItem||{};for(let t of["group","label","line","symbol"])i[t]&&(i[t]=i[t].destroy());e&&v(e),t.legendItem=void 0}destroy(){for(let t of this.getAllItems())this.destroyItem(t);for(let t of["clipRect","up","down","pager","nav","box","title","group"])this[t]&&(this[t]=this[t].destroy());this.display=null}positionCheckboxes(){let t;let e=this.group&&this.group.alignAttr,i=this.clipHeight||this.legendHeight,s=this.titleHeight;e&&(t=e.translateY,this.allItems.forEach(function(r){let o;let n=r.checkbox;n&&(o=t+s+n.y+(this.scrollOffset||0)+3,y(n,{left:e.translateX+r.checkboxOffset+n.x-20+"px",top:o+"px",display:this.proximate||o>t-6&&o1.5*S?v.height:S))}layoutItem(t){let e=this.options,i=this.padding,s="horizontal"===e.layout,r=t.itemHeight,o=this.itemMarginBottom,n=this.itemMarginTop,a=s?w(e.itemDistance,20):0,h=this.maxLegendWidth,l=e.alignColumns&&this.totalItemWidth>h?this.maxItemWidth:t.itemWidth,d=t.legendItem||{};s&&this.itemX-i+l>h&&(this.itemX=i,this.lastLineHeight&&(this.itemY+=n+this.lastLineHeight+o),this.lastLineHeight=0),this.lastItemY=n+this.itemY+o,this.lastLineHeight=Math.max(r,this.lastLineHeight),d.x=this.itemX,d.y=this.itemY,s?this.itemX+=l:(this.itemY+=n+r+o,this.lastLineHeight=r),this.offsetWidth=this.widthOption||Math.max((s?this.itemX-i-(t.checkbox?0:a):l)+i,this.offsetWidth)}getAllItems(){let t=[];return this.chart.series.forEach(function(e){let i=e&&e.options;e&&w(i.showInLegend,!b(i.linkedTo)&&void 0,!0)&&(t=t.concat((e.legendItem||{}).labels||("point"===i.legendType?e.data:e)))}),C(this,"afterGetAllItems",{allItems:t}),t}getAlignment(){let t=this.options;return this.proximate?t.align.charAt(0)+"tv":t.floating?"":t.align.charAt(0)+t.verticalAlign.charAt(0)+t.layout.charAt(0)}adjustMargins(t,e){let i=this.chart,s=this.options,r=this.getAlignment();r&&[/(lth|ct|rth)/,/(rtv|rm|rbv)/,/(rbh|cb|lbh)/,/(lbv|lm|ltv)/].forEach(function(o,n){o.test(r)&&!b(t[n])&&(i[u[n]]=Math.max(i[u[n]],i.legend[(n+1)%2?"legendHeight":"legendWidth"]+[1,-1,-1,1][n]*s[n%2?"x":"y"]+w(s.margin,12)+e[n]+(i.titleOffset[n]||0)))})}proximatePositions(){let t;let e=this.chart,i=[],s="left"===this.options.align;for(let r of(this.allItems.forEach(function(t){let r,o,n=s,a,h;t.yAxis&&(t.xAxis.options.reversed&&(n=!n),t.points&&(r=S(n?t.points:t.points.slice(0).reverse(),function(t){return k(t.plotY)})),o=this.itemMarginTop+t.legendItem.label.getBBox().height+this.itemMarginBottom,h=t.yAxis.top-e.plotTop,a=t.visible?(r?r.plotY:t.yAxis.height)+(h-.3*o):h+t.yAxis.height,i.push({target:a,size:o,item:t}))},this),g(i,e.plotHeight)))t=r.item.legendItem||{},k(r.pos)&&(t.y=e.plotTop-e.spacing[0]+r.pos)}render(){let t=this.chart,e=t.renderer,i=this.options,s=this.padding,r=this.getAllItems(),o,n,a,h=this.group,l,d=this.box;this.itemX=s,this.itemY=this.initialItemY,this.offsetWidth=0,this.lastItemY=0,this.widthOption=A(i.width,t.spacingBox.width-s),l=t.spacingBox.width-2*s-i.x,["rm","lm"].indexOf(this.getAlignment().substring(0,2))>-1&&(l/=2),this.maxLegendWidth=this.widthOption||l,h||(this.group=h=e.g("legend").addClass(i.className||"").attr({zIndex:7}).add(),this.contentGroup=e.g().attr({zIndex:1}).add(h),this.scrollGroup=e.g().add(this.contentGroup)),this.renderTitle(),P(r,(t,e)=>(t.options&&t.options.legendIndex||0)-(e.options&&e.options.legendIndex||0)),i.reversed&&r.reverse(),this.allItems=r,this.display=o=!!r.length,this.lastLineHeight=0,this.maxItemWidth=0,this.totalItemWidth=0,this.itemHeight=0,r.forEach(this.renderItem,this),r.forEach(this.layoutItem,this),n=(this.widthOption||this.offsetWidth)+s,a=this.lastItemY+this.lastLineHeight+this.titleHeight,a=this.handleOverflow(a)+s,d||(this.box=d=e.rect().addClass("highcharts-legend-box").attr({r:i.borderRadius}).add(h)),t.styledMode||d.attr({stroke:i.borderColor,"stroke-width":i.borderWidth||0,fill:i.backgroundColor||"none"}).shadow(i.shadow),n>0&&a>0&&d[d.placed?"animate":"attr"](d.crisp.call({},{x:0,y:0,width:n,height:a},d.strokeWidth())),h[o?"show":"hide"](),t.styledMode&&"none"===h.getStyle("display")&&(n=a=0),this.legendWidth=n,this.legendHeight=a,o&&this.align(),this.proximate||this.positionItems(),C(this,"afterRender")}align(t=this.chart.spacingBox){let e=this.chart,i=this.options,s=t.y;/(lth|ct|rth)/.test(this.getAlignment())&&e.titleOffset[0]>0?s+=e.titleOffset[0]:/(lbh|cb|rbh)/.test(this.getAlignment())&&e.titleOffset[2]>0&&(s-=e.titleOffset[2]),s!==t.y&&(t=M(t,{y:s})),e.hasRendered||(this.group.placed=!1),this.group.align(M(i,{width:this.legendWidth,height:this.legendHeight,verticalAlign:this.proximate?"top":i.verticalAlign}),!0,t)}handleOverflow(t){let e=this,i=this.chart,s=i.renderer,r=this.options,o=r.y,n="top"===r.verticalAlign,a=this.padding,h=r.maxHeight,l=r.navigation,d=w(l.animation,!0),c=l.arrowSize||12,p=this.pages,u=this.allItems,g=function(t){"number"==typeof t?S.attr({height:t}):S&&(e.clipRect=S.destroy(),e.contentGroup.clip()),e.contentGroup.div&&(e.contentGroup.div.style.clip=t?"rect("+a+"px,9999px,"+(a+t)+"px,0)":"auto")},f=function(t){return e[t]=s.circle(0,0,1.3*c).translate(c/2,c/2).add(v),i.styledMode||e[t].attr("fill","rgba(0,0,0,0.0001)"),e[t]},m,x,y,b=i.spacingBox.height+(n?-o:o)-a,v=this.nav,S=this.clipRect;return"horizontal"!==r.layout||"middle"===r.verticalAlign||r.floating||(b/=2),h&&(b=Math.min(b,h)),p.length=0,t&&b>0&&t>b&&!1!==l.enabled?(this.clipHeight=m=Math.max(b-20-this.titleHeight-a,0),this.currentPage=w(this.currentPage,1),this.fullHeight=t,u.forEach((t,e)=>{let i=(y=t.legendItem||{}).y||0,s=Math.round(y.label.getBBox().height),r=p.length;(!r||i-p[r-1]>m&&(x||i)!==p[r-1])&&(p.push(x||i),r++),y.pageIx=r-1,x&&((u[e-1].legendItem||{}).pageIx=r-1),e===u.length-1&&i+s-p[r-1]>m&&i>p[r-1]&&(p.push(i),y.pageIx=r),i!==x&&(x=i)}),S||(S=e.clipRect=s.clipRect(0,a-2,9999,0),e.contentGroup.clip(S)),g(m),v||(this.nav=v=s.g().attr({zIndex:1}).add(this.group),this.up=s.symbol("triangle",0,0,c,c).add(v),f("upTracker").on("click",function(){e.scroll(-1,d)}),this.pager=s.text("",15,10).addClass("highcharts-legend-navigation"),!i.styledMode&&l.style&&this.pager.css(l.style),this.pager.add(v),this.down=s.symbol("triangle-down",0,0,c,c).add(v),f("downTracker").on("click",function(){e.scroll(1,d)})),e.scroll(0),t=b):v&&(g(),this.nav=v.destroy(),this.scrollGroup.attr({translateY:1}),this.clipHeight=0),t}scroll(t,e){let i=this.chart,s=this.pages,r=s.length,o=this.clipHeight,n=this.options.navigation,a=this.pager,h=this.padding,c=this.currentPage+t;c>r&&(c=r),c>0&&(void 0!==e&&d(e,i),this.nav.attr({translateX:h,translateY:o+this.padding+7+this.titleHeight,visibility:"inherit"}),[this.up,this.upTracker].forEach(function(t){t.attr({class:1===c?"highcharts-legend-nav-inactive":"highcharts-legend-nav-active"})}),a.attr({text:c+"/"+r}),[this.down,this.downTracker].forEach(function(t){t.attr({x:18+this.pager.getBBox().width,class:c===r?"highcharts-legend-nav-inactive":"highcharts-legend-nav-active"})},this),i.styledMode||(this.up.attr({fill:1===c?n.inactiveColor:n.activeColor}),this.upTracker.css({cursor:1===c?"default":"pointer"}),this.down.attr({fill:c===r?n.inactiveColor:n.activeColor}),this.downTracker.css({cursor:c===r?"default":"pointer"})),this.scrollOffset=-s[c-1]+this.initialItemY,this.scrollGroup.animate({translateY:this.scrollOffset}),this.currentPage=c,this.positionCheckboxes(),L(()=>{C(this,"afterScroll",{currentPage:c})},l(w(e,i.renderer.globalAnimation,!0)).duration))}setItemEvents(t,e,i){let o=this,n=t.legendItem||{},a=o.chart.renderer.boxWrapper,h=t instanceof r,l=t instanceof s,d="highcharts-legend-"+(h?"point":"series")+"-active",c=o.chart.styledMode,p=i?[e,n.symbol]:[n.group],u=e=>{o.allItems.forEach(i=>{t!==i&&[i].concat(i.linkedSeries||[]).forEach(t=>{t.setState(e,!h)})})};for(let i of p)i&&i.on("mouseover",function(){t.visible&&u("inactive"),t.setState("hover"),t.visible&&a.addClass(d),c||e.css(o.options.itemHoverStyle)}).on("mouseout",function(){o.chart.styledMode||e.css(M(t.visible?o.itemStyle:o.itemHiddenStyle)),u(""),a.removeClass(d),t.setState()}).on("click",function(e){let i=function(){t.setVisible&&t.setVisible(),u(t.visible?"inactive":"")};a.removeClass(d),C(o,"itemClick",{browserEvent:e,legendItem:t},i),h?t.firePointEvent("legendItemClick",{browserEvent:e}):l&&C(t,"legendItemClick",{browserEvent:e})})}createCheckboxForItem(t){t.checkbox=x("input",{type:"checkbox",className:"highcharts-legend-checkbox",checked:t.selected,defaultChecked:t.selected},this.options.itemCheckboxStyle,this.chart.container),m(t.checkbox,"click",function(e){let i=e.target;C(t.series||t,"checkboxClick",{checked:i.checked,item:t},function(){t.select()})})}}return(h=O||(O={})).compose=function(t){T(p,"Core.Legend")&&m(t,"beforeMargins",function(){this.legend=new h(this,this.options.legend)})},O}),i(e,"Core/Chart/Chart.js",[e["Core/Animation/AnimationUtilities.js"],e["Core/Axis/Axis.js"],e["Core/Defaults.js"],e["Core/Templating.js"],e["Core/Foundation.js"],e["Core/Globals.js"],e["Core/Renderer/RendererRegistry.js"],e["Core/Series/Series.js"],e["Core/Series/SeriesRegistry.js"],e["Core/Renderer/SVG/SVGRenderer.js"],e["Core/Time.js"],e["Core/Utilities.js"],e["Core/Renderer/HTML/AST.js"],e["Core/Axis/Tick.js"]],function(t,e,i,s,r,o,n,a,h,l,d,c,p,u){let{animate:g,animObject:f,setAnimation:m}=t,{defaultOptions:x,defaultTime:y}=i,{numberFormat:b}=s,{registerEventOptions:v}=r,{charts:S,doc:C,marginNames:k,svg:M,win:w}=o,{seriesTypes:T}=h,{addEvent:A,attr:P,createElement:L,css:O,defined:D,diffObjects:E,discardElement:I,erase:j,error:B,extend:R,find:z,fireEvent:N,getStyle:W,isArray:G,isNumber:H,isObject:X,isString:F,merge:Y,objectEach:U,pick:V,pInt:$,relativeLength:Z,removeEvent:_,splat:q,syncTimeout:K,uniqueKey:J}=c;class Q{static chart(t,e,i){return new Q(t,e,i)}constructor(t,e,i){this.sharedClips={};let s=[...arguments];(F(t)||t.nodeName)&&(this.renderTo=s.shift()),this.init(s[0],s[1])}setZoomOptions(){let t=this.options.chart,e=t.zooming;this.zooming={...e,type:V(t.zoomType,e.type),key:V(t.zoomKey,e.key),pinchType:V(t.pinchType,e.pinchType),singleTouch:V(t.zoomBySingleTouch,e.singleTouch,!1),resetButton:Y(e.resetButton,t.resetZoomButton)}}init(t,e){N(this,"init",{args:arguments},function(){let i=Y(x,t),s=i.chart;this.userOptions=R({},t),this.margin=[],this.spacing=[],this.labelCollectors=[],this.callback=e,this.isResizing=0,this.options=i,this.axes=[],this.series=[],this.time=t.time&&Object.keys(t.time).length?new d(t.time):o.time,this.numberFormatter=s.numberFormatter||b,this.styledMode=s.styledMode,this.hasCartesianSeries=s.showAxes,this.index=S.length,S.push(this),o.chartCount++,v(this,s),this.xAxis=[],this.yAxis=[],this.pointCount=this.colorCounter=this.symbolCounter=0,this.setZoomOptions(),N(this,"afterInit"),this.firstRender()})}initSeries(t){let e=this.options.chart,i=t.type||e.type,s=T[i];s||B(17,!0,this,{missingModuleFor:i});let r=new s;return"function"==typeof r.init&&r.init(this,t),r}setSortedData(){this.getSeriesOrderByLinks().forEach(function(t){t.points||t.data||!t.enabledDataSorting||t.setData(t.options.data,!1)})}getSeriesOrderByLinks(){return this.series.concat().sort(function(t,e){return t.linkedSeries.length||e.linkedSeries.length?e.linkedSeries.length-t.linkedSeries.length:0})}orderItems(t,e=0){let i=this[t],s=this.options[t]=q(this.options[t]).slice(),r=this.userOptions[t]=this.userOptions[t]?q(this.userOptions[t]).slice():[];if(this.hasRendered&&(s.splice(e),r.splice(e)),i)for(let t=e,o=i.length;t=Math.max(h+o,t.pos)&&e<=Math.min(h+o+c.width,t.pos+t.len)||(g.isInsidePlot=!1)}if(!i.ignoreY&&g.isInsidePlot){let t=!s&&i.axis&&!i.axis.isXAxis&&i.axis||d&&(s?d.xAxis:d.yAxis)||{pos:n,len:1/0},e=i.paneCoordinates?t.pos+u:n+u;e>=Math.max(l+n,t.pos)&&e<=Math.min(l+n+c.height,t.pos+t.len)||(g.isInsidePlot=!1)}return N(this,"afterIsInsidePlot",g),g.isInsidePlot}redraw(t){N(this,"beforeRedraw");let e=this.hasCartesianSeries?this.axes:this.colorAxis||[],i=this.series,s=this.pointer,r=this.legend,o=this.userOptions.legend,n=this.renderer,a=n.isHidden(),h=[],l,d,c,p=this.isDirtyBox,u=this.isDirtyLegend,g;for(n.rootFontSize=n.boxWrapper.getStyle("font-size"),this.setResponsive&&this.setResponsive(!1),m(!!this.hasRendered&&t,this),a&&this.temporaryDisplay(),this.layOutTitles(!1),c=i.length;c--;)if(((g=i[c]).options.stacking||g.options.centerInCategory)&&(d=!0,g.isDirty)){l=!0;break}if(l)for(c=i.length;c--;)(g=i[c]).options.stacking&&(g.isDirty=!0);i.forEach(function(t){t.isDirty&&("point"===t.options.legendType?("function"==typeof t.updateTotals&&t.updateTotals(),u=!0):o&&(o.labelFormatter||o.labelFormat)&&(u=!0)),t.isDirtyData&&N(t,"updatedData")}),u&&r&&r.options.enabled&&(r.render(),this.isDirtyLegend=!1),d&&this.getStacks(),e.forEach(function(t){t.updateNames(),t.setScale()}),this.getMargins(),e.forEach(function(t){t.isDirty&&(p=!0)}),e.forEach(function(t){let e=t.min+","+t.max;t.extKey!==e&&(t.extKey=e,h.push(function(){N(t,"afterSetExtremes",R(t.eventArgs,t.getExtremes())),delete t.eventArgs})),(p||d)&&t.redraw()}),p&&this.drawChartBox(),N(this,"predraw"),i.forEach(function(t){(p||t.isDirty)&&t.visible&&t.redraw(),t.isDirtyData=!1}),s&&s.reset(!0),n.draw(),N(this,"redraw"),N(this,"render"),a&&this.temporaryDisplay(!0),h.forEach(function(t){t.call()})}get(t){let e=this.series;function i(e){return e.id===t||e.options&&e.options.id===t}let s=z(this.axes,i)||z(this.series,i);for(let t=0;!s&&t(e.getPointsCollection().forEach(e=>{V(e.selectedStaging,e.selected)&&t.push(e)}),t),[])}getSelectedSeries(){return this.series.filter(function(t){return t.selected})}setTitle(t,e,i){this.applyDescription("title",t),this.applyDescription("subtitle",e),this.applyDescription("caption",void 0),this.layOutTitles(i)}applyDescription(t,e){let i=this,s=this.options[t]=Y(this.options[t],e),r=this[t];r&&e&&(this[t]=r=r.destroy()),s&&!r&&((r=this.renderer.text(s.text,0,0,s.useHTML).attr({align:s.align,class:"highcharts-"+t,zIndex:s.zIndex||4}).add()).update=function(e,s){i.applyDescription(t,e),i.layOutTitles(s)},this.styledMode||r.css(R("title"===t?{fontSize:this.options.isStock?"1em":"1.2em"}:{},s.style)),this[t]=r)}layOutTitles(t=!0){let e=[0,0,0],i=this.renderer,s=this.spacingBox;["title","subtitle","caption"].forEach(function(t){let r=this[t],o=this.options[t],n=o.verticalAlign||"top",a="title"===t?"top"===n?-3:0:"top"===n?e[0]+2:0;if(r){r.css({width:(o.width||s.width+(o.widthAdjust||0))+"px"});let t=i.fontMetrics(r).b,h=Math.round(r.getBBox(o.useHTML).height);r.align(R({y:"bottom"===n?t:a+t,height:h},o),!1,"spacingBox"),o.floating||("top"===n?e[0]=Math.ceil(e[0]+h):"bottom"===n&&(e[2]=Math.ceil(e[2]+h)))}},this),e[0]&&"top"===(this.options.title.verticalAlign||"top")&&(e[0]+=this.options.title.margin),e[2]&&"bottom"===this.options.caption.verticalAlign&&(e[2]+=this.options.caption.margin);let r=!this.titleOffset||this.titleOffset.join(",")!==e.join(",");this.titleOffset=e,N(this,"afterLayOutTitles"),!this.isDirtyBox&&r&&(this.isDirtyBox=this.isDirtyLegend=r,this.hasRendered&&t&&this.isDirtyBox&&this.redraw())}getContainerBox(){let t=[].map.call(this.renderTo.children,t=>{if(t!==this.container){let e=t.style.display;return t.style.display="none",[t,e]}}),e={width:W(this.renderTo,"width",!0)||0,height:W(this.renderTo,"height",!0)||0};return t.filter(Boolean).forEach(([t,e])=>{t.style.display=e}),e}getChartSize(){let t=this.options.chart,e=t.width,i=t.height,s=this.getContainerBox(),r=s.height>1&&!(!this.renderTo.parentElement?.style.height&&"100%"===this.renderTo.style.height);this.chartWidth=Math.max(0,e||s.width||600),this.chartHeight=Math.max(0,Z(i,this.chartWidth)||(r?s.height:400)),this.containerBox=s}temporaryDisplay(t){let e=this.renderTo,i;if(t)for(;e&&e.style;)e.hcOrigStyle&&(O(e,e.hcOrigStyle),delete e.hcOrigStyle),e.hcOrigDetached&&(C.body.removeChild(e),e.hcOrigDetached=!1),e=e.parentNode;else for(;e&&e.style&&(C.body.contains(e)||e.parentNode||(e.hcOrigDetached=!0,C.body.appendChild(e)),("none"===W(e,"display",!1)||e.hcOricDetached)&&(e.hcOrigStyle={display:e.style.display,height:e.style.height,overflow:e.style.overflow},i={display:"block",overflow:"hidden"},e!==this.renderTo&&(i.height=0),O(e,i),e.offsetWidth||e.style.setProperty("display","block","important")),(e=e.parentNode)!==C.body););}setClassName(t){this.container.className="highcharts-container "+(t||"")}getContainer(){let t=this.options,e=t.chart,i="data-highcharts-chart",s=J(),r,o=this.renderTo;o||(this.renderTo=o=e.renderTo),F(o)&&(this.renderTo=o=C.getElementById(o)),o||B(13,!0,this);let a=$(P(o,i));H(a)&&S[a]&&S[a].hasRendered&&S[a].destroy(),P(o,i,this.index),o.innerHTML=p.emptyHTML,e.skipClone||o.offsetWidth||this.temporaryDisplay(),this.getChartSize();let h=this.chartHeight,d=this.chartWidth;O(o,{overflow:"hidden"}),this.styledMode||(r=R({position:"relative",overflow:"hidden",width:d+"px",height:h+"px",textAlign:"left",lineHeight:"normal",zIndex:0,"-webkit-tap-highlight-color":"rgba(0,0,0,0)",userSelect:"none","touch-action":"manipulation",outline:"none",padding:"0px"},e.style||{}));let c=L("div",{id:s},r,o);this.container=c,this.getChartSize(),d===this.chartWidth||(d=this.chartWidth,this.styledMode||O(c,{width:V(e.style?.width,d+"px")})),this.containerBox=this.getContainerBox(),this._cursor=c.style.cursor;let u=e.renderer||!M?n.getRendererType(e.renderer):l;if(this.renderer=new u(c,d,h,void 0,e.forExport,t.exporting&&t.exporting.allowHTML,this.styledMode),m(void 0,this),this.setClassName(e.className),this.styledMode)for(let e in t.defs)this.renderer.definition(t.defs[e]);else this.renderer.setStyle(e.style);this.renderer.chartIndex=this.index,N(this,"afterGetContainer")}getMargins(t){let{spacing:e,margin:i,titleOffset:s}=this;this.resetMargins(),s[0]&&!D(i[0])&&(this.plotTop=Math.max(this.plotTop,s[0]+e[0])),s[2]&&!D(i[2])&&(this.marginBottom=Math.max(this.marginBottom,s[2]+e[2])),this.legend&&this.legend.display&&this.legend.adjustMargins(i,e),N(this,"getMargins"),t||this.getAxisMargins()}getAxisMargins(){let t=this,e=t.axisOffset=[0,0,0,0],i=t.colorAxis,s=t.margin,r=function(t){t.forEach(function(t){t.visible&&t.getOffset()})};t.hasCartesianSeries?r(t.axes):i&&i.length&&r(i),k.forEach(function(i,r){D(s[r])||(t[i]+=e[r])}),t.setChartSize()}getOptions(){return E(this.userOptions,x)}reflow(t){let e=this,i=e.containerBox,s=e.getContainerBox();delete e.pointer?.chartPosition,!e.isPrinting&&!e.isResizing&&i&&s.width&&((s.width!==i.width||s.height!==i.height)&&(c.clearTimeout(e.reflowTimeout),e.reflowTimeout=K(function(){e.container&&e.setSize(void 0,void 0,!1)},t?100:0)),e.containerBox=s)}setReflow(){let t=this,e=e=>{t.options?.chart.reflow&&t.hasLoaded&&t.reflow(e)};if("function"==typeof ResizeObserver)new ResizeObserver(e).observe(t.renderTo);else{let t=A(w,"resize",e);A(this,"destroy",t)}}setSize(t,e,i){let s=this,r=s.renderer;s.isResizing+=1,m(i,s);let o=r.globalAnimation;s.oldChartHeight=s.chartHeight,s.oldChartWidth=s.chartWidth,void 0!==t&&(s.options.chart.width=t),void 0!==e&&(s.options.chart.height=e),s.getChartSize();let{chartWidth:n,chartHeight:a,scrollablePixelsX:h=0,scrollablePixelsY:l=0}=s;(s.isDirtyBox||n!==s.oldChartWidth||a!==s.oldChartHeight)&&(s.styledMode||(o?g:O)(s.container,{width:`${n+h}px`,height:`${a+l}px`},o),s.setChartSize(!0),r.setSize(n,a,o),s.axes.forEach(function(t){t.isDirty=!0,t.setScale()}),s.isDirtyLegend=!0,s.isDirtyBox=!0,s.layOutTitles(),s.getMargins(),s.redraw(o),s.oldChartHeight=void 0,N(s,"resize"),setTimeout(()=>{s&&N(s,"endResize")},f(o).duration)),s.isResizing-=1}setChartSize(t){let e,i,s,r;let{chartHeight:o,chartWidth:n,inverted:a,spacing:h,renderer:l}=this,d=this.clipOffset,c=Math[a?"floor":"round"];this.plotLeft=e=Math.round(this.plotLeft),this.plotTop=i=Math.round(this.plotTop),this.plotWidth=s=Math.max(0,Math.round(n-e-this.marginRight)),this.plotHeight=r=Math.max(0,Math.round(o-i-this.marginBottom)),this.plotSizeX=a?r:s,this.plotSizeY=a?s:r,this.spacingBox=l.spacingBox={x:h[3],y:h[0],width:n-h[3]-h[1],height:o-h[0]-h[2]},this.plotBox=l.plotBox={x:e,y:i,width:s,height:r},d&&(this.clipBox={x:c(d[3]),y:c(d[0]),width:c(this.plotSizeX-d[1]-d[3]),height:c(this.plotSizeY-d[0]-d[2])}),t||(this.axes.forEach(function(t){t.setAxisSize(),t.setAxisTranslation()}),l.alignElements()),N(this,"afterSetChartSize",{skipAxes:t})}resetMargins(){N(this,"resetMargins");let t=this,e=t.options.chart,i=e.plotBorderWidth||0,s=i/2;["margin","spacing"].forEach(function(i){let s=e[i],r=X(s)?s:[s,s,s,s];["Top","Right","Bottom","Left"].forEach(function(s,o){t[i][o]=V(e[i+s],r[o])})}),k.forEach(function(e,i){t[e]=V(t.margin[i],t.spacing[i])}),t.axisOffset=[0,0,0,0],t.clipOffset=[s,s,s,s],t.plotBorderWidth=i}drawChartBox(){let t=this.options.chart,e=this.renderer,i=this.chartWidth,s=this.chartHeight,r=this.styledMode,o=this.plotBGImage,n=t.backgroundColor,a=t.plotBackgroundColor,h=t.plotBackgroundImage,l=this.plotLeft,d=this.plotTop,c=this.plotWidth,p=this.plotHeight,u=this.plotBox,g=this.clipRect,f=this.clipBox,m=this.chartBackground,x=this.plotBackground,y=this.plotBorder,b,v,S,C="animate";m||(this.chartBackground=m=e.rect().addClass("highcharts-background").add(),C="attr"),r?b=v=m.strokeWidth():(v=(b=t.borderWidth||0)+(t.shadow?8:0),S={fill:n||"none"},(b||m["stroke-width"])&&(S.stroke=t.borderColor,S["stroke-width"]=b),m.attr(S).shadow(t.shadow)),m[C]({x:v/2,y:v/2,width:i-v-b%2,height:s-v-b%2,r:t.borderRadius}),C="animate",x||(C="attr",this.plotBackground=x=e.rect().addClass("highcharts-plot-background").add()),x[C](u),!r&&(x.attr({fill:a||"none"}).shadow(t.plotShadow),h&&(o?(h!==o.attr("href")&&o.attr("href",h),o.animate(u)):this.plotBGImage=e.image(h,l,d,c,p).add())),g?g.animate({width:f.width,height:f.height}):this.clipRect=e.clipRect(f),C="animate",y||(C="attr",this.plotBorder=y=e.rect().addClass("highcharts-plot-border").attr({zIndex:1}).add()),r||y.attr({stroke:t.plotBorderColor,"stroke-width":t.plotBorderWidth||0,fill:"none"}),y[C](y.crisp({x:l,y:d,width:c,height:p},-y.strokeWidth())),this.isDirtyBox=!1,N(this,"afterDrawChartBox")}propFromSeries(){let t,e,i;let s=this,r=s.options.chart,o=s.options.series;["inverted","angular","polar"].forEach(function(n){for(e=T[r.type],i=r[n]||e&&e.prototype[n],t=o&&o.length;!i&&t--;)(e=T[o[t].type])&&e.prototype[n]&&(i=!0);s[n]=i})}linkSeries(t){let e=this,i=e.series;i.forEach(function(t){t.linkedSeries.length=0}),i.forEach(function(t){let{linkedTo:i}=t.options;if(F(i)){let s;(s=":previous"===i?e.series[t.index-1]:e.get(i))&&s.linkedParent!==t&&(s.linkedSeries.push(t),t.linkedParent=s,s.enabledDataSorting&&t.setDataSortingOptions(),t.visible=V(t.options.visible,s.options.visible,t.visible))}}),N(this,"afterLinkSeries",{isUpdating:t})}renderSeries(){this.series.forEach(function(t){t.translate(),t.render()})}render(){let t=this.axes,e=this.colorAxis,i=this.renderer,s=this.options.chart.axisLayoutRuns||2,r=t=>{t.forEach(t=>{t.visible&&t.render()})},o=0,n=!0,a,h=0;for(let e of(this.setTitle(),N(this,"beforeMargins"),this.getStacks?.(),this.getMargins(!0),this.setChartSize(),t)){let{options:t}=e,{labels:i}=t;if(this.hasCartesianSeries&&e.horiz&&e.visible&&i.enabled&&e.series.length&&"colorAxis"!==e.coll&&!this.polar){o=t.tickLength,e.createGroups();let s=new u(e,0,"",!0),r=s.createLabel("x",i);if(s.destroy(),r&&V(i.reserveSpace,!H(t.crossing))&&(o=r.getBBox().height+i.distance+Math.max(t.offset||0,0)),o){r?.destroy();break}}}for(this.plotHeight=Math.max(this.plotHeight-o,0);(n||a||s>1)&&h(h?1:1.1),a=i/this.plotHeight>(h?1:1.05),h++}this.drawChartBox(),this.hasCartesianSeries?r(t):e&&e.length&&r(e),this.seriesGroup||(this.seriesGroup=i.g("series-group").attr({zIndex:3}).shadow(this.options.chart.seriesGroupShadow).add()),this.renderSeries(),this.addCredits(),this.setResponsive&&this.setResponsive(),this.hasRendered=!0}addCredits(t){let e=this,i=Y(!0,this.options.credits,t);i.enabled&&!this.credits&&(this.credits=this.renderer.text(i.text+(this.mapCredits||""),0,0).addClass("highcharts-credits").on("click",function(){i.href&&(w.location.href=i.href)}).attr({align:i.position.align,zIndex:8}),e.styledMode||this.credits.css(i.style),this.credits.add().align(i.position),this.credits.update=function(t){e.credits=e.credits.destroy(),e.addCredits(t)})}destroy(){let t;let e=this,i=e.axes,s=e.series,r=e.container,n=r&&r.parentNode;for(N(e,"destroy"),e.renderer.forExport?j(S,e):S[e.index]=void 0,o.chartCount--,e.renderTo.removeAttribute("data-highcharts-chart"),_(e),t=i.length;t--;)i[t]=i[t].destroy();for(this.scroller&&this.scroller.destroy&&this.scroller.destroy(),t=s.length;t--;)s[t]=s[t].destroy();["title","subtitle","chartBackground","plotBackground","plotBGImage","plotBorder","seriesGroup","clipRect","credits","pointer","rangeSelector","legend","resetZoomButton","tooltip","renderer"].forEach(function(t){let i=e[t];i&&i.destroy&&(e[t]=i.destroy())}),r&&(r.innerHTML=p.emptyHTML,_(r),n&&I(r)),U(e,function(t,i){delete e[i]})}firstRender(){let t=this,e=t.options;t.getContainer(),t.resetMargins(),t.setChartSize(),t.propFromSeries(),t.getAxes();let i=G(e.series)?e.series:[];e.series=[],i.forEach(function(e){t.initSeries(e)}),t.linkSeries(),t.setSortedData(),N(t,"beforeRender"),t.render(),t.pointer?.getChartPosition(),t.renderer.imgCount||t.hasLoaded||t.onload(),t.temporaryDisplay(!0)}onload(){this.callbacks.concat([this.callback]).forEach(function(t){t&&void 0!==this.index&&t.apply(this,[this])},this),N(this,"load"),N(this,"render"),D(this.index)&&this.setReflow(),this.warnIfA11yModuleNotLoaded(),this.hasLoaded=!0}warnIfA11yModuleNotLoaded(){let{options:t,title:e}=this;!t||this.accessibility||(this.renderer.boxWrapper.attr({role:"img","aria-label":(e&&e.element.textContent||"").replace(/this.transform({reset:!0,trigger:"zoom"}))}pan(t,e){let i=this,s="object"==typeof e?e:{enabled:e,type:"x"},r=s.type,o=r&&i[({x:"xAxis",xy:"axes",y:"yAxis"})[r]].filter(t=>t.options.panningEnabled&&!t.options.isInternal),n=i.options.chart;n?.panning&&(n.panning=s),N(this,"pan",{originalEvent:t},()=>{i.transform({axes:o,event:t,to:{x:t.chartX-(i.mouseDownX||0),y:t.chartY-(i.mouseDownY||0)},trigger:"pan"}),O(i.container,{cursor:"move"})})}transform(t){let{axes:e=this.axes,event:i,from:s={},reset:r,selection:o,to:n={},trigger:a}=t,{inverted:h}=this,l=!1,d,c;for(let t of(this.hoverPoints?.forEach(t=>t.setState()),e)){let{horiz:e,len:p,minPointOffset:u=0,options:g,reversed:f}=t,m=e?"width":"height",x=e?"x":"y",y=V(n[m],t.len),b=V(s[m],t.len),v=10>Math.abs(y)?1:y/b,S=(s[x]||0)+b/2-t.pos,C=S-((n[x]??t.pos)+y/2-t.pos)/v,k=f&&!h||!f&&h?-1:1;if(!r&&(S<0||S>t.len))continue;let M=t.toValue(C,!0)+(o||t.isOrdinal?0:u*k),w=t.toValue(C+p/v,!0)-(o||t.isOrdinal?0:u*k||0),T=t.allExtremes;if(M>w&&([M,w]=[w,M]),1===v&&!r&&"yAxis"===t.coll&&!T){for(let e of t.series){let t=e.getExtremes(e.getProcessedData(!0).yData,!0);T??(T={dataMin:Number.MAX_VALUE,dataMax:-Number.MAX_VALUE}),H(t.dataMin)&&H(t.dataMax)&&(T.dataMin=Math.min(t.dataMin,T.dataMin),T.dataMax=Math.max(t.dataMax,T.dataMax))}t.allExtremes=T}let{dataMin:A,dataMax:P,min:L,max:O}=R(t.getExtremes(),T||{}),E=A??g.min,I=P??g.max,j=w-M,B=t.categories?0:Math.min(j,I-E),z=E-B*(D(g.min)?0:g.minPadding),N=I+B*(D(g.max)?0:g.maxPadding),W=t.allowZoomOutside||1===v||"zoom"!==a&&v>1,G=Math.min(g.min??z,z,W?L:z),X=Math.max(g.max??N,N,W?O:N);(!t.isOrdinal||t.options.overscroll||1!==v||r)&&(M=1&&(w=M+j)),w>X&&(w=X,v>=1&&(M=w-j)),(r||t.series.length&&(M!==L||w!==O)&&M>=G&&w<=X)&&(o?o[t.coll].push({axis:t,min:M,max:w}):(t.isPanning="zoom"!==a,t.isPanning&&(c=!0),t.setExtremes(r?void 0:M,r?void 0:w,!1,!1,{move:C,trigger:a,scale:v}),!r&&(M>G||w{delete t.selection,t.trigger="zoom",this.transform(t)}):(!d||c||this.resetZoomButton?!d&&this.resetZoomButton&&(this.resetZoomButton=this.resetZoomButton.destroy()):this.showResetZoom(),this.redraw("zoom"===a&&(this.options.chart.animation??this.pointCount<100)))),l}}return R(Q.prototype,{callbacks:[],collectionsWithInit:{xAxis:[Q.prototype.addAxis,[!0]],yAxis:[Q.prototype.addAxis,[!1]],series:[Q.prototype.addSeries]},collectionsWithUpdate:["xAxis","yAxis","series"],propsRequireDirtyBox:["backgroundColor","borderColor","borderWidth","borderRadius","plotBackgroundColor","plotBackgroundImage","plotBorderColor","plotBorderWidth","plotShadow","shadow"],propsRequireReflow:["margin","marginTop","marginRight","marginBottom","marginLeft","spacing","spacingTop","spacingRight","spacingBottom","spacingLeft"],propsRequireUpdateSeries:["chart.inverted","chart.polar","chart.ignoreHiddenSeries","chart.type","colors","plotOptions","time","tooltip"]}),Q}),i(e,"Extensions/ScrollablePlotArea.js",[e["Core/Animation/AnimationUtilities.js"],e["Core/Globals.js"],e["Core/Renderer/RendererRegistry.js"],e["Core/Utilities.js"]],function(t,e,i,s){let{stop:r}=t,{composed:o}=e,{addEvent:n,createElement:a,css:h,defined:l,merge:d,pushUnique:c}=s;function p(){let t=this.scrollablePlotArea;(this.scrollablePixelsX||this.scrollablePixelsY)&&!t&&(this.scrollablePlotArea=t=new g(this)),t?.applyFixed()}function u(){this.chart.scrollablePlotArea&&(this.chart.scrollablePlotArea.isDirty=!0)}class g{static compose(t,e,i){c(o,this.compose)&&(n(t,"afterInit",u),n(e,"afterSetChartSize",t=>this.afterSetSize(t.target,t)),n(e,"render",p),n(i,"show",u))}static afterSetSize(t,e){let i,s,r;let{minWidth:o,minHeight:n}=t.options.chart.scrollablePlotArea||{},{clipBox:a,plotBox:h,inverted:c,renderer:p}=t;if(!p.forExport&&(o?(t.scrollablePixelsX=i=Math.max(0,o-t.chartWidth),i&&(t.scrollablePlotBox=d(t.plotBox),h.width=t.plotWidth+=i,a[c?"height":"width"]+=i,r=!0)):n&&(t.scrollablePixelsY=s=Math.max(0,n-t.chartHeight),l(s)&&(t.scrollablePlotBox=d(t.plotBox),h.height=t.plotHeight+=s,a[c?"width":"height"]+=s,r=!1)),l(r)&&!e.skipAxes))for(let e of t.axes)e.horiz===r&&(e.setAxisSize(),e.setAxisTranslation())}constructor(t){let e;let s=t.options.chart,r=i.getRendererType(),o=s.scrollablePlotArea||{},l=this.moveFixedElements.bind(this),d={WebkitOverflowScrolling:"touch",overflowX:"hidden",overflowY:"hidden"};t.scrollablePixelsX&&(d.overflowX="auto"),t.scrollablePixelsY&&(d.overflowY="auto"),this.chart=t;let c=this.parentDiv=a("div",{className:"highcharts-scrolling-parent"},{position:"relative"},t.renderTo),p=this.scrollingContainer=a("div",{className:"highcharts-scrolling"},d,c),u=this.innerContainer=a("div",{className:"highcharts-inner-container"},void 0,p),g=this.fixedDiv=a("div",{className:"highcharts-fixed"},{position:"absolute",overflow:"hidden",pointerEvents:"none",zIndex:(s.style?.zIndex||0)+2,top:0},void 0,!0),f=this.fixedRenderer=new r(g,t.chartWidth,t.chartHeight,s.style);this.mask=f.path().attr({fill:s.backgroundColor||"#fff","fill-opacity":o.opacity??.85,zIndex:-1}).addClass("highcharts-scrollable-mask").add(),p.parentNode.insertBefore(g,p),h(t.renderTo,{overflow:"visible"}),n(t,"afterShowResetZoom",l),n(t,"afterApplyDrilldown",l),n(t,"afterLayOutTitles",l),n(p,"scroll",()=>{let{pointer:i,hoverPoint:s}=t;i&&(delete i.chartPosition,s&&(e=s),i.runPointActions(void 0,e,!0))}),u.appendChild(t.container)}applyFixed(){let{chart:t,fixedRenderer:e,isDirty:i,scrollingContainer:s}=this,{axisOffset:o,chartWidth:n,chartHeight:a,container:d,plotHeight:c,plotLeft:p,plotTop:u,plotWidth:g,scrollablePixelsX:f=0,scrollablePixelsY:m=0}=t,{scrollPositionX:x=0,scrollPositionY:y=0}=t.options.chart.scrollablePlotArea||{},b=n+f,v=a+m;e.setSize(n,a),(i??!0)&&(this.isDirty=!1,this.moveFixedElements()),r(t.container),h(d,{width:`${b}px`,height:`${v}px`}),t.renderer.boxWrapper.attr({width:b,height:v,viewBox:[0,0,b,v].join(" ")}),t.chartBackground?.attr({width:b,height:v}),h(s,{width:`${n}px`,height:`${a}px`}),l(i)||(s.scrollLeft=f*x,s.scrollTop=m*y);let S=u-o[0]-1,C=p-o[3]-1,k=u+c+o[2]+1,M=p+g+o[1]+1,w=p+g-f,T=u+c-m,A=[["M",0,0]];f?A=[["M",0,S],["L",p-1,S],["L",p-1,k],["L",0,k],["Z"],["M",w,S],["L",n,S],["L",n,k],["L",w,k],["Z"]]:m&&(A=[["M",C,0],["L",C,u-1],["L",M,u-1],["L",M,0],["Z"],["M",C,T],["L",C,a],["L",M,a],["L",M,T],["Z"]]),"adjustHeight"!==t.redrawTrigger&&this.mask.attr({d:A})}moveFixedElements(){let t;let{container:e,inverted:i,scrollablePixelsX:s,scrollablePixelsY:r}=this.chart,o=this.fixedRenderer,n=g.fixedSelectors;for(let a of(s&&!i?t=".highcharts-yaxis":s&&i?t=".highcharts-xaxis":r&&!i?t=".highcharts-xaxis":r&&i&&(t=".highcharts-yaxis"),t&&n.push(`${t}:not(.highcharts-radial-axis)`,`${t}-labels:not(.highcharts-radial-axis-labels)`),n))[].forEach.call(e.querySelectorAll(a),t=>{(t.namespaceURI===o.SVG_NS?o.box:o.box.parentNode).appendChild(t),t.style.pointerEvents="auto"})}}return g.fixedSelectors=[".highcharts-breadcrumbs-group",".highcharts-contextbutton",".highcharts-caption",".highcharts-credits",".highcharts-drillup-button",".highcharts-legend",".highcharts-legend-checkbox",".highcharts-navigator-series",".highcharts-navigator-xaxis",".highcharts-navigator-yaxis",".highcharts-navigator",".highcharts-range-selector-group",".highcharts-reset-zoom",".highcharts-scrollbar",".highcharts-subtitle",".highcharts-title"],g}),i(e,"Core/Axis/Stacking/StackItem.js",[e["Core/Templating.js"],e["Core/Series/SeriesRegistry.js"],e["Core/Utilities.js"]],function(t,e,i){let{format:s}=t,{series:r}=e,{destroyObjectProperties:o,fireEvent:n,isNumber:a,pick:h}=i;return class{constructor(t,e,i,s,r){let o=t.chart.inverted,n=t.reversed;this.axis=t;let a=this.isNegative=!!i!=!!n;this.options=e=e||{},this.x=s,this.total=null,this.cumulative=null,this.points={},this.hasValidPoints=!1,this.stack=r,this.leftCliff=0,this.rightCliff=0,this.alignOptions={align:e.align||(o?a?"left":"right":"center"),verticalAlign:e.verticalAlign||(o?"middle":a?"bottom":"top"),y:e.y,x:e.x},this.textAlign=e.textAlign||(o?a?"right":"left":"center")}destroy(){o(this,this.axis)}render(t){let e=this.axis.chart,i=this.options,r=i.format,o=r?s(r,this,e):i.formatter.call(this);if(this.label)this.label.attr({text:o,visibility:"hidden"});else{this.label=e.renderer.label(o,null,void 0,i.shape,void 0,void 0,i.useHTML,!1,"stack-labels");let s={r:i.borderRadius||0,text:o,padding:h(i.padding,5),visibility:"hidden"};e.styledMode||(s.fill=i.backgroundColor,s.stroke=i.borderColor,s["stroke-width"]=i.borderWidth,this.label.css(i.style||{})),this.label.attr(s),this.label.added||this.label.add(t)}this.label.labelrank=e.plotSizeY,n(this,"afterRender")}setOffset(t,e,i,s,o,l){let{alignOptions:d,axis:c,label:p,options:u,textAlign:g}=this,f=c.chart,m=this.getStackBox({xOffset:t,width:e,boxBottom:i,boxTop:s,defaultX:o,xAxis:l}),{verticalAlign:x}=d;if(p&&m){let t=p.getBBox(void 0,0),e=p.padding,i="justify"===h(u.overflow,"justify"),s;d.x=u.x||0,d.y=u.y||0;let{x:o,y:n}=this.adjustStackPosition({labelBox:t,verticalAlign:x,textAlign:g});m.x-=o,m.y-=n,p.align(d,!1,m),(s=f.isInsidePlot(p.alignAttr.x+d.x+o,p.alignAttr.y+d.y+n))||(i=!1),i&&r.prototype.justifyDataLabel.call(c,p,d,p.alignAttr,t,m),p.attr({x:p.alignAttr.x,y:p.alignAttr.y,rotation:u.rotation,rotationOriginX:t.width*({left:0,center:.5,right:1})[u.textAlign||"center"],rotationOriginY:t.height/2}),h(!i&&u.crop,!0)&&(s=a(p.x)&&a(p.y)&&f.isInsidePlot(p.x-e+(p.width||0),p.y)&&f.isInsidePlot(p.x+e,p.y)),p[s?"show":"hide"]()}n(this,"afterSetOffset",{xOffset:t,width:e})}adjustStackPosition({labelBox:t,verticalAlign:e,textAlign:i}){let s={bottom:0,middle:1,top:2,right:1,center:0,left:-1},r=s[e],o=s[i];return{x:t.width/2+t.width/2*o,y:t.height/2*r}}getStackBox(t){let e=this.axis,i=e.chart,{boxTop:s,defaultX:r,xOffset:o,width:n,boxBottom:l}=t,d=e.stacking.usePercentage?100:h(s,this.total,0),c=e.toPixels(d),p=t.xAxis||i.xAxis[0],u=h(r,p.translate(this.x))+o,g=Math.abs(c-e.toPixels(l||a(e.min)&&e.logarithmic&&e.logarithmic.lin2log(e.min)||0)),f=i.inverted,m=this.isNegative;return f?{x:(m?c:c-g)-i.plotLeft,y:p.height-u-n+p.top-i.plotTop,width:g,height:n}:{x:u+p.transB-i.plotLeft,y:(m?c-g:c)-i.plotTop,width:n,height:g}}}}),i(e,"Core/Axis/Stacking/StackingAxis.js",[e["Core/Animation/AnimationUtilities.js"],e["Core/Axis/Axis.js"],e["Core/Series/SeriesRegistry.js"],e["Core/Axis/Stacking/StackItem.js"],e["Core/Utilities.js"]],function(t,e,i,s,r){var o;let{getDeferredAnimation:n}=t,{series:{prototype:a}}=i,{addEvent:h,correctFloat:l,defined:d,destroyObjectProperties:c,fireEvent:p,isArray:u,isNumber:g,objectEach:f,pick:m}=r;function x(){let t=this.inverted;this.axes.forEach(t=>{t.stacking&&t.stacking.stacks&&t.hasVisibleSeries&&(t.stacking.oldStacks=t.stacking.stacks)}),this.series.forEach(e=>{let i=e.xAxis&&e.xAxis.options||{};e.options.stacking&&e.reserveSpace()&&(e.stackKey=[e.type,m(e.options.stack,""),t?i.top:i.left,t?i.height:i.width].join(","))})}function y(){let t=this.stacking;if(t){let e=t.stacks;f(e,(t,i)=>{c(t),delete e[i]}),t.stackTotalGroup?.destroy()}}function b(){this.stacking||(this.stacking=new w(this))}function v(t,e,i,s){return!d(t)||t.x!==e||s&&t.stackKey!==s?t={x:e,index:0,key:s,stackKey:s}:t.index++,t.key=[i,e,t.index].join(","),t}function S(){let t;let e=this,i=e.yAxis,s=e.stackKey||"",r=i.stacking.stacks,o=e.processedXData,n=e.options.stacking,a=e[n+"Stacker"];a&&[s,"-"+s].forEach(i=>{let s=o.length,n,h,l;for(;s--;)n=o[s],t=e.getStackIndicator(t,n,e.index,i),h=r[i]?.[n],(l=h?.points[t.key||""])&&a.call(e,l,h,s)})}function C(t,e,i){let s=e.total?100/e.total:0;t[0]=l(t[0]*s),t[1]=l(t[1]*s),this.stackedYData[i]=t[1]}function k(t){(this.is("column")||this.is("columnrange"))&&(this.options.centerInCategory&&!this.options.stacking&&this.chart.series.length>1?a.setStackedPoints.call(this,t,"group"):t.stacking.resetStacks())}function M(t,e){let i,r,o,n,a,h,c,p,g;let f=e||this.options.stacking;if(!f||!this.reserveSpace()||(({group:"xAxis"})[f]||"yAxis")!==t.coll)return;let x=this.processedXData,y=this.processedYData,b=[],v=y.length,S=this.options,C=S.threshold||0,k=S.startFromThreshold?C:0,M=S.stack,w=e?`${this.type},${f}`:this.stackKey||"",T="-"+w,A=this.negStacks,P=t.stacking,L=P.stacks,O=P.oldStacks;for(P.stacksTouched+=1,c=0;c0&&!1===this.singleStacks&&(o.points[h][0]=o.points[this.index+","+p+",0"][0])):(delete o.points[h],delete o.points[this.index]);let e=o.total||0;"percent"===f?(n=r?w:T,e=A&&L[n]?.[p]?(n=L[n][p]).total=Math.max(n.total||0,e)+Math.abs(g)||0:l(e+(Math.abs(g)||0))):"group"===f?(u(g)&&(g=g[0]),null!==g&&e++):e=l(e+(g||0)),"group"===f?o.cumulative=(e||1)-1:o.cumulative=l(m(o.cumulative,k)+(g||0)),o.total=e,null!==g&&(o.points[h].push(o.cumulative),b[c]=o.cumulative,o.hasValidPoints=!0)}"percent"===f&&(P.usePercentage=!0),"group"!==f&&(this.stackedYData=b),P.oldStacks={}}class w{constructor(t){this.oldStacks={},this.stacks={},this.stacksTouched=0,this.axis=t}buildStacks(){let t,e;let i=this.axis,s=i.series,r="xAxis"===i.coll,o=i.options.reversedStacks,n=s.length;for(this.resetStacks(),this.usePercentage=!1,e=n;e--;)t=s[o?e:n-e-1],r&&t.setGroupedPoints(i),t.setStackedPoints(i);if(!r)for(e=0;e{f(t,t=>{t.cumulative=t.total})}))}resetStacks(){f(this.stacks,t=>{f(t,(e,i)=>{g(e.touched)&&e.touched{f(t,t=>{t.render(o)})}),o.animate({opacity:1},r)}}return(o||(o={})).compose=function(t,e,i){let s=e.prototype,r=i.prototype;s.getStacks||(h(t,"init",b),h(t,"destroy",y),s.getStacks=x,r.getStackIndicator=v,r.modifyStacks=S,r.percentStacker=C,r.setGroupedPoints=k,r.setStackedPoints=M)},o}),i(e,"Series/Line/LineSeries.js",[e["Core/Series/Series.js"],e["Core/Series/SeriesRegistry.js"],e["Core/Utilities.js"]],function(t,e,i){let{defined:s,merge:r,isObject:o}=i;class n extends t{drawGraph(){let t=this.options,e=(this.gappedPath||this.getGraphPath).call(this),i=this.chart.styledMode;[this,...this.zones].forEach((s,n)=>{let a,h=s.graph,l=h?"animate":"attr",d=s.dashStyle||t.dashStyle;h?(h.endX=this.preventGraphAnimation?null:e.xMap,h.animate({d:e})):e.length&&(s.graph=h=this.chart.renderer.path(e).addClass("highcharts-graph"+(n?` highcharts-zone-graph-${n-1} `:" ")+(n&&s.className||"")).attr({zIndex:1}).add(this.group)),h&&!i&&(a={stroke:!n&&t.lineColor||s.color||this.color||"#cccccc","stroke-width":t.lineWidth||0,fill:this.fillGraph&&this.color||"none"},d?a.dashstyle=d:"square"!==t.linecap&&(a["stroke-linecap"]=a["stroke-linejoin"]="round"),h[l](a).shadow(n<2&&t.shadow&&r({filterUnits:"userSpaceOnUse"},o(t.shadow)?t.shadow:{}))),h&&(h.startX=e.xMap,h.isArea=e.isArea)})}getGraphPath(t,e,i){let r=this,o=r.options,n=[],a=[],h,l=o.step,d=(t=t||r.points).reversed;return d&&t.reverse(),(l=({right:1,center:2})[l]||l&&3)&&d&&(l=4-l),(t=this.getValidPoints(t,!1,!(o.connectNulls&&!e&&!i))).forEach(function(d,c){let p;let u=d.plotX,g=d.plotY,f=t[c-1],m=d.isNull||"number"!=typeof g;(d.leftCliff||f&&f.rightCliff)&&!i&&(h=!0),m&&!s(e)&&c>0?h=!o.connectNulls:m&&!e?h=!0:(0===c||h?p=[["M",d.plotX,d.plotY]]:r.getPointSpline?p=[r.getPointSpline(t,d,c)]:l?(p=1===l?[["L",f.plotX,g]]:2===l?[["L",(f.plotX+u)/2,f.plotY],["L",(f.plotX+u)/2,g]]:[["L",u,f.plotY]]).push(["L",u,g]):p=[["L",u,g]],a.push(d.x),l&&(a.push(d.x),2===l&&a.push(d.x)),n.push.apply(n,p),h=!1)}),n.xMap=a,r.graphPath=n,n}}return n.defaultOptions=r(t.defaultOptions,{legendSymbol:"lineMarker"}),e.registerSeriesType("line",n),n}),i(e,"Series/Area/AreaSeriesDefaults.js",[],function(){return{threshold:0,legendSymbol:"areaMarker"}}),i(e,"Series/Area/AreaSeries.js",[e["Series/Area/AreaSeriesDefaults.js"],e["Core/Series/SeriesRegistry.js"],e["Core/Utilities.js"]],function(t,e,i){let{seriesTypes:{line:s}}=e,{extend:r,merge:o,objectEach:n,pick:a}=i;class h extends s{drawGraph(){this.areaPath=[],super.drawGraph.apply(this);let{areaPath:t,options:e}=this;[this,...this.zones].forEach((i,s)=>{let r={},o=i.fillColor||e.fillColor,n=i.area,a=n?"animate":"attr";n?(n.endX=this.preventGraphAnimation?null:t.xMap,n.animate({d:t})):(r.zIndex=0,(n=i.area=this.chart.renderer.path(t).addClass("highcharts-area"+(s?` highcharts-zone-area-${s-1} `:" ")+(s&&i.className||"")).add(this.group)).isArea=!0),this.chart.styledMode||(r.fill=o||i.color||this.color,r["fill-opacity"]=o?1:e.fillOpacity??.75,n.css({pointerEvents:this.stickyTracking?"none":"auto"})),n[a](r),n.startX=t.xMap,n.shiftUnit=e.step?2:1})}getGraphPath(t){let e,i,r;let o=s.prototype.getGraphPath,n=this.options,h=n.stacking,l=this.yAxis,d=[],c=[],p=this.index,u=l.stacking.stacks[this.stackKey],g=n.threshold,f=Math.round(l.getThreshold(n.threshold)),m=a(n.connectNulls,"percent"===h),x=function(i,s,r){let o=t[i],n=h&&u[o.x].points[p],a=o[r+"Null"]||0,m=o[r+"Cliff"]||0,x,y,b=!0;m||a?(x=(a?n[0]:n[1])+m,y=n[0]+m,b=!!a):!h&&t[s]&&t[s].isNull&&(x=y=g),void 0!==x&&(c.push({plotX:e,plotY:null===x?f:l.getThreshold(x),isNull:b,isCliff:!0}),d.push({plotX:e,plotY:null===y?f:l.getThreshold(y),doCurve:!1}))};t=t||this.points,h&&(t=this.getStackPoints(t));for(let s=0,o=t.length;s1&&h&&c.some(t=>t.isCliff)&&(S.hasStackedCliffs=C.hasStackedCliffs=!0),S.xMap=y.xMap,this.areaPath=S,C}getStackPoints(t){let e=this,i=[],s=[],r=this.xAxis,o=this.yAxis,h=o.stacking.stacks[this.stackKey],l={},d=o.series,c=d.length,p=o.options.reversedStacks?1:-1,u=d.indexOf(e);if(t=t||this.points,this.options.stacking){for(let e=0;et.visible);s.forEach(function(t,n){let f=0,m,x;if(l[t]&&!l[t].isNull)i.push(l[t]),[-1,1].forEach(function(i){let r=1===i?"rightNull":"leftNull",o=h[s[n+i]],a=0;if(o){let i=u;for(;i>=0&&i=0&&ei&&o>l?(o=Math.max(i,l),a=2*l-o):op&&a>l?(a=Math.max(p,l),o=2*l-a):a1){let r=this.xAxis.series.filter(t=>t.visible).map(t=>t.index),o=0,n=0;b(this.xAxis.stacking?.stacks,t=>{if("number"==typeof i.x){let e=t[i.x.toString()];if(e&&f(e.points[this.index])){let t=Object.keys(e.points).filter(t=>!t.match(",")&&e.points[t]&&e.points[t].length>1).map(parseFloat).filter(t=>-1!==r.indexOf(t)).sort((t,e)=>e-t);o=t.indexOf(this.index),n=t.length}}}),o=this.xAxis.reversed?n-1-o:o;let a=(n-1)*s.paddedWidth+e;t=(i.plotX||0)+a/2-e-o*s.paddedWidth}return t}translate(){let t=this,e=t.chart,i=t.options,s=t.dense=t.closestPointRange*t.xAxis.transA<2,o=t.borderWidth=y(i.borderWidth,s?0:1),n=t.xAxis,a=t.yAxis,h=i.threshold,l=y(i.minPointLength,5),c=t.getColumnMetrics(),u=c.width,f=t.pointXOffset=c.offset,x=t.dataMin,b=t.dataMax,v=t.translatedThreshold=a.getThreshold(h),S=t.barW=Math.max(u,1+2*o);i.pointPadding&&(S=Math.ceil(S)),r.prototype.translate.apply(t),t.points.forEach(function(s){let r=y(s.yBottom,v),o=999+Math.abs(r),g=s.plotX||0,C=d(s.plotY,-o,a.len+o),k,M=Math.min(C,r),w=Math.max(C,r)-M,T=u,A=g+f,P=S;l&&Math.abs(w)l?r-l:v-(k?l:0)),p(s.options.pointWidth)&&(A-=Math.round(((T=P=Math.ceil(s.options.pointWidth))-u)/2)),i.centerInCategory&&!i.stacking&&(A=t.adjustForMissingColumns(A,T,s,c)),s.barX=A,s.pointWidth=T,s.tooltipPos=e.inverted?[d(a.len+a.pos-e.plotLeft-C,a.pos-e.plotLeft,a.len+a.pos-e.plotLeft),n.len+n.pos-e.plotTop-A-P/2,w]:[n.left-e.plotLeft+A+P/2,d(C+a.pos-e.plotTop,a.pos-e.plotTop,a.len+a.pos-e.plotTop),w],s.shapeType=t.pointClass.prototype.shapeType||"roundedRect",s.shapeArgs=t.crispCol(A,s.isNull?v:M,P,s.isNull?0:w)}),g(this,"afterColumnTranslate")}drawGraph(){this.group[this.dense?"addClass":"removeClass"]("highcharts-dense-data")}pointAttribs(t,e){let i=this.options,s=this.pointAttrToOptions||{},r=s.stroke||"borderColor",o=s["stroke-width"]||"borderWidth",n,a,l,d=t&&t.color||this.color,c=t&&t[r]||i[r]||d,p=t&&t.options.dashStyle||i.dashStyle,u=t&&t[o]||i[o]||this[o]||0,g=y(t&&t.opacity,i.opacity,1);t&&this.zones.length&&(a=t.getZone(),d=t.options.color||a&&(a.color||t.nonZonedColor)||this.color,a&&(c=a.borderColor||c,p=a.dashStyle||p,u=a.borderWidth||u)),e&&t&&(l=(n=x(i.states[e],t.options.states&&t.options.states[e]||{})).brightness,d=n.color||void 0!==l&&h(d).brighten(n.brightness).get()||d,c=n[r]||c,u=n[o]||u,p=n.dashStyle||p,g=y(n.opacity,g));let f={fill:d,stroke:c,"stroke-width":u,opacity:g};return p&&(f.dashstyle=p),f}drawPoints(t=this.points){let e;let i=this,s=this.chart,r=i.options,o=s.renderer,n=r.animationLimit||250;t.forEach(function(t){let a=t.plotY,h=t.graphic,l=!!h,d=h&&s.pointCountt?.enabled)}function i(t,e,i,s,r){let{chart:o,enabledDataSorting:h}=this,l=this.isCartesian&&o.inverted,d=t.plotX,p=t.plotY,g=i.rotation||0,f=n(d)&&n(p)&&o.isInsidePlot(d,Math.round(p),{inverted:l,paneCoordinates:!0,series:this}),m=0===g&&"justify"===u(i.overflow,h?"none":"justify"),x=this.visible&&!1!==t.visible&&n(d)&&(t.series.forceDL||h&&!m||f||u(i.inside,!!this.options.stacking)&&s&&o.isInsidePlot(d,l?s.x+1:s.y+s.height-1,{inverted:l,paneCoordinates:!0,series:this})),y=t.pos();if(x&&y){var b;let n=e.getBBox(),d=e.getBBox(void 0,0),p={right:1,center:.5}[i.align||0]||0,v={bottom:1,middle:.5}[i.verticalAlign||0]||0;if(s=a({x:y[0],y:Math.round(y[1]),width:0,height:0},s||{}),"plotEdges"===i.alignTo&&this.isCartesian&&(s[l?"x":"y"]=0,s[l?"width":"height"]=this.yAxis?.len||0),a(i,{width:n.width,height:n.height}),b=s,h&&this.xAxis&&!m&&this.setDataLabelStartPos(t,e,r,f,b),e.align(c(i,{width:d.width,height:d.height}),!1,s,!1),e.alignAttr.x+=p*(d.width-n.width),e.alignAttr.y+=v*(d.height-n.height),e[e.placed?"animate":"attr"]({x:e.alignAttr.x+(n.width-d.width)/2,y:e.alignAttr.y+(n.height-d.height)/2,rotationOriginX:(e.width||0)/2,rotationOriginY:(e.height||0)/2}),m&&s.height>=0)this.justifyDataLabel(e,i,e.alignAttr,n,s,r);else if(u(i.crop,!0)){let{x:t,y:i}=e.alignAttr;x=o.isInsidePlot(t,i,{paneCoordinates:!0,series:this})&&o.isInsidePlot(t+n.width-1,i+n.height-1,{paneCoordinates:!0,series:this})}i.shape&&!g&&e[r?"attr":"animate"]({anchorX:y[0],anchorY:y[1]})}r&&h&&(e.placed=!1),x||h&&!m?(e.show(),e.placed=!0):(e.hide(),e.placed=!1)}function s(){return this.plotGroup("dataLabelsGroup","data-labels",this.hasRendered?"inherit":"hidden",this.options.dataLabels.zIndex||6)}function m(t){let e=this.hasRendered||0,i=this.initDataLabelsGroup().attr({opacity:+e});return!e&&i&&(this.visible&&i.show(),this.options.animation?i.animate({opacity:1},t):i.attr({opacity:1})),i}function x(t){let e;t=t||this.points;let i=this,s=i.chart,a=i.options,l=s.renderer,{backgroundColor:c,plotBackgroundColor:m}=s.options.chart,x=l.getContrast(d(m)&&m||d(c)&&c||"#000000"),y=v(i),{animation:S,defer:C}=y[0],k=C?r(s,S,i):{defer:0,duration:0};h(this,"drawDataLabels"),i.hasDataLabels?.()&&(e=this.initDataLabels(k),t.forEach(t=>{let r=t.dataLabels||[];f(b(y,t.dlOptions||t.options?.dataLabels)).forEach((c,f)=>{let m=c.enabled&&(t.visible||t.dataLabelOnHidden)&&(!t.isNull||t.dataLabelOnNull)&&function(t,e){let i=e.filter;if(i){let e=i.operator,s=t[i.property],r=i.value;return">"===e&&s>r||"<"===e&&s="===e&&s>=r||"<="===e&&s<=r||"=="===e&&s==r||"==="===e&&s===r||"!="===e&&s!=r||"!=="===e&&s!==r}return!0}(t,c),{backgroundColor:y,borderColor:b,distance:v,style:S={}}=c,C,k,M,w,T={},A=r[f],P=!A,L;m&&(k=u(c[t.formatPrefix+"Format"],c.format),C=t.getLabelConfig(),M=n(k)?o(k,C,s):(c[t.formatPrefix+"Formatter"]||c.formatter).call(C,c),w=c.rotation,!s.styledMode&&(S.color=u(c.color,S.color,d(i.color)?i.color:void 0,"#000000"),"contrast"===S.color?("none"!==y&&(L=y),t.contrastColor=l.getContrast("auto"!==L&&L||t.color||i.color),S.color=L||!n(v)&&c.inside||0>g(v||0)||a.stacking?t.contrastColor:x):delete t.contrastColor,a.cursor&&(S.cursor=a.cursor)),T={r:c.borderRadius||0,rotation:w,padding:c.padding,zIndex:1},s.styledMode||(T.fill="auto"===y?t.color:y,T.stroke="auto"===b?t.color:b,T["stroke-width"]=c.borderWidth),p(T,(t,e)=>{void 0===t&&delete T[e]})),!A||m&&n(M)&&!!A.div==!!c.useHTML&&(A.rotation&&c.rotation||A.rotation===c.rotation)||(A=void 0,P=!0),m&&n(M)&&(A?T.text=M:(A=l.label(M,0,0,c.shape,void 0,void 0,c.useHTML,void 0,"data-label")).addClass(" highcharts-data-label-color-"+t.colorIndex+" "+(c.className||"")+(c.useHTML?" highcharts-tracker":"")),A&&(A.options=c,A.attr(T),s.styledMode?S.width&&A.css({width:S.width,textOverflow:S.textOverflow}):A.css(S).shadow(c.shadow),h(A,"beforeAddingDataLabel",{labelOptions:c,point:t}),A.added||A.add(e),i.alignDataLabel(t,A,c,void 0,P),A.isActive=!0,r[f]&&r[f]!==A&&r[f].destroy(),r[f]=A))});let c=r.length;for(;c--;)r[c]&&r[c].isActive?r[c].isActive=!1:(r[c]?.destroy(),r.splice(c,1));t.dataLabel=r[0],t.dataLabels=r})),h(this,"afterDrawDataLabels")}function y(t,e,i,s,r,o){let n=this.chart,a=e.align,h=e.verticalAlign,l=t.box?0:t.padding||0,d=n.inverted?this.yAxis:this.xAxis,c=d?d.left-n.plotLeft:0,p=n.inverted?this.xAxis:this.yAxis,u=p?p.top-n.plotTop:0,{x:g=0,y:f=0}=e,m,x;return(m=(i.x||0)+l+c)<0&&("right"===a&&g>=0?(e.align="left",e.inside=!0):g-=m,x=!0),(m=(i.x||0)+s.width-l+c)>n.plotWidth&&("left"===a&&g<=0?(e.align="right",e.inside=!0):g+=n.plotWidth-m,x=!0),(m=i.y+l+u)<0&&("bottom"===h&&f>=0?(e.verticalAlign="top",e.inside=!0):f-=m,x=!0),(m=(i.y||0)+s.height-l+u)>n.plotHeight&&("top"===h&&f<=0?(e.verticalAlign="bottom",e.inside=!0):f+=n.plotHeight-m,x=!0),x&&(e.x=g,e.y=f,t.placed=!o,t.align(e,void 0,r)),x}function b(t,e){let i=[],s;if(l(t)&&!l(e))i=t.map(function(t){return c(t,e)});else if(l(e)&&!l(t))i=e.map(function(e){return c(t,e)});else if(l(t)||l(e)){if(l(t)&&l(e))for(s=Math.max(t.length,e.length);s--;)i[s]=c(t[s],e[s])}else i=c(t,e);return i}function v(t){let e=t.chart.options.plotOptions;return f(b(b(e?.series?.dataLabels,e?.[t.type]?.dataLabels),t.options.dataLabels))}function S(t,e,i,s,r){let o=this.chart,n=o.inverted,a=this.xAxis,h=a.reversed,l=((n?e.height:e.width)||0)/2,d=t.pointWidth,c=d?d/2:0;e.startXPos=n?r.x:h?-l-c:a.width-l+c,e.startYPos=n?h?this.yAxis.height-l+c:-l-c:r.y,s?"hidden"===e.visibility&&(e.show(),e.attr({opacity:0}).animate({opacity:1})):e.attr({opacity:1}).animate({opacity:0},void 0,e.hide),o.hasRendered&&(i&&e.attr({x:e.startXPos,y:e.startYPos}),e.placed=!0)}t.compose=function(t){let r=t.prototype;r.initDataLabels||(r.initDataLabels=m,r.initDataLabelsGroup=s,r.alignDataLabel=i,r.drawDataLabels=x,r.justifyDataLabel=y,r.setDataLabelStartPos=S,r.hasDataLabels=e)}}(s||(s={})),s}),i(e,"Series/Column/ColumnDataLabel.js",[e["Core/Series/DataLabel.js"],e["Core/Globals.js"],e["Core/Series/SeriesRegistry.js"],e["Core/Utilities.js"]],function(t,e,i,s){var r;let{composed:o}=e,{series:n}=i,{merge:a,pick:h,pushUnique:l}=s;return function(e){function i(t,e,i,s,r){let o=this.chart.inverted,l=t.series,d=(l.xAxis?l.xAxis.len:this.chart.plotSizeX)||0,c=(l.yAxis?l.yAxis.len:this.chart.plotSizeY)||0,p=t.dlBox||t.shapeArgs,u=h(t.below,t.plotY>h(this.translatedThreshold,c)),g=h(i.inside,!!this.options.stacking);if(p){if(s=a(p),!("allow"===i.overflow&&!1===i.crop)){s.y<0&&(s.height+=s.y,s.y=0);let t=s.y+s.height-c;t>0&&t {series.name}
    ',pointFormat:"x: {point.x}
    y: {point.y}
    "}}}),i(e,"Series/Scatter/ScatterSeries.js",[e["Series/Scatter/ScatterSeriesDefaults.js"],e["Core/Series/SeriesRegistry.js"],e["Core/Utilities.js"]],function(t,e,i){let{column:s,line:r}=e.seriesTypes,{addEvent:o,extend:n,merge:a}=i;class h extends r{applyJitter(){let t=this,e=this.options.jitter,i=this.points.length;e&&this.points.forEach(function(s,r){["x","y"].forEach(function(o,n){if(e[o]&&!s.isNull){let a=`plot${o.toUpperCase()}`,h=t[`${o}Axis`],l=e[o]*h.transA;if(h&&!h.logarithmic){let t=Math.max(0,(s[a]||0)-l),e=Math.min(h.len,(s[a]||0)+l);s[a]=t+(e-t)*function(t){let e=1e4*Math.sin(t);return e-Math.floor(e)}(r+n*i),"x"===o&&(s.clientX=s.plotX)}}})})}drawGraph(){this.options.lineWidth?super.drawGraph():this.graph&&(this.graph=this.graph.destroy())}}return h.defaultOptions=a(r.defaultOptions,t),n(h.prototype,{drawTracker:s.prototype.drawTracker,sorted:!1,requireSorting:!1,noSharedTooltip:!0,trackerGroups:["group","markerGroup","dataLabelsGroup"]}),o(h,"afterTranslate",function(){this.applyJitter()}),e.registerSeriesType("scatter",h),h}),i(e,"Series/CenteredUtilities.js",[e["Core/Globals.js"],e["Core/Series/Series.js"],e["Core/Utilities.js"]],function(t,e,i){var s,r;let{deg2rad:o}=t,{fireEvent:n,isNumber:a,pick:h,relativeLength:l}=i;return(r=s||(s={})).getCenter=function(){let t=this.options,i=this.chart,s=2*(t.slicedOffset||0),r=i.plotWidth-2*s,o=i.plotHeight-2*s,d=t.center,c=Math.min(r,o),p=t.thickness,u,g=t.size,f=t.innerSize||0,m,x;"string"==typeof g&&(g=parseFloat(g)),"string"==typeof f&&(f=parseFloat(f));let y=[h(d[0],"50%"),h(d[1],"50%"),h(g&&g<0?void 0:t.size,"100%"),h(f&&f<0?void 0:t.innerSize||0,"0%")];for(!i.angular||this instanceof e||(y[3]=0),m=0;m<4;++m)x=y[m],u=m<2||2===m&&/%$/.test(x),y[m]=l(x,[r,o,c,y[2]][m])+(u?s:0);return y[3]>y[2]&&(y[3]=y[2]),a(p)&&2*p0&&(y[3]=y[2]-2*p),n(this,"afterGetCenter",{positions:y}),y},r.getStartAndEndRadians=function(t,e){let i=a(t)?t:0,s=a(e)&&e>i&&e-i<360?e:i+360;return{start:o*(i+-90),end:o*(s+-90)}},s}),i(e,"Series/Pie/PiePoint.js",[e["Core/Animation/AnimationUtilities.js"],e["Core/Series/Point.js"],e["Core/Utilities.js"]],function(t,e,i){let{setAnimation:s}=t,{addEvent:r,defined:o,extend:n,isNumber:a,pick:h,relativeLength:l}=i;class d extends e{getConnectorPath(t){let e=t.dataLabelPosition,i=t.options||{},s=i.connectorShape,r=this.connectorShapes[s]||s;return e&&r.call(this,{...e.computed,alignment:e.alignment},e.connectorPosition,i)||[]}getTranslate(){return this.sliced&&this.slicedTranslation||{translateX:0,translateY:0}}haloPath(t){let e=this.shapeArgs;return this.sliced||!this.visible?[]:this.series.chart.renderer.symbols.arc(e.x,e.y,e.r+t,e.r+t,{innerR:e.r-1,start:e.start,end:e.end,borderRadius:e.borderRadius})}constructor(t,e,i){super(t,e,i),this.half=0,this.name??(this.name="Slice");let s=t=>{this.slice("select"===t.type)};r(this,"select",s),r(this,"unselect",s)}isValid(){return a(this.y)&&this.y>=0}setVisible(t,e=!0){t!==this.visible&&this.update({visible:t??!this.visible},e,void 0,!1)}slice(t,e,i){let r=this.series;s(i,r.chart),e=h(e,!0),this.sliced=this.options.sliced=t=o(t)?t:!this.sliced,r.options.data[r.data.indexOf(this)]=this.options,this.graphic&&this.graphic.animate(this.getTranslate())}}return n(d.prototype,{connectorShapes:{fixedOffset:function(t,e,i){let s=e.breakAt,r=e.touchingSliceAt,o=i.softConnector?["C",t.x+("left"===t.alignment?-5:5),t.y,2*s.x-r.x,2*s.y-r.y,s.x,s.y]:["L",s.x,s.y];return[["M",t.x,t.y],o,["L",r.x,r.y]]},straight:function(t,e){let i=e.touchingSliceAt;return[["M",t.x,t.y],["L",i.x,i.y]]},crookedLine:function(t,e,i){let{breakAt:s,touchingSliceAt:r}=e,{series:o}=this,[n,a,h]=o.center,d=h/2,{plotLeft:c,plotWidth:p}=o.chart,u="left"===t.alignment,{x:g,y:f}=t,m=s.x;if(i.crookDistance){let t=l(i.crookDistance,1);m=u?n+d+(p+c-n-d)*(1-t):c+(n-d)*t}else m=n+(a-f)*Math.tan((this.angle||0)-Math.PI/2);let x=[["M",g,f]];return(u?m<=g&&m>=s.x:m>=g&&m<=s.x)&&x.push(["L",m,f]),x.push(["L",s.x,s.y],["L",r.x,r.y]),x}}}),d}),i(e,"Series/Pie/PieSeriesDefaults.js",[],function(){return{borderRadius:3,center:[null,null],clip:!1,colorByPoint:!0,dataLabels:{connectorPadding:5,connectorShape:"crookedLine",crookDistance:void 0,distance:30,enabled:!0,formatter:function(){return this.point.isNull?void 0:this.point.name},softConnector:!0,x:0},fillColor:void 0,ignoreHiddenPoint:!0,inactiveOtherPoints:!0,legendType:"point",marker:null,size:null,showInLegend:!1,slicedOffset:10,stickyTracking:!1,tooltip:{followPointer:!0},borderColor:"#ffffff",borderWidth:1,lineWidth:void 0,states:{hover:{brightness:.1}}}}),i(e,"Series/Pie/PieSeries.js",[e["Series/CenteredUtilities.js"],e["Series/Column/ColumnSeries.js"],e["Core/Globals.js"],e["Series/Pie/PiePoint.js"],e["Series/Pie/PieSeriesDefaults.js"],e["Core/Series/Series.js"],e["Core/Series/SeriesRegistry.js"],e["Core/Renderer/SVG/Symbols.js"],e["Core/Utilities.js"]],function(t,e,i,s,r,o,n,a,h){let{getStartAndEndRadians:l}=t,{noop:d}=i,{clamp:c,extend:p,fireEvent:u,merge:g,pick:f}=h;class m extends o{animate(t){let e=this,i=e.points,s=e.startAngleRad;t||i.forEach(function(t){let i=t.graphic,r=t.shapeArgs;i&&r&&(i.attr({r:f(t.startR,e.center&&e.center[3]/2),start:s,end:s}),i.animate({r:r.r,start:r.start,end:r.end},e.options.animation))})}drawEmpty(){let t,e;let i=this.startAngleRad,s=this.endAngleRad,r=this.options;0===this.total&&this.center?(t=this.center[0],e=this.center[1],this.graph||(this.graph=this.chart.renderer.arc(t,e,this.center[1]/2,0,i,s).addClass("highcharts-empty-series").add(this.group)),this.graph.attr({d:a.arc(t,e,this.center[2]/2,0,{start:i,end:s,innerR:this.center[3]/2})}),this.chart.styledMode||this.graph.attr({"stroke-width":r.borderWidth,fill:r.fillColor||"none",stroke:r.color||"#cccccc"})):this.graph&&(this.graph=this.graph.destroy())}drawPoints(){let t=this.chart.renderer;this.points.forEach(function(e){e.graphic&&e.hasNewShapeType()&&(e.graphic=e.graphic.destroy()),e.graphic||(e.graphic=t[e.shapeType](e.shapeArgs).add(e.series.group),e.delayedRendering=!0)})}generatePoints(){super.generatePoints(),this.updateTotals()}getX(t,e,i,s){let r=this.center,o=this.radii?this.radii[i.index]||0:r[2]/2,n=s.dataLabelPosition,a=n?.distance||0,h=Math.asin(c((t-r[1])/(o+a),-1,1));return r[0]+Math.cos(h)*(o+a)*(e?-1:1)+(a>0?(e?-1:1)*(s.padding||0):0)}hasData(){return!!this.processedXData.length}redrawPoints(){let t,e,i,s;let r=this,o=r.chart;this.drawEmpty(),r.group&&!o.styledMode&&r.group.shadow(r.options.shadow),r.points.forEach(function(n){let a={};e=n.graphic,!n.isNull&&e?(s=n.shapeArgs,t=n.getTranslate(),o.styledMode||(i=r.pointAttribs(n,n.selected&&"select")),n.delayedRendering?(e.setRadialReference(r.center).attr(s).attr(t),o.styledMode||e.attr(i).attr({"stroke-linejoin":"round"}),n.delayedRendering=!1):(e.setRadialReference(r.center),o.styledMode||g(!0,a,i),g(!0,a,s,t),e.animate(a)),e.attr({visibility:n.visible?"inherit":"hidden"}),e.addClass(n.getClassName(),!0)):e&&(n.graphic=e.destroy())})}sortByAngle(t,e){t.sort(function(t,i){return void 0!==t.angle&&(i.angle-t.angle)*e})}translate(t){u(this,"translate"),this.generatePoints();let e=this.options,i=e.slicedOffset,s=l(e.startAngle,e.endAngle),r=this.startAngleRad=s.start,o=(this.endAngleRad=s.end)-r,n=this.points,a=e.ignoreHiddenPoint,h=n.length,d,c,p,g,f,m,x,y=0;for(t||(this.center=t=this.getCenter()),m=0;m1.5*Math.PI?p-=2*Math.PI:p<-Math.PI/2&&(p+=2*Math.PI),x.slicedTranslation={translateX:Math.round(Math.cos(p)*i),translateY:Math.round(Math.sin(p)*i)},g=Math.cos(p)*t[2]/2,f=Math.sin(p)*t[2]/2,x.tooltipPos=[t[0]+.7*g,t[1]+.7*f],x.half=p<-Math.PI/2||p>Math.PI/2?1:0,x.angle=p}u(this,"afterTranslate")}updateTotals(){let t=this.points,e=t.length,i=this.options.ignoreHiddenPoint,s,r,o=0;for(s=0;s0&&(r.visible||!i)?r.y/o*100:0,r.total=o}}return m.defaultOptions=g(o.defaultOptions,r),p(m.prototype,{axisTypes:[],directTouch:!0,drawGraph:void 0,drawTracker:e.prototype.drawTracker,getCenter:t.getCenter,getSymbol:d,invertible:!1,isCartesian:!1,noSharedTooltip:!0,pointAttribs:e.prototype.pointAttribs,pointClass:s,requireSorting:!1,searchPoint:d,trackerGroups:["group","dataLabelsGroup"]}),n.registerSeriesType("pie",m),m}),i(e,"Series/Pie/PieDataLabel.js",[e["Core/Series/DataLabel.js"],e["Core/Globals.js"],e["Core/Renderer/RendererUtilities.js"],e["Core/Series/SeriesRegistry.js"],e["Core/Utilities.js"]],function(t,e,i,s,r){var o;let{composed:n,noop:a}=e,{distribute:h}=i,{series:l}=s,{arrayMax:d,clamp:c,defined:p,pick:u,pushUnique:g,relativeLength:f}=r;return function(e){let i={radialDistributionY:function(t,e){return(e.dataLabelPosition?.top||0)+t.distributeBox.pos},radialDistributionX:function(t,e,i,s,r){let o=r.dataLabelPosition;return t.getX(i<(o?.top||0)+2||i>(o?.bottom||0)-2?s:i,e.half,e,r)},justify:function(t,e,i,s){return s[0]+(t.half?-1:1)*(i+(e.dataLabelPosition?.distance||0))},alignToPlotEdges:function(t,e,i,s){let r=t.getBBox().width;return e?r+s:i-r-s},alignToConnectors:function(t,e,i,s){let r=0,o;return t.forEach(function(t){(o=t.dataLabel.getBBox().width)>r&&(r=o)}),e?r+s:i-r-s}};function s(t,e){let{center:i,options:s}=this,r=i[2]/2,o=t.angle||0,n=Math.cos(o),a=Math.sin(o),h=i[0]+n*r,l=i[1]+a*r,d=Math.min((s.slicedOffset||0)+(s.borderWidth||0),e/5);return{natural:{x:h+n*e,y:l+a*e},computed:{},alignment:e<0?"center":t.half?"right":"left",connectorPosition:{breakAt:{x:h+n*d,y:l+a*d},touchingSliceAt:{x:h,y:l}},distance:e}}function r(){let t=this,e=t.points,i=t.chart,s=i.plotWidth,r=i.plotHeight,o=i.plotLeft,n=Math.round(i.chartWidth/3),a=t.center,c=a[2]/2,g=a[1],m=[[],[]],x=[0,0,0,0],y=t.dataLabelPositioners,b,v,S,C=0;t.visible&&t.hasDataLabels?.()&&(e.forEach(t=>{(t.dataLabels||[]).forEach(t=>{t.shortened&&(t.attr({width:"auto"}).css({width:"auto",textOverflow:"clip"}),t.shortened=!1)})}),l.prototype.drawDataLabels.apply(t),e.forEach(t=>{(t.dataLabels||[]).forEach((e,i)=>{let s=a[2]/2,r=e.options,o=f(r?.distance||0,s);0===i&&m[t.half].push(t),!p(r?.style?.width)&&e.getBBox().width>n&&(e.css({width:Math.round(.7*n)+"px"}),e.shortened=!0),e.dataLabelPosition=this.getDataLabelPosition(t,o),C=Math.max(C,o)})}),m.forEach((e,n)=>{let l=e.length,d=[],f,m,b=0,k;l&&(t.sortByAngle(e,n-.5),C>0&&(f=Math.max(0,g-c-C),m=Math.min(g+c+C,i.plotHeight),e.forEach(t=>{(t.dataLabels||[]).forEach(e=>{let s=e.dataLabelPosition;s&&s.distance>0&&(s.top=Math.max(0,g-c-s.distance),s.bottom=Math.min(g+c+s.distance,i.plotHeight),b=e.getBBox().height||21,e.lineHeight=i.renderer.fontMetrics(e.text||e).h+2*e.padding,t.distributeBox={target:(e.dataLabelPosition?.natural.y||0)-s.top+e.lineHeight/2,size:b,rank:t.y},d.push(t.distributeBox))})}),h(d,k=m+b-f,k/5)),e.forEach(i=>{(i.dataLabels||[]).forEach(h=>{let l=h.options||{},g=i.distributeBox,f=h.dataLabelPosition,m=f?.natural.y||0,b=l.connectorPadding||0,C=h.lineHeight||21,k=(C-h.getBBox().height)/2,M=0,w=m,T="inherit";if(f){if(d&&p(g)&&f.distance>0&&(void 0===g.pos?T="hidden":(S=g.size,w=y.radialDistributionY(i,h))),l.justify)M=y.justify(i,h,c,a);else switch(l.alignTo){case"connectors":M=y.alignToConnectors(e,n,s,o);break;case"plotEdges":M=y.alignToPlotEdges(h,n,s,o);break;default:M=y.radialDistributionX(t,i,w-k,m,h)}if(f.attribs={visibility:T,align:f.alignment},f.posAttribs={x:M+(l.x||0)+(({left:b,right:-b})[f.alignment]||0),y:w+(l.y||0)-C/2},f.computed.x=M,f.computed.y=w-k,u(l.crop,!0)){let t;M-(v=h.getBBox().width)s-b&&0===n&&(t=Math.round(M+v-s+b),x[1]=Math.max(t,x[1])),w-S/2<0?x[0]=Math.max(Math.round(-w+S/2),x[0]):w+S/2>r&&(x[2]=Math.max(Math.round(w+S/2-r),x[2])),f.sideOverflow=t}}})}))}),(0===d(x)||this.verifyDataLabelOverflow(x))&&(this.placeDataLabels(),this.points.forEach(e=>{(e.dataLabels||[]).forEach(s=>{let{connectorColor:r,connectorWidth:o=1}=s.options||{},n=s.dataLabelPosition;if(o){let a;b=s.connector,n&&n.distance>0?(a=!b,b||(s.connector=b=i.renderer.path().addClass("highcharts-data-label-connector highcharts-color-"+e.colorIndex+(e.className?" "+e.className:"")).add(t.dataLabelsGroup)),i.styledMode||b.attr({"stroke-width":o,stroke:r||e.color||"#666666"}),b[a?"attr":"animate"]({d:e.getConnectorPath(s)}),b.attr({visibility:n.attribs?.visibility})):b&&(s.connector=b.destroy())}})})))}function o(){this.points.forEach(t=>{(t.dataLabels||[]).forEach(t=>{let e=t.dataLabelPosition;e?(e.sideOverflow&&(t.css({width:Math.max(t.getBBox().width-e.sideOverflow,0)+"px",textOverflow:(t.options?.style||{}).textOverflow||"ellipsis"}),t.shortened=!0),t.attr(e.attribs),t[t.moved?"animate":"attr"](e.posAttribs),t.moved=!0):t&&t.attr({y:-9999})}),delete t.distributeBox},this)}function m(t){let e=this.center,i=this.options,s=i.center,r=i.minSize||80,o=r,n=null!==i.size;return!n&&(null!==s[0]?o=Math.max(e[2]-Math.max(t[1],t[3]),r):(o=Math.max(e[2]-t[1]-t[3],r),e[0]+=(t[3]-t[1])/2),null!==s[1]?o=c(o,r,e[2]-Math.max(t[0],t[2])):(o=c(o,r,e[2]-t[0]-t[2]),e[1]+=(t[0]-t[2])/2),o(t.x+=e.x,t.y+=e.y,t),{x:0,y:0});return{x:e.x/t.length,y:e.y/t.length}},e.getDistanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},e.getAngleBetweenPoints=function(t,e){return Math.atan2(e.x-t.x,e.y-t.y)},e.pointInPolygon=function({x:t,y:e},i){let s=i.length,r,o,n=!1;for(r=0,o=s-1;re!=l>e&&t<(h-s)*(e-a)/(l-a)+s&&(n=!n)}return n},t}),i(e,"Extensions/OverlappingDataLabels.js",[e["Core/Geometry/GeometryUtilities.js"],e["Core/Utilities.js"]],function(t,e){let{pointInPolygon:i}=t,{addEvent:s,fireEvent:r,objectEach:o,pick:n}=e;function a(t){let e=t.length,s=(t,e)=>!(e.x>=t.x+t.width||e.x+e.width<=t.x||e.y>=t.y+t.height||e.y+e.height<=t.y),o=(t,e)=>{for(let s of t)if(i({x:s[0],y:s[1]},e))return!0;return!1},n,a,l,d,c,p=!1;for(let i=0;i(e.labelrank||0)-(t.labelrank||0));for(let i=0;i{o(t,t=>{t.label&&e.push(t.label)})});for(let i of t.series||[])if(i.visible&&i.hasDataLabels?.()){let s=i=>{for(let s of i)s.visible&&(s.dataLabels||[]).forEach(i=>{let r=i.options||{};i.labelrank=n(r.labelrank,s.labelrank,s.shapeArgs?.height),r.allowOverlap??Number(r.distance)>0?(i.oldOpacity=i.opacity,i.newOpacity=1,h(i,t)):e.push(i)})};s(i.nodes||[]),s(i.points)}this.hideOverlappingLabels(e)}return{compose:function(t){let e=t.prototype;e.hideOverlappingLabels||(e.hideOverlappingLabels=a,s(t,"render",l))}}}),i(e,"Extensions/BorderRadius.js",[e["Core/Defaults.js"],e["Core/Globals.js"],e["Core/Utilities.js"]],function(t,e,i){let{defaultOptions:s}=t,{noop:r}=e,{addEvent:o,extend:n,isObject:a,merge:h,relativeLength:l}=i,d={radius:0,scope:"stack",where:void 0},c=r,p=r;function u(t,e,i,s,r={}){let o=c(t,e,i,s,r),{innerR:n=0,r:a=i,start:h=0,end:d=0}=r;if(r.open||!r.borderRadius)return o;let p=d-h,g=Math.sin(p/2),f=Math.max(Math.min(l(r.borderRadius||0,a-n),(a-n)/2,a*g/(1+g)),0),m=Math.min(f,p/Math.PI*2*n),x=o.length-1;for(;x--;)!function(t,e,i){let s,r,o;let n=t[e],a=t[e+1];if("Z"===a[0]&&(a=t[0]),("M"===n[0]||"L"===n[0])&&"A"===a[0]?(s=n,r=a,o=!0):"A"===n[0]&&("M"===a[0]||"L"===a[0])&&(s=a,r=n),s&&r&&r.params){let n=r[1],a=r[5],h=r.params,{start:l,end:d,cx:c,cy:p}=h,u=a?n-i:n+i,g=u?Math.asin(i/u):0,f=a?g:-g,m=Math.cos(g)*u;o?(h.start=l+f,s[1]=c+m*Math.cos(l),s[2]=p+m*Math.sin(l),t.splice(e+1,0,["A",i,i,0,0,1,c+n*Math.cos(h.start),p+n*Math.sin(h.start)])):(h.end=d-f,r[6]=c+n*Math.cos(h.end),r[7]=p+n*Math.sin(h.end),t.splice(e+1,0,["A",i,i,0,0,1,c+m*Math.cos(d),p+m*Math.sin(d)])),r[4]=Math.abs(h.end-h.start)1?m:f);return o}function g(){if(this.options.borderRadius&&!(this.chart.is3d&&this.chart.is3d())){let{options:t,yAxis:e}=this,i="percent"===t.stacking,r=s.plotOptions?.[this.type]?.borderRadius,o=f(t.borderRadius,a(r)?r:{}),h=e.options.reversed;for(let s of this.points){let{shapeArgs:r}=s;if("roundedRect"===s.shapeType&&r){let{width:a=0,height:d=0,y:c=0}=r,p=c,u=d;if("stack"===o.scope&&s.stackTotal){let r=e.translate(i?100:s.stackTotal,!1,!0,!1,!0),o=e.translate(t.threshold||0,!1,!0,!1,!0),n=this.crispCol(0,Math.min(r,o),0,Math.abs(r-o));p=n.y,u=n.height}let g=(s.negative?-1:1)*(h?-1:1)==-1,f=o.where;!f&&this.is("waterfall")&&Math.abs((s.yBottom||0)-(this.translatedThreshold||0))>this.borderWidth&&(f="all"),f||(f="end");let m=Math.min(l(o.radius,a),a/2,"all"===f?d/2:1/0)||0;"end"===f&&(g&&(p-=m),u+=m),n(r,{brBoxHeight:u,brBoxY:p,r:m})}}}}function f(t,e){return a(t)||(t={radius:t||0}),h(d,e,t)}function m(){let t=f(this.options.borderRadius);for(let e of this.points){let i=e.shapeArgs;i&&(i.borderRadius=l(t.radius,(i.r||0)-(i.innerR||0)))}}function x(t,e,i,s,r={}){let o=p(t,e,i,s,r),{r:n=0,brBoxHeight:a=s,brBoxY:h=e}=r,l=e-h,d=h+a-(e+s),c=l-n>-.1?0:n,u=d-n>-.1?0:n,g=Math.max(c&&l,0),f=Math.max(u&&d,0),m=[t+c,e],y=[t+i-c,e],b=[t+i,e+c],v=[t+i,e+s-u],S=[t+i-u,e+s],C=[t+u,e+s],k=[t,e+s-u],M=[t,e+c],w=(t,e)=>Math.sqrt(Math.pow(t,2)-Math.pow(e,2));if(g){let t=w(c,c-g);m[0]-=t,y[0]+=t,b[1]=M[1]=e+c-g}if(s=n(i.minWidth,0)&&this.chartHeight>=n(i.minHeight,0)}).call(this)&&e.push(t._id)}function h(t,e){let s=this.options.responsive,n=this.currentResponsive,h=[],l;!e&&s&&s.rules&&s.rules.forEach(t=>{void 0===t._id&&(t._id=a()),this.matchResponsiveRule(t,h)},this);let d=o(...h.map(t=>r((s||{}).rules||[],e=>e._id===t)).map(t=>t&&t.chartOptions));d.isResponsiveOptions=!0,h=h.toString()||void 0;let c=n&&n.ruleIds;h===c||(n&&(this.currentResponsive=void 0,this.updatingResponsive=!0,this.update(n.undoOptions,t,!0),this.updatingResponsive=!1),h?((l=i(d,this.options,!0,this.collectionsWithUpdate)).isResponsiveOptions=!0,this.currentResponsive={ruleIds:h,mergedOptions:d,undoOptions:l},this.updatingResponsive||this.update(d,t,!0)):this.currentResponsive=void 0)}t.compose=function(t){let i=t.prototype;return i.matchResponsiveRule||s(i,{matchResponsiveRule:e,setResponsive:h}),t}}(e||(e={})),e}),i(e,"masters/highcharts.src.js",[e["Core/Globals.js"],e["Core/Utilities.js"],e["Core/Defaults.js"],e["Core/Animation/Fx.js"],e["Core/Animation/AnimationUtilities.js"],e["Core/Renderer/HTML/AST.js"],e["Core/Templating.js"],e["Core/Renderer/RendererRegistry.js"],e["Core/Renderer/RendererUtilities.js"],e["Core/Renderer/SVG/SVGElement.js"],e["Core/Renderer/SVG/SVGRenderer.js"],e["Core/Renderer/HTML/HTMLElement.js"],e["Core/Axis/Axis.js"],e["Core/Axis/DateTimeAxis.js"],e["Core/Axis/LogarithmicAxis.js"],e["Core/Axis/PlotLineOrBand/PlotLineOrBand.js"],e["Core/Axis/Tick.js"],e["Core/Tooltip.js"],e["Core/Series/Point.js"],e["Core/Pointer.js"],e["Core/Legend/Legend.js"],e["Core/Legend/LegendSymbol.js"],e["Core/Chart/Chart.js"],e["Extensions/ScrollablePlotArea.js"],e["Core/Axis/Stacking/StackingAxis.js"],e["Core/Axis/Stacking/StackItem.js"],e["Core/Series/Series.js"],e["Core/Series/SeriesRegistry.js"],e["Series/Column/ColumnDataLabel.js"],e["Series/Pie/PieDataLabel.js"],e["Core/Series/DataLabel.js"],e["Extensions/OverlappingDataLabels.js"],e["Extensions/BorderRadius.js"],e["Core/Responsive.js"],e["Core/Color/Color.js"],e["Core/Time.js"]],function(t,e,i,s,r,o,n,a,h,l,d,c,p,u,g,f,m,x,y,b,v,S,C,k,M,w,T,A,P,L,O,D,E,I,j,B){return t.AST=o,t.Axis=p,t.Chart=C,t.Color=j,t.DataLabel=O,t.Fx=s,t.HTMLElement=c,t.Legend=v,t.LegendSymbol=S,t.OverlappingDataLabels=t.OverlappingDataLabels||D,t.PlotLineOrBand=f,t.Point=y,t.Pointer=b,t.RendererRegistry=a,t.Series=T,t.SeriesRegistry=A,t.StackItem=w,t.SVGElement=l,t.SVGRenderer=d,t.Templating=n,t.Tick=m,t.Time=B,t.Tooltip=x,t.animate=r.animate,t.animObject=r.animObject,t.chart=C.chart,t.color=j.parse,t.dateFormat=n.dateFormat,t.defaultOptions=i.defaultOptions,t.distribute=h.distribute,t.format=n.format,t.getDeferredAnimation=r.getDeferredAnimation,t.getOptions=i.getOptions,t.numberFormat=n.numberFormat,t.seriesType=A.seriesType,t.setAnimation=r.setAnimation,t.setOptions=i.setOptions,t.stop=r.stop,t.time=i.defaultTime,t.timers=s.timers,E.compose(t.Series,t.SVGElement,t.SVGRenderer),P.compose(t.Series.types.column),O.compose(t.Series),u.compose(t.Axis),c.compose(t.SVGRenderer),v.compose(t.Chart),g.compose(t.Axis),D.compose(t.Chart),L.compose(t.Series.types.pie),f.compose(t.Chart,t.Axis),b.compose(t.Chart),I.compose(t.Chart),k.compose(t.Axis,t.Chart,t.Series),M.compose(t.Axis,t.Chart,t.Series),x.compose(t.Pointer),e.extend(t,e),t}),e["masters/highcharts.src.js"]._modules=e,e["masters/highcharts.src.js"]}); \ No newline at end of file diff --git a/factgenie/static/js/jquery.min.js b/factgenie/static/js/lib/jquery.min.js similarity index 100% rename from factgenie/static/js/jquery.min.js rename to factgenie/static/js/lib/jquery.min.js diff --git a/factgenie/static/js/jsonview.js b/factgenie/static/js/lib/jsonview.js similarity index 100% rename from factgenie/static/js/jsonview.js rename to factgenie/static/js/lib/jsonview.js diff --git a/factgenie/static/js/jspdf.umd.min.js b/factgenie/static/js/lib/jspdf.umd.min.js similarity index 100% rename from factgenie/static/js/jspdf.umd.min.js rename to factgenie/static/js/lib/jspdf.umd.min.js diff --git a/factgenie/static/js/meteogram.js b/factgenie/static/js/lib/meteogram.js similarity index 100% rename from factgenie/static/js/meteogram.js rename to factgenie/static/js/lib/meteogram.js diff --git a/factgenie/static/js/lib/modules/accessibility.js b/factgenie/static/js/lib/modules/accessibility.js new file mode 100644 index 00000000..4ee8b696 --- /dev/null +++ b/factgenie/static/js/lib/modules/accessibility.js @@ -0,0 +1,10 @@ +!/** + * Highcharts JS v11.4.8 (2024-08-29) + * + * Accessibility module + * + * (c) 2010-2024 Highsoft AS + * Author: Oystein Moseng + * + * License: www.highcharts.com/license + */function(e){"object"==typeof module&&module.exports?(e.default=e,module.exports=e):"function"==typeof define&&define.amd?define("highcharts/modules/accessibility",["highcharts"],function(t){return e(t),e.Highcharts=t,e}):e("undefined"!=typeof Highcharts?Highcharts:void 0)}(function(e){"use strict";var t=e?e._modules:{};function i(t,i,s,n){t.hasOwnProperty(i)||(t[i]=n.apply(null,s),"function"==typeof CustomEvent&&e.win.dispatchEvent(new CustomEvent("HighchartsModuleLoaded",{detail:{path:i,module:t[i]}})))}i(t,"Accessibility/Utils/HTMLUtilities.js",[t["Core/Globals.js"],t["Core/Utilities.js"]],function(e,t){let{doc:i,win:s}=e,{css:n}=t,r=s.EventTarget&&new s.EventTarget||"none";function o(e){if("function"==typeof s.MouseEvent)return new s.MouseEvent(e.type,e);if(i.createEvent){let t=i.createEvent("MouseEvent");if(t.initMouseEvent)return t.initMouseEvent(e.type,e.bubbles,e.cancelable,e.view||s,e.detail,e.screenX,e.screenY,e.clientX,e.clientY,e.ctrlKey,e.altKey,e.shiftKey,e.metaKey,e.button,e.relatedTarget),t}return a(e.type)}function a(e,t,n){let o=t||{x:0,y:0};if("function"==typeof s.MouseEvent)return new s.MouseEvent(e,{bubbles:!0,cancelable:!0,composed:!0,button:0,buttons:1,relatedTarget:n||r,view:s,detail:"click"===e?1:0,screenX:o.x,screenY:o.y,clientX:o.x,clientY:o.y});if(i.createEvent){let t=i.createEvent("MouseEvent");if(t.initMouseEvent)return t.initMouseEvent(e,!0,!0,s,"click"===e?1:0,o.x,o.y,o.x,o.y,!1,!1,!1,!1,0,null),t}return{type:e}}return{addClass:function(e,t){e.classList?e.classList.add(t):0>e.className.indexOf(t)&&(e.className+=" "+t)},cloneMouseEvent:o,cloneTouchEvent:function(e){let t=e=>{let t=[];for(let i=0;i/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")},getElement:function(e){return i.getElementById(e)},getFakeMouseEvent:a,getHeadingTagNameForElement:function(e){let t=e=>"h"+Math.min(6,parseInt(e.slice(1),10)+1),i=e=>/^H[1-6]$/i.test(e),s=e=>{let t=e;for(;t=t.previousSibling;){let e=t.tagName||"";if(i(e))return e}return""},n=e=>{let r=s(e);if(r)return t(r);let o=e.parentElement;if(!o)return"p";let a=o.tagName;return i(a)?t(a):n(o)};return n(e)},removeChildNodes:function(e){for(;e.lastChild;)e.removeChild(e.lastChild)},removeClass:function(e,t){e.classList?e.classList.remove(t):e.className=e.className.replace(RegExp(t,"g"),"")},removeElement:function(e){e&&e.parentNode&&e.parentNode.removeChild(e)},reverseChildNodes:function(e){let t=e.childNodes.length;for(;t--;)e.appendChild(e.childNodes[t])},simulatedEventTarget:r,stripHTMLTagsFromString:function(e,t=!1){return"string"==typeof e?t?e.replace(/<\/?[^>]+(>|$)/g,""):e.replace(/<\/?(?!\s)[^>]+(>|$)/g,""):e},visuallyHideElement:function(e){n(e,{position:"absolute",width:"1px",height:"1px",overflow:"hidden",whiteSpace:"nowrap",clip:"rect(1px, 1px, 1px, 1px)",marginTop:"-3px","-ms-filter":"progid:DXImageTransform.Microsoft.Alpha(Opacity=1)",filter:"alpha(opacity=1)",opacity:.01})}}}),i(t,"Accessibility/A11yI18n.js",[t["Core/Templating.js"],t["Core/Utilities.js"]],function(e,t){var i;let{format:s}=e,{getNestedProperty:n,pick:r}=t;return function(e){function t(e,t,i){let o=(e,t)=>{let i=e.slice(t||0),s=i.indexOf("{"),n=i.indexOf("}");if(s>-1&&n>s)return{statement:i.substring(s+1,n),begin:t+s+1,end:t+n}},a=[],l,h,c=0;do l=o(e,c),(h=e.substring(c,l&&l.begin-1)).length&&a.push({value:h,type:"constant"}),l&&a.push({value:l.statement,type:"statement"}),c=l?l.end+1:c+1;while(l);return a.forEach(e=>{"statement"===e.type&&(e.value=function(e,t){let i,s;let o=e.indexOf("#each("),a=e.indexOf("#plural("),l=e.indexOf("["),h=e.indexOf("]");if(o>-1){let r=e.slice(o).indexOf(")")+o,a=e.substring(0,o),l=e.substring(r+1),h=e.substring(o+6,r).split(","),c=Number(h[1]),d;if(s="",i=n(h[0],t)){d=(c=isNaN(c)?i.length:c)<0?i.length+c:Math.min(c,i.length);for(let e=0;e-1){var c;let i=e.slice(a).indexOf(")")+a,o=e.substring(a+8,i).split(",");switch(Number(n(o[0],t))){case 0:s=r(o[4],o[1]);break;case 1:s=r(o[2],o[1]);break;case 2:s=r(o[3],o[1]);break;default:s=o[1]}return s?(c=s).trim&&c.trim()||c.replace(/^\s+|\s+$/g,""):""}if(l>-1){let s;let r=e.substring(0,l),o=Number(e.substring(l+1,h));return i=n(r,t),!isNaN(o)&&i&&(o<0?void 0===(s=i[i.length+o])&&(s=i[0]):void 0===(s=i[o])&&(s=i[i.length-1])),void 0!==s?s:""}return"{"+e+"}"}(e.value,t))}),s(a.reduce((e,t)=>e+t.value,""),t,i)}function i(e,i){let s=e.split("."),n=this.options.lang,r=0;for(;r!!e.graphic);return t&&t.graphic&&t.graphic.element}}function h(e){let t=l(e);return t&&t.parentNode||e.graph&&e.graph.element||e.group&&e.group.element}return{fireEventOnWrappedOrUnwrappedElement:function e(t,i){let n=i.type,r=t.hcEvents;s.createEvent&&(t.dispatchEvent||t.fireEvent)?t.dispatchEvent?t.dispatchEvent(i):t.fireEvent(n,i):r&&r[n]?a(t,n,i):t.element&&e(t.element,i)},getChartTitle:function(e){return n(e.options.title.text||e.langFormat("accessibility.defaultChartTitle",{chart:e}),e.renderer.forExport)},getAxisDescription:function(e){return e&&(e.options.accessibility?.description||e.axisTitle?.textStr||e.options.id||e.categories&&"categories"||e.dateTime&&"Time"||"values")},getAxisRangeDescription:function(e){let t=e.options||{};return t.accessibility&&void 0!==t.accessibility.rangeDescription?t.accessibility.rangeDescription:e.categories?function(e){let t=e.chart;return e.dataMax&&e.dataMin?t.langFormat("accessibility.axis.rangeCategories",{chart:t,axis:e,numCategories:e.dataMax-e.dataMin+1}):""}(e):e.dateTime&&(0===e.min||0===e.dataMin)?function(e){let t=e.chart,i={},s=e.dataMin||e.min||0,n=e.dataMax||e.max||0,r="Seconds";i.Seconds=(n-s)/1e3,i.Minutes=i.Seconds/60,i.Hours=i.Minutes/60,i.Days=i.Hours/24,["Minutes","Hours","Days"].forEach(function(e){i[e]>2&&(r=e)});let o=i[r].toFixed("Seconds"!==r&&"Minutes"!==r?1:0);return t.langFormat("accessibility.axis.timeRange"+r,{chart:t,axis:e,range:o.replace(".0","")})}(e):function(e){let t=e.chart,i=t.options,s=i&&i.accessibility&&i.accessibility.screenReaderSection.axisRangeDateFormat||"",n={min:e.dataMin||e.min||0,max:e.dataMax||e.max||0},r=function(i){return e.dateTime?t.time.dateFormat(s,n[i]):n[i].toString()};return t.langFormat("accessibility.axis.rangeFromTo",{chart:t,axis:e,rangeFrom:r("min"),rangeTo:r("max")})}(e)},getPointFromXY:function(e,t,i){let s=e.length,n;for(;s--;)if(n=o(e[s].points||[],function(e){return e.x===t&&e.y===i}))return n},getSeriesFirstPointElement:l,getSeriesFromName:function(e,t){return t?(e.series||[]).filter(function(e){return e.name===t}):e.series},getSeriesA11yElement:h,unhideChartElementFromAT:function e(t,i){i.setAttribute("aria-hidden",!1),i!==t.renderTo&&i.parentNode&&i.parentNode!==s.body&&(Array.prototype.forEach.call(i.parentNode.childNodes,function(e){e.hasAttribute("aria-hidden")||e.setAttribute("aria-hidden",!0)}),e(t,i.parentNode))},hideSeriesFromAT:function(e){let t=h(e);t&&t.setAttribute("aria-hidden",!0)},scrollAxisToPoint:function(e){let t=e.series.xAxis,i=e.series.yAxis,s=t&&t.scrollbar?t:i,n=s&&s.scrollbar;if(n&&r(n.to)&&r(n.from)){let t=n.to-n.from,i=function(e,t){if(!r(e.dataMin)||!r(e.dataMax))return 0;let i=e.toPixels(e.dataMin),s=e.toPixels(e.dataMax),n="xAxis"===e.coll?"x":"y";return(e.toPixels(t[n]||0)-i)/(s-i)}(s,e);n.updatePosition(i-t/2,i+t/2),a(n,"changed",{from:n.from,to:n.to,trigger:"scrollbar",DOMEvent:null})}}}}),i(t,"Accessibility/Utils/DOMElementProvider.js",[t["Core/Globals.js"],t["Accessibility/Utils/HTMLUtilities.js"]],function(e,t){let{doc:i}=e,{removeElement:s}=t;return class{constructor(){this.elements=[]}createElement(){let e=i.createElement.apply(i,arguments);return this.elements.push(e),e}removeElement(e){s(e),this.elements.splice(this.elements.indexOf(e),1)}destroyCreatedElements(){this.elements.forEach(function(e){s(e)}),this.elements=[]}}}),i(t,"Accessibility/Utils/EventProvider.js",[t["Core/Globals.js"],t["Core/Utilities.js"]],function(e,t){let{addEvent:i}=t;return class{constructor(){this.eventRemovers=[]}addEvent(){let t=i.apply(e,arguments);return this.eventRemovers.push({element:arguments[0],remover:t}),t}removeEvent(e){let t=this.eventRemovers.map(e=>e.remover).indexOf(e);this.eventRemovers[t].remover(),this.eventRemovers.splice(t,1)}removeAddedEvents(){this.eventRemovers.map(e=>e.remover).forEach(e=>e()),this.eventRemovers=[]}}}),i(t,"Accessibility/AccessibilityComponent.js",[t["Accessibility/Utils/ChartUtilities.js"],t["Accessibility/Utils/DOMElementProvider.js"],t["Accessibility/Utils/EventProvider.js"],t["Accessibility/Utils/HTMLUtilities.js"]],function(e,t,i,s){let{fireEventOnWrappedOrUnwrappedElement:n}=e,{getFakeMouseEvent:r}=s;return class{destroy(){}getKeyboardNavigation(){return[]}init(){}onChartRender(){}onChartUpdate(){}initBase(e,s){this.chart=e,this.eventProvider=new i,this.domElementProvider=new t,this.proxyProvider=s,this.keyCodes={left:37,right:39,up:38,down:40,enter:13,space:32,esc:27,tab:9,pageUp:33,pageDown:34,end:35,home:36}}addEvent(e,t,i,s){return this.eventProvider.addEvent(e,t,i,s)}createElement(e,t){return this.domElementProvider.createElement(e,t)}fakeClickEvent(e){n(e,r("click"))}destroyBase(){this.domElementProvider.destroyCreatedElements(),this.eventProvider.removeAddedEvents()}}}),i(t,"Accessibility/KeyboardNavigationHandler.js",[t["Core/Utilities.js"]],function(e){let{find:t}=e;return class{constructor(e,t){this.chart=e,this.keyCodeMap=t.keyCodeMap||[],this.validate=t.validate,this.init=t.init,this.terminate=t.terminate,this.response={success:1,prev:2,next:3,noHandler:4,fail:5}}run(e){let i=e.which||e.keyCode,s=this.response.noHandler,n=t(this.keyCodeMap,function(e){return e[0].indexOf(i)>-1});return n?s=n[1].call(this,i,e):9===i&&(s=this.response[e.shiftKey?"prev":"next"]),s}}}),i(t,"Accessibility/Components/ContainerComponent.js",[t["Accessibility/AccessibilityComponent.js"],t["Accessibility/KeyboardNavigationHandler.js"],t["Accessibility/Utils/ChartUtilities.js"],t["Core/Globals.js"],t["Accessibility/Utils/HTMLUtilities.js"]],function(e,t,i,s,n){let{unhideChartElementFromAT:r,getChartTitle:o}=i,{doc:a}=s,{stripHTMLTagsFromString:l}=n;return class extends e{onChartUpdate(){this.handleSVGTitleElement(),this.setSVGContainerLabel(),this.setGraphicContainerAttrs(),this.setRenderToAttrs(),this.makeCreditsAccessible()}handleSVGTitleElement(){let e=this.chart,t="highcharts-title-"+e.index,i=l(e.langFormat("accessibility.svgContainerTitle",{chartTitle:o(e)}));if(i.length){let s=this.svgTitleElement=this.svgTitleElement||a.createElementNS("http://www.w3.org/2000/svg","title");s.textContent=i,s.id=t,e.renderTo.insertBefore(s,e.renderTo.firstChild)}}setSVGContainerLabel(){let e=this.chart,t=e.langFormat("accessibility.svgContainerLabel",{chartTitle:o(e)});e.renderer.box&&t.length&&e.renderer.box.setAttribute("aria-label",t)}setGraphicContainerAttrs(){let e=this.chart,t=e.langFormat("accessibility.graphicContainerLabel",{chartTitle:o(e)});t.length&&e.container.setAttribute("aria-label",t)}setRenderToAttrs(){let e=this.chart,t="disabled"!==e.options.accessibility.landmarkVerbosity,i=e.langFormat("accessibility.chartContainerLabel",{title:o(e),chart:e});i&&(e.renderTo.setAttribute("role",t?"region":"group"),e.renderTo.setAttribute("aria-label",i))}makeCreditsAccessible(){let e=this.chart,t=e.credits;t&&(t.textStr&&t.element.setAttribute("aria-label",e.langFormat("accessibility.credits",{creditsStr:l(t.textStr,e.renderer.forExport)})),r(e,t.element))}getKeyboardNavigation(){let e=this.chart;return new t(e,{keyCodeMap:[],validate:function(){return!0},init:function(){let t=e.accessibility;t&&t.keyboardNavigation.tabindexContainer.focus()}})}destroy(){this.chart.renderTo.setAttribute("aria-hidden",!0)}}}),i(t,"Accessibility/FocusBorder.js",[t["Core/Utilities.js"]],function(e){var t;let{addEvent:i,pick:s}=e;return function(e){let t=["x","y","transform","width","height","r","d","stroke-width"];function n(){let e=this.focusElement,t=this.options.accessibility.keyboardNavigation.focusBorder;e&&(e.removeFocusBorder(),t.enabled&&e.addFocusBorder(t.margin,{stroke:t.style.color,strokeWidth:t.style.lineWidth,r:t.style.borderRadius}))}function r(e,t){let s=this.options.accessibility.keyboardNavigation.focusBorder,n=t||e.element;n&&n.focus&&(n.hcEvents&&n.hcEvents.focusin||i(n,"focusin",function(){}),n.focus(),s.hideBrowserFocusOutline&&(n.style.outline="none")),this.focusElement&&this.focusElement.removeFocusBorder(),this.focusElement=e,this.renderFocusBorder()}function o(e,i){this.focusBorder&&this.removeFocusBorder();let n=this.getBBox(),r=s(e,3),o=this.parentGroup,a=this.scaleX||o&&o.scaleX,l=this.scaleY||o&&o.scaleY,h=(a?!l:l)?Math.abs(a||l||1):(Math.abs(a||1)+Math.abs(l||1))/2;n.x+=this.translateX?this.translateX:0,n.y+=this.translateY?this.translateY:0;let c=n.x-r,d=n.y-r,u=n.width+2*r,p=n.height+2*r,g=!!this.text;if("text"===this.element.nodeName||g){let e,t;let i=!!this.rotation,s=g?{x:i?1:0,y:0}:(e=0,t=0,"middle"===this.attr("text-anchor")?e=t=.5:this.rotation?e=.25:t=.75,{x:e,y:t}),o=+this.attr("x"),a=+this.attr("y");if(isNaN(o)||(c=o-n.width*s.x-r),isNaN(a)||(d=a-n.height*s.y-r),g&&i){let e=u;u=p,p=e,isNaN(o)||(c=o-n.height*s.x-r),isNaN(a)||(d=a-n.width*s.y-r)}}this.focusBorder=this.renderer.rect(c,d,u,p,parseInt((i&&i.r||0).toString(),10)/h).addClass("highcharts-focus-border").attr({zIndex:99}).add(o),this.renderer.styledMode||this.focusBorder.attr({stroke:i&&i.stroke,"stroke-width":(i&&i.strokeWidth||0)/h}),function(e,...i){e.focusBorderUpdateHooks||(e.focusBorderUpdateHooks={},t.forEach(t=>{let s=t+"Setter",n=e[s]||e._defaultSetter;e.focusBorderUpdateHooks[s]=n,e[s]=function(){let t=n.apply(e,arguments);return e.addFocusBorder.apply(e,i),t}}))}(this,e,i),function(e){if(e.focusBorderDestroyHook)return;let t=e.destroy;e.destroy=function(){return e.focusBorder&&e.focusBorder.destroy&&e.focusBorder.destroy(),t.apply(e,arguments)},e.focusBorderDestroyHook=t}(this)}function a(){var e;e=this,e.focusBorderUpdateHooks&&(Object.keys(e.focusBorderUpdateHooks).forEach(t=>{let i=e.focusBorderUpdateHooks[t];i===e._defaultSetter?delete e[t]:e[t]=i}),delete e.focusBorderUpdateHooks),this.focusBorderDestroyHook&&(this.destroy=this.focusBorderDestroyHook,delete this.focusBorderDestroyHook),this.focusBorder&&(this.focusBorder.destroy(),delete this.focusBorder)}e.compose=function(e,t){let i=e.prototype,s=t.prototype;i.renderFocusBorder||(i.renderFocusBorder=n,i.setFocusToElement=r),s.addFocusBorder||(s.addFocusBorder=o,s.removeFocusBorder=a)}}(t||(t={})),t}),i(t,"Accessibility/Utils/Announcer.js",[t["Core/Renderer/HTML/AST.js"],t["Accessibility/Utils/DOMElementProvider.js"],t["Core/Globals.js"],t["Accessibility/Utils/HTMLUtilities.js"],t["Core/Utilities.js"]],function(e,t,i,s,n){let{doc:r}=i,{addClass:o,visuallyHideElement:a}=s,{attr:l}=n;return class{constructor(e,i){this.chart=e,this.domElementProvider=new t,this.announceRegion=this.addAnnounceRegion(i)}destroy(){this.domElementProvider.destroyCreatedElements()}announce(t){e.setElementHTML(this.announceRegion,t),this.clearAnnouncementRegionTimer&&clearTimeout(this.clearAnnouncementRegionTimer),this.clearAnnouncementRegionTimer=setTimeout(()=>{this.announceRegion.innerHTML=e.emptyHTML,delete this.clearAnnouncementRegionTimer},3e3)}addAnnounceRegion(e){let t=this.chart.announcerContainer||this.createAnnouncerContainer(),i=this.domElementProvider.createElement("div");return l(i,{"aria-hidden":!1,"aria-live":e,"aria-atomic":!0}),this.chart.styledMode?o(i,"highcharts-visually-hidden"):a(i),t.appendChild(i),i}createAnnouncerContainer(){let e=this.chart,t=r.createElement("div");return l(t,{"aria-hidden":!1,class:"highcharts-announcer-container"}),t.style.position="relative",e.renderTo.insertBefore(t,e.renderTo.firstChild),e.announcerContainer=t,t}}}),i(t,"Accessibility/Components/AnnotationsA11y.js",[t["Accessibility/Utils/HTMLUtilities.js"]],function(e){let{escapeStringForHTML:t,stripHTMLTagsFromString:i}=e;function s(e){return(e.annotations||[]).reduce((e,t)=>(t.options&&!1!==t.options.visible&&(e=e.concat(t.labels)),e),[])}function n(e){return e.options&&e.options.accessibility&&e.options.accessibility.description||e.graphic&&e.graphic.text&&e.graphic.text.textStr||""}function r(e){let t=e.options&&e.options.accessibility&&e.options.accessibility.description;if(t)return t;let i=e.chart,s=n(e),r=e.points,o=e=>e.graphic&&e.graphic.element&&e.graphic.element.getAttribute("aria-label")||"",a=r.filter(e=>!!e.graphic).map(e=>{let t=e.accessibility&&e.accessibility.valueDescription||o(e),i=e&&e.series.name||"";return(i?i+", ":"")+"data point "+t}).filter(e=>!!e),l=a.length,h=l>1?"MultiplePoints":l?"SinglePoint":"NoPoints",c={annotationText:s,annotation:e,numPoints:l,annotationPoint:a[0],additionalAnnotationPoints:a.slice(1)};return i.langFormat("accessibility.screenReaderSection.annotations.description"+h,c)}function o(e){return s(e).map(s=>{let n=t(i(r(s),e.renderer.forExport));return n?`
  • ${n}
  • `:""})}return{getAnnotationsInfoHTML:function(e){let t=e.annotations;if(!(t&&t.length))return"";let i=o(e);return`
      ${i.join(" ")}
    `},getAnnotationLabelDescription:r,getAnnotationListItems:o,getPointAnnotationTexts:function(e){let t=s(e.series.chart).filter(t=>t.points.indexOf(e)>-1);return t.length?t.map(e=>`${n(e)}`):[]}}}),i(t,"Accessibility/Components/InfoRegionsComponent.js",[t["Accessibility/A11yI18n.js"],t["Accessibility/AccessibilityComponent.js"],t["Accessibility/Utils/Announcer.js"],t["Accessibility/Components/AnnotationsA11y.js"],t["Core/Renderer/HTML/AST.js"],t["Accessibility/Utils/ChartUtilities.js"],t["Core/Templating.js"],t["Core/Globals.js"],t["Accessibility/Utils/HTMLUtilities.js"],t["Core/Utilities.js"]],function(e,t,i,s,n,r,o,a,l,h){let{getAnnotationsInfoHTML:c}=s,{getAxisDescription:d,getAxisRangeDescription:u,getChartTitle:p,unhideChartElementFromAT:g}=r,{format:m}=o,{doc:b}=a,{addClass:y,getElement:f,getHeadingTagNameForElement:x,stripHTMLTagsFromString:v,visuallyHideElement:A}=l,{attr:C,pick:w,replaceNested:E}=h;function T(e){return E(e,[/<([\w\-.:!]+)\b[^<>]*>\s*<\/\1>/g,""])}return class extends t{constructor(){super(...arguments),this.screenReaderSections={}}init(){let e=this.chart,t=this;this.initRegionsDefinitions(),this.addEvent(e,"aftergetTableAST",function(e){t.onDataTableCreated(e)}),this.addEvent(e,"afterViewData",function(e){e.wasHidden&&(t.dataTableDiv=e.element,setTimeout(function(){t.focusDataTable()},300))}),this.addEvent(e,"afterHideData",function(){t.viewDataTableButton&&t.viewDataTableButton.setAttribute("aria-expanded","false")}),e.exporting&&this.addEvent(e,"afterPrint",function(){t.updateAllScreenReaderSections()}),this.announcer=new i(e,"assertive")}initRegionsDefinitions(){let e=this,t=this.chart.options.accessibility;this.screenReaderSections={before:{element:null,buildContent:function(i){let s=t.screenReaderSection.beforeChartFormatter;return s?s(i):e.defaultBeforeChartFormatter(i)},insertIntoDOM:function(e,t){t.renderTo.insertBefore(e,t.renderTo.firstChild)},afterInserted:function(){void 0!==e.sonifyButtonId&&e.initSonifyButton(e.sonifyButtonId),void 0!==e.dataTableButtonId&&e.initDataTableButton(e.dataTableButtonId)}},after:{element:null,buildContent:function(i){let s=t.screenReaderSection.afterChartFormatter;return s?s(i):e.defaultAfterChartFormatter()},insertIntoDOM:function(e,t){t.renderTo.insertBefore(e,t.container.nextSibling)},afterInserted:function(){e.chart.accessibility&&t.keyboardNavigation.enabled&&e.chart.accessibility.keyboardNavigation.updateExitAnchor()}}}}onChartRender(){this.linkedDescriptionElement=this.getLinkedDescriptionElement(),this.setLinkedDescriptionAttrs(),this.updateAllScreenReaderSections()}updateAllScreenReaderSections(){let e=this;Object.keys(this.screenReaderSections).forEach(function(t){e.updateScreenReaderSection(t)})}getLinkedDescriptionElement(){let e=this.chart.options.accessibility.linkedDescription;if(!e)return;if("string"!=typeof e)return e;let t=m(e,this.chart),i=b.querySelectorAll(t);if(1===i.length)return i[0]}setLinkedDescriptionAttrs(){let e=this.linkedDescriptionElement;e&&(e.setAttribute("aria-hidden","true"),y(e,"highcharts-linked-description"))}updateScreenReaderSection(e){let t=this.chart,i=this.screenReaderSections[e],s=i.buildContent(t),r=i.element=i.element||this.createElement("div"),o=r.firstChild||this.createElement("div");s?(this.setScreenReaderSectionAttribs(r,e),n.setElementHTML(o,s),r.appendChild(o),i.insertIntoDOM(r,t),t.styledMode?y(o,"highcharts-visually-hidden"):A(o),g(t,o),i.afterInserted&&i.afterInserted()):(r.parentNode&&r.parentNode.removeChild(r),i.element=null)}setScreenReaderSectionAttribs(e,t){let i=this.chart,s=i.langFormat("accessibility.screenReaderSection."+t+"RegionLabel",{chart:i,chartTitle:p(i)});C(e,{id:`highcharts-screen-reader-region-${t}-${i.index}`,"aria-label":s||void 0}),e.style.position="relative",s&&e.setAttribute("role","all"===i.options.accessibility.landmarkVerbosity?"region":"group")}defaultBeforeChartFormatter(){let t=this.chart,i=t.options.accessibility.screenReaderSection.beforeChartFormat;if(!i)return"";let s=this.getAxesDescription(),n=t.sonify&&t.options.sonification&&t.options.sonification.enabled,r="highcharts-a11y-sonify-data-btn-"+t.index,o="hc-linkto-highcharts-data-table-"+t.index,a=c(t),l=t.langFormat("accessibility.screenReaderSection.annotations.heading",{chart:t}),h={headingTagName:x(t.renderTo),chartTitle:p(t),typeDescription:this.getTypeDescriptionText(),chartSubtitle:this.getSubtitleText(),chartLongdesc:this.getLongdescText(),xAxisDescription:s.xAxis,yAxisDescription:s.yAxis,playAsSoundButton:n?this.getSonifyButtonText(r):"",viewTableButton:t.getCSV?this.getDataTableButtonText(o):"",annotationsTitle:a?l:"",annotationsList:a},d=e.i18nFormat(i,h,t);return this.dataTableButtonId=o,this.sonifyButtonId=r,T(d)}defaultAfterChartFormatter(){let t=this.chart,i=t.options.accessibility.screenReaderSection.afterChartFormat;if(!i)return"";let s={endOfChartMarker:this.getEndOfChartMarkerText()};return T(e.i18nFormat(i,s,t))}getLinkedDescription(){let e=this.linkedDescriptionElement;return v(e&&e.innerHTML||"",this.chart.renderer.forExport)}getLongdescText(){let e=this.chart.options,t=e.caption,i=t&&t.text,s=this.getLinkedDescription();return e.accessibility.description||s||i||""}getTypeDescriptionText(){let e=this.chart;return e.types?e.options.accessibility.typeDescription||function(e,t){let i=t[0],s=e.series&&e.series[0]||{},n=e.mapView&&e.mapView.geoMap&&e.mapView.geoMap.title,r={numSeries:e.series.length,numPoints:s.points&&s.points.length,chart:e,mapTitle:n};return i?"map"===i||"tiledwebmap"===i?r.mapTitle?e.langFormat("accessibility.chartTypes.mapTypeDescription",r):e.langFormat("accessibility.chartTypes.unknownMap",r):e.types.length>1?e.langFormat("accessibility.chartTypes.combinationChart",r):function(e,t,i){let s=t[0],n=e.langFormat("accessibility.seriesTypeDescriptions."+s,i),r=e.series&&e.series.length<2?"Single":"Multiple";return(e.langFormat("accessibility.chartTypes."+s+r,i)||e.langFormat("accessibility.chartTypes.default"+r,i))+(n?" "+n:"")}(e,t,r):e.langFormat("accessibility.chartTypes.emptyChart",r)}(e,e.types):""}getDataTableButtonText(e){let t=this.chart;return'"}getSonifyButtonText(e){let t=this.chart;return t.options.sonification&&!1===t.options.sonification.enabled?"":'"}getSubtitleText(){let e=this.chart.options.subtitle;return v(e&&e.text||"",this.chart.renderer.forExport)}getEndOfChartMarkerText(){let e=f(`highcharts-end-of-chart-marker-${this.chart.index}`);if(e)return e.outerHTML;let t=this.chart,i=t.langFormat("accessibility.screenReaderSection.endOfChartMarker",{chart:t});return'
    '+i+"
    "}onDataTableCreated(e){let t=this.chart;if(t.options.accessibility.enabled){this.viewDataTableButton&&this.viewDataTableButton.setAttribute("aria-expanded","true");let i=e.tree.attributes||{};i.tabindex=-1,i.summary=t.langFormat("accessibility.table.tableSummary",{chart:t}),e.tree.attributes=i}}focusDataTable(){let e=this.dataTableDiv,t=e&&e.getElementsByTagName("table")[0];t&&t.focus&&t.focus()}initSonifyButton(e){let t=this.sonifyButton=f(e),i=this.chart,s=e=>{t&&(t.setAttribute("aria-hidden","true"),t.setAttribute("aria-label","")),e.preventDefault(),e.stopPropagation();let s=i.langFormat("accessibility.sonification.playAsSoundClickAnnouncement",{chart:i});this.announcer.announce(s),setTimeout(()=>{t&&(t.removeAttribute("aria-hidden"),t.removeAttribute("aria-label")),i.sonify&&i.sonify()},1e3)};t&&i&&(t.setAttribute("tabindex",-1),t.onclick=function(e){(i.options.accessibility&&i.options.accessibility.screenReaderSection.onPlayAsSoundClick||s).call(this,e,i)})}initDataTableButton(e){let t=this.viewDataTableButton=f(e),i=this.chart,s=e.replace("hc-linkto-","");t&&(C(t,{tabindex:-1,"aria-expanded":!!f(s)}),t.onclick=i.options.accessibility.screenReaderSection.onViewDataTableClick||function(){i.viewData()})}getAxesDescription(){let e=this.chart,t=function(t,i){let s=e[t];return s.length>1||s[0]&&w(s[0].options.accessibility&&s[0].options.accessibility.enabled,i)},i=!!e.types&&0>e.types.indexOf("map")&&0>e.types.indexOf("treemap")&&0>e.types.indexOf("tilemap"),s=!!e.hasCartesianSeries,n=t("xAxis",!e.angular&&s&&i),r=t("yAxis",s&&i),o={};return n&&(o.xAxis=this.getAxisDescriptionText("xAxis")),r&&(o.yAxis=this.getAxisDescriptionText("yAxis")),o}getAxisDescriptionText(e){let t=this.chart,i=t[e];return t.langFormat("accessibility.axis."+e+"Description"+(i.length>1?"Plural":"Singular"),{chart:t,names:i.map(function(e){return d(e)}),ranges:i.map(function(e){return u(e)}),numAxes:i.length})}destroy(){this.announcer&&this.announcer.destroy()}}}),i(t,"Accessibility/Components/MenuComponent.js",[t["Core/Utilities.js"],t["Accessibility/AccessibilityComponent.js"],t["Accessibility/KeyboardNavigationHandler.js"],t["Accessibility/Utils/ChartUtilities.js"],t["Accessibility/Utils/HTMLUtilities.js"]],function(e,t,i,s,n){let{attr:r}=e,{getChartTitle:o,unhideChartElementFromAT:a}=s,{getFakeMouseEvent:l}=n;function h(e){return e.exportSVGElements&&e.exportSVGElements[0]}class c extends t{init(){let e=this.chart,t=this;this.addEvent(e,"exportMenuShown",function(){t.onMenuShown()}),this.addEvent(e,"exportMenuHidden",function(){t.onMenuHidden()}),this.createProxyGroup()}onMenuHidden(){let e=this.chart.exportContextMenu;e&&e.setAttribute("aria-hidden","true"),this.setExportButtonExpandedState("false")}onMenuShown(){let e=this.chart,t=e.exportContextMenu;t&&(this.addAccessibleContextMenuAttribs(),a(e,t)),this.setExportButtonExpandedState("true")}setExportButtonExpandedState(e){this.exportButtonProxy&&this.exportButtonProxy.innerElement.setAttribute("aria-expanded",e)}onChartRender(){let e=this.chart,t=e.focusElement,i=e.accessibility;this.proxyProvider.clearGroup("chartMenu"),this.proxyMenuButton(),this.exportButtonProxy&&t&&t===e.exportingGroup&&(t.focusBorder?e.setFocusToElement(t,this.exportButtonProxy.innerElement):i&&i.keyboardNavigation.tabindexContainer.focus())}proxyMenuButton(){let e=this.chart,t=this.proxyProvider,i=h(e);(function(e){let t=e.options.exporting,i=h(e);return!!(t&&!1!==t.enabled&&t.accessibility&&t.accessibility.enabled&&i&&i.element)})(e)&&i&&(this.exportButtonProxy=t.addProxyElement("chartMenu",{click:i},"button",{"aria-label":e.langFormat("accessibility.exporting.menuButtonLabel",{chart:e,chartTitle:o(e)}),"aria-expanded":!1,title:e.options.lang.contextButtonTitle||null}))}createProxyGroup(){this.chart&&this.proxyProvider&&this.proxyProvider.addGroup("chartMenu")}addAccessibleContextMenuAttribs(){let e=this.chart,t=e.exportDivElements;if(t&&t.length){t.forEach(e=>{e&&("LI"!==e.tagName||e.children&&e.children.length?e.setAttribute("aria-hidden","true"):e.setAttribute("tabindex",-1))});let i=t[0]&&t[0].parentNode;i&&r(i,{"aria-hidden":void 0,"aria-label":e.langFormat("accessibility.exporting.chartMenuLabel",{chart:e}),role:"list"})}}getKeyboardNavigation(){let e=this.keyCodes,t=this.chart,s=this;return new i(t,{keyCodeMap:[[[e.left,e.up],function(){return s.onKbdPrevious(this)}],[[e.right,e.down],function(){return s.onKbdNext(this)}],[[e.enter,e.space],function(){return s.onKbdClick(this)}]],validate:function(){return!!t.exporting&&!1!==t.options.exporting.enabled&&!1!==t.options.exporting.accessibility.enabled},init:function(){let e=s.exportButtonProxy,i=s.chart.exportingGroup;e&&i&&t.setFocusToElement(i,e.innerElement)},terminate:function(){t.hideExportMenu()}})}onKbdPrevious(e){let t=this.chart,i=t.options.accessibility,s=e.response,n=t.highlightedExportItemIx||0;for(;n--;)if(t.highlightExportItem(n))return s.success;return i.keyboardNavigation.wrapAround?(t.highlightLastExportItem(),s.success):s.prev}onKbdNext(e){let t=this.chart,i=t.options.accessibility,s=e.response;for(let e=(t.highlightedExportItemIx||0)+1;e{e&&"highcharts-menu-item"===e.className&&e.onmouseout&&e.onmouseout(l("mouseout"))}),this.highlightedExportItemIx=0,this.exportContextMenu.hideMenu(),this.container.focus())}function s(e){let t=this.exportDivElements&&this.exportDivElements[e],i=this.exportDivElements&&this.exportDivElements[this.highlightedExportItemIx];if(t&&"LI"===t.tagName&&!(t.children&&t.children.length)){let s=!!(this.renderTo.getElementsByTagName("g")[0]||{}).focus;return t.focus&&s&&t.focus(),i&&i.onmouseout&&i.onmouseout(l("mouseout")),t.onmouseover&&t.onmouseover(l("mouseover")),this.highlightedExportItemIx=e,!0}return!1}function n(){if(this.exportDivElements){let e=this.exportDivElements.length;for(;e--;)if(this.highlightExportItem(e))return!0}return!1}e.compose=function(e){let r=e.prototype;r.hideExportMenu||(r.hideExportMenu=i,r.highlightExportItem=s,r.highlightLastExportItem=n,r.showExportMenu=t)}}(c||(c={})),c}),i(t,"Accessibility/KeyboardNavigation.js",[t["Core/Globals.js"],t["Accessibility/Components/MenuComponent.js"],t["Core/Utilities.js"],t["Accessibility/Utils/EventProvider.js"],t["Accessibility/Utils/HTMLUtilities.js"]],function(e,t,i,s,n){let{doc:r,win:o}=e,{addEvent:a,defined:l,fireEvent:h}=i,{getElement:c,simulatedEventTarget:d}=n;class u{constructor(e,t){this.currentModuleIx=NaN,this.modules=[],this.init(e,t)}init(e,t){let i=this.eventProvider=new s;this.chart=e,this.components=t,this.modules=[],this.currentModuleIx=0,this.update(),i.addEvent(this.tabindexContainer,"keydown",e=>this.onKeydown(e)),i.addEvent(this.tabindexContainer,"focus",e=>this.onFocus(e)),["mouseup","touchend"].forEach(e=>i.addEvent(r,e,e=>this.onMouseUp(e))),["mousedown","touchstart"].forEach(t=>i.addEvent(e.renderTo,t,()=>{this.isClickingChart=!0}))}update(e){let t=this.chart.options.accessibility,i=t&&t.keyboardNavigation,s=this.components;this.updateContainerTabindex(),i&&i.enabled&&e&&e.length?(this.modules=e.reduce(function(e,t){let i=s[t].getKeyboardNavigation();return e.concat(i)},[]),this.updateExitAnchor()):(this.modules=[],this.currentModuleIx=0,this.removeExitAnchor())}updateExitAnchor(){let e=c(`highcharts-end-of-chart-marker-${this.chart.index}`);this.removeExitAnchor(),e?(this.makeElementAnExitAnchor(e),this.exitAnchor=e):this.createExitAnchor()}move(e){let t=this.modules&&this.modules[this.currentModuleIx];t&&t.terminate&&t.terminate(e),this.chart.focusElement&&this.chart.focusElement.removeFocusBorder(),this.currentModuleIx+=e;let i=this.modules&&this.modules[this.currentModuleIx];if(i){if(i.validate&&!i.validate())return this.move(e);if(i.init)return i.init(e),!0}return this.currentModuleIx=0,this.exiting=!0,e>0?this.exitAnchor&&this.exitAnchor.focus():this.tabindexContainer.focus(),!1}onFocus(e){let t=this.chart,i=e.relatedTarget&&t.container.contains(e.relatedTarget),s=t.options.accessibility,n=s&&s.keyboardNavigation;if(n&&n.enabled&&!this.exiting&&!this.tabbingInBackwards&&!this.isClickingChart&&!i){let e=this.getFirstValidModuleIx();null!==e&&(this.currentModuleIx=e,this.modules[e].init(1))}this.keyboardReset=!1,this.exiting=!1}onMouseUp(e){if(delete this.isClickingChart,!this.keyboardReset&&e.relatedTarget!==d){let t=this.chart;if(!e.target||!t.container.contains(e.target)){let e=this.modules&&this.modules[this.currentModuleIx||0];e&&e.terminate&&e.terminate(),this.currentModuleIx=0}t.focusElement&&(t.focusElement.removeFocusBorder(),delete t.focusElement),this.keyboardReset=!0}}onKeydown(e){let t;let i=e||o.event,s=this.modules&&this.modules.length&&this.modules[this.currentModuleIx],n=i.target;if((!n||"INPUT"!==n.nodeName||n.classList.contains("highcharts-a11y-proxy-element"))&&(this.keyboardReset=!1,this.exiting=!1,s)){let e=s.run(i);e===s.response.success?t=!0:e===s.response.prev?t=this.move(-1):e===s.response.next&&(t=this.move(1)),t&&(i.preventDefault(),i.stopPropagation())}}updateContainerTabindex(){let e;let t=this.chart.options.accessibility,i=t&&t.keyboardNavigation,s=!(i&&!1===i.enabled),n=this.chart,r=n.container;n.renderTo.hasAttribute("tabindex")?(r.removeAttribute("tabindex"),e=n.renderTo):e=r,this.tabindexContainer=e;let o=e.getAttribute("tabindex");s&&!o?e.setAttribute("tabindex","0"):s||n.container.removeAttribute("tabindex")}createExitAnchor(){let e=this.chart,t=this.exitAnchor=r.createElement("div");e.renderTo.appendChild(t),this.makeElementAnExitAnchor(t)}makeElementAnExitAnchor(e){let t=this.tabindexContainer.getAttribute("tabindex")||0;e.setAttribute("class","highcharts-exit-anchor"),e.setAttribute("tabindex",t),e.setAttribute("aria-hidden",!1),this.addExitAnchorEventsToEl(e)}removeExitAnchor(){if(this.exitAnchor){let e=this.eventProvider.eventRemovers.find(e=>e.element===this.exitAnchor);e&&l(e.remover)&&this.eventProvider.removeEvent(e.remover),this.exitAnchor.parentNode&&this.exitAnchor.parentNode.removeChild(this.exitAnchor),delete this.exitAnchor}}addExitAnchorEventsToEl(e){let t=this.chart,i=this;this.eventProvider.addEvent(e,"focus",function(e){let s=e||o.event,n=!(s.relatedTarget&&t.container.contains(s.relatedTarget)||i.exiting);if(t.focusElement&&delete t.focusElement,n){if(i.tabbingInBackwards=!0,i.tabindexContainer.focus(),delete i.tabbingInBackwards,s.preventDefault(),i.modules&&i.modules.length){i.currentModuleIx=i.modules.length-1;let e=i.modules[i.currentModuleIx];e&&e.validate&&!e.validate()?i.move(-1):e&&e.init(-1)}}else i.exiting=!1})}getFirstValidModuleIx(){let e=this.modules.length;for(let t=0;t{e&&e.dismissPopupContent&&e.dismissPopupContent()})}i.compose=function(e){t.compose(e);let i=e.prototype;return i.dismissPopupContent||(i.dismissPopupContent=s,a(r,"keydown",n)),e}}(u||(u={})),u}),i(t,"Accessibility/Components/LegendComponent.js",[t["Core/Animation/AnimationUtilities.js"],t["Core/Globals.js"],t["Core/Legend/Legend.js"],t["Core/Utilities.js"],t["Accessibility/AccessibilityComponent.js"],t["Accessibility/KeyboardNavigationHandler.js"],t["Accessibility/Utils/ChartUtilities.js"],t["Accessibility/Utils/HTMLUtilities.js"]],function(e,t,i,s,n,r,o,a){let{animObject:l}=e,{doc:h}=t,{addEvent:c,fireEvent:d,isNumber:u,pick:p,syncTimeout:g}=s,{getChartTitle:m}=o,{stripHTMLTagsFromString:b,addClass:y,removeClass:f}=a;function x(e){let t=e.legend&&e.legend.allItems,i=e.options.legend.accessibility||{},s=e.colorAxis&&e.colorAxis.some(e=>!e.dataClasses||!e.dataClasses.length);return!!(t&&t.length&&!s&&!1!==i.enabled)}function v(e,t){let i=t.legendItem||{};for(let s of(t.setState(e?"hover":"",!0),["group","label","symbol"])){let t=i[s],n=t&&t.element||t;n&&d(n,e?"mouseover":"mouseout")}}class A extends n{constructor(){super(...arguments),this.highlightedLegendItemIx=NaN,this.proxyGroup=null}init(){let e=this;this.recreateProxies(),this.addEvent(i,"afterScroll",function(){this.chart===e.chart&&(e.proxyProvider.updateGroupProxyElementPositions("legend"),e.updateLegendItemProxyVisibility(),e.highlightedLegendItemIx>-1&&this.chart.highlightLegendItem(e.highlightedLegendItemIx))}),this.addEvent(i,"afterPositionItem",function(t){this.chart===e.chart&&this.chart.renderer&&e.updateProxyPositionForItem(t.item)}),this.addEvent(i,"afterRender",function(){this.chart===e.chart&&this.chart.renderer&&e.recreateProxies()&&g(()=>e.proxyProvider.updateGroupProxyElementPositions("legend"),l(p(this.chart.renderer.globalAnimation,!0)).duration)})}updateLegendItemProxyVisibility(){let e;let t=this.chart,i=t.legend,s=i.allItems||[],n=i.currentPage||1,r=i.clipHeight||0;s.forEach(s=>{if(s.a11yProxyElement){let o=i.pages&&i.pages.length,a=s.a11yProxyElement.element,l=!1;if(e=s.legendItem||{},o){let t=e.pageIx||0;l=(e.y||0)+(e.label?Math.round(e.label.getBBox().height):0)-i.pages[t]>r||t!==n-1}l?t.styledMode?y(a,"highcharts-a11y-invisible"):a.style.visibility="hidden":(f(a,"highcharts-a11y-invisible"),a.style.visibility="")}})}onChartRender(){x(this.chart)||this.removeProxies()}highlightAdjacentLegendPage(e){let t=this.chart,i=t.legend,s=(i.currentPage||1)+e,n=i.pages||[];if(s>0&&s<=n.length){let e=0;for(let n of i.allItems)((n.legendItem||{}).pageIx||0)+1===s&&t.highlightLegendItem(e)&&(this.highlightedLegendItemIx=e),++e}}updateProxyPositionForItem(e){e.a11yProxyElement&&e.a11yProxyElement.refreshPosition()}recreateProxies(){let e=h.activeElement,t=this.proxyGroup,i=e&&t&&t.contains(e);return this.removeProxies(),!!x(this.chart)&&(this.addLegendProxyGroup(),this.proxyLegendItems(),this.updateLegendItemProxyVisibility(),this.updateLegendTitle(),i&&this.chart.highlightLegendItem(this.highlightedLegendItemIx),!0)}removeProxies(){this.proxyProvider.removeGroup("legend")}updateLegendTitle(){let e=this.chart,t=b((e.legend&&e.legend.options.title&&e.legend.options.title.text||"").replace(/
    /g," "),e.renderer.forExport),i=e.langFormat("accessibility.legend.legendLabel"+(t?"":"NoTitle"),{chart:e,legendTitle:t,chartTitle:m(e)});this.proxyProvider.updateGroupAttrs("legend",{"aria-label":i})}addLegendProxyGroup(){let e="all"===this.chart.options.accessibility.landmarkVerbosity?"region":null;this.proxyGroup=this.proxyProvider.addGroup("legend","ul",{"aria-label":"_placeholder_",role:e})}proxyLegendItems(){let e;let t=this;((this.chart.legend||{}).allItems||[]).forEach(i=>{(e=i.legendItem||{}).label&&e.label.element&&t.proxyLegendItem(i)})}proxyLegendItem(e){let t=e.legendItem||{};if(!t.label||!t.group)return;let i=this.chart.langFormat("accessibility.legend.legendItem",{chart:this.chart,itemName:b(e.name,this.chart.renderer.forExport),item:e}),s={tabindex:-1,"aria-pressed":e.visible,"aria-label":i},n=t.group.div?t.label:t.group;e.a11yProxyElement=this.proxyProvider.addProxyElement("legend",{click:t.label,visual:n.element},"button",s)}getKeyboardNavigation(){let e=this.keyCodes,t=this,i=this.chart;return new r(i,{keyCodeMap:[[[e.left,e.right,e.up,e.down],function(e){return t.onKbdArrowKey(this,e)}],[[e.enter,e.space],function(){return t.onKbdClick(this)}],[[e.pageDown,e.pageUp],function(i){let s=i===e.pageDown?1:-1;return t.highlightAdjacentLegendPage(s),this.response.success}]],validate:function(){return t.shouldHaveLegendNavigation()},init:function(){i.highlightLegendItem(0),t.highlightedLegendItemIx=0},terminate:function(){t.highlightedLegendItemIx=-1,i.legend.allItems.forEach(e=>v(!1,e))}})}onKbdArrowKey(e,t){let{keyCodes:{left:i,up:s},highlightedLegendItemIx:n,chart:r}=this,o=r.legend.allItems.length,a=r.options.accessibility.keyboardNavigation.wrapAround,l=t===i||t===s?-1:1;return r.highlightLegendItem(n+l)?this.highlightedLegendItemIx+=l:a&&o>1&&(this.highlightedLegendItemIx=l>0?0:o-1,r.highlightLegendItem(this.highlightedLegendItemIx)),e.response.success}onKbdClick(e){let t=this.chart.legend.allItems[this.highlightedLegendItemIx];return t&&t.a11yProxyElement&&t.a11yProxyElement.click(),e.response.success}shouldHaveLegendNavigation(){if(!x(this.chart))return!1;let e=this.chart,t=(e.options.legend||{}).accessibility||{};return!!(e.legend.display&&t.keyboardNavigation&&t.keyboardNavigation.enabled)}destroy(){this.removeProxies()}}return function(e){function t(e){let t=this.legend.allItems,i=this.accessibility&&this.accessibility.components.legend.highlightedLegendItemIx,s=t[e],n=s?.legendItem||{};if(s){u(i)&&t[i]&&v(!1,t[i]),function(e,t){let i=(e.allItems[t].legendItem||{}).pageIx,s=e.currentPage;void 0!==i&&i+1!==s&&e.scroll(1+i-s)}(this.legend,e);let r=n.label,o=s.a11yProxyElement&&s.a11yProxyElement.innerElement;return r&&r.element&&o&&this.setFocusToElement(r,o),v(!0,s),!0}return!1}function i(e){let t=this.chart.options.accessibility,i=e.item;t.enabled&&i&&i.a11yProxyElement&&i.a11yProxyElement.innerElement.setAttribute("aria-pressed",e.visible?"true":"false")}e.compose=function(e,s){let n=e.prototype;n.highlightLegendItem||(n.highlightLegendItem=t,c(s,"afterColorizeItem",i))}}(A||(A={})),A}),i(t,"Stock/Navigator/ChartNavigatorComposition.js",[t["Core/Globals.js"],t["Core/Utilities.js"]],function(e,t){let i;let{isTouchDevice:s}=e,{addEvent:n,merge:r,pick:o}=t,a=[];function l(){this.navigator&&this.navigator.setBaseSeries(null,!1)}function h(){let e,t,i;let s=this.legend,n=this.navigator;if(n){e=s&&s.options,t=n.xAxis,i=n.yAxis;let{scrollbarHeight:r,scrollButtonSize:a}=n;this.inverted?(n.left=n.opposite?this.chartWidth-r-n.height:this.spacing[3]+r,n.top=this.plotTop+a):(n.left=o(t.left,this.plotLeft+a),n.top=n.navigatorOptions.top||this.chartHeight-n.height-r-(this.scrollbar?.options.margin||0)-this.spacing[2]-(this.rangeSelector&&this.extraBottomMargin?this.rangeSelector.getHeight():0)-(e&&"bottom"===e.verticalAlign&&"proximate"!==e.layout&&e.enabled&&!e.floating?s.legendHeight+o(e.margin,10):0)-(this.titleOffset?this.titleOffset[2]:0)),t&&i&&(this.inverted?t.options.left=i.options.left=n.left:t.options.top=i.options.top=n.top,t.setAxisSize(),i.setAxisSize())}}function c(e){!this.navigator&&!this.scroller&&(this.options.navigator.enabled||this.options.scrollbar.enabled)&&(this.scroller=this.navigator=new i(this),o(e.redraw,!0)&&this.redraw(e.animation))}function d(){let e=this.options;(e.navigator.enabled||e.scrollbar.enabled)&&(this.scroller=this.navigator=new i(this))}function u(){let e=this.options,t=e.navigator,i=e.rangeSelector;if((t&&t.enabled||i&&i.enabled)&&(!s&&"x"===this.zooming.type||s&&"x"===this.zooming.pinchType))return!1}function p(e){let t=e.navigator;if(t&&e.xAxis[0]){let i=e.xAxis[0].getExtremes();t.render(i.min,i.max)}}function g(e){let t=e.options.navigator||{},i=e.options.scrollbar||{};!this.navigator&&!this.scroller&&(t.enabled||i.enabled)&&(r(!0,this.options.navigator,t),r(!0,this.options.scrollbar,i),delete e.options.navigator,delete e.options.scrollbar)}return{compose:function(e,s){if(t.pushUnique(a,e)){let t=e.prototype;i=s,t.callbacks.push(p),n(e,"afterAddSeries",l),n(e,"afterSetChartSize",h),n(e,"afterUpdate",c),n(e,"beforeRender",d),n(e,"beforeShowResetZoom",u),n(e,"update",g)}}}}),i(t,"Core/Axis/NavigatorAxisComposition.js",[t["Core/Globals.js"],t["Core/Utilities.js"]],function(e,t){let{isTouchDevice:i}=e,{addEvent:s,correctFloat:n,defined:r,isNumber:o,pick:a}=t;function l(){this.navigatorAxis||(this.navigatorAxis=new c(this))}function h(e){let t;let s=this.chart,n=s.options,o=n.navigator,a=this.navigatorAxis,l=s.zooming.pinchType,h=n.rangeSelector,c=s.zooming.type;if(this.isXAxis&&(o?.enabled||h?.enabled)){if("y"===c&&"zoom"===e.trigger)t=!1;else if(("zoom"===e.trigger&&"xy"===c||i&&"xy"===l)&&this.options.range){let t=a.previousZoom;r(e.min)?a.previousZoom=[this.min,this.max]:t&&(e.min=t[0],e.max=t[1],a.previousZoom=void 0)}}void 0!==t&&e.preventDefault()}class c{static compose(e){e.keepProps.includes("navigatorAxis")||(e.keepProps.push("navigatorAxis"),s(e,"init",l),s(e,"setExtremes",h))}constructor(e){this.axis=e}destroy(){this.axis=void 0}toFixedRange(e,t,i,s){let l=this.axis,h=(l.pointRange||0)/2,c=a(i,l.translate(e,!0,!l.horiz)),d=a(s,l.translate(t,!0,!l.horiz));return r(i)||(c=n(c+h)),r(s)||(d=n(d-h)),o(c)&&o(d)||(c=d=void 0),{min:c,max:d}}}return c}),i(t,"Stock/Navigator/NavigatorDefaults.js",[t["Core/Color/Color.js"],t["Core/Series/SeriesRegistry.js"]],function(e,t){let{parse:i}=e,{seriesTypes:s}=t;return{height:40,margin:25,maskInside:!0,handles:{width:7,borderRadius:0,height:15,symbols:["navigator-handle","navigator-handle"],enabled:!0,lineWidth:1,backgroundColor:"#f2f2f2",borderColor:"#999999"},maskFill:i("#667aff").setOpacity(.3).get(),outlineColor:"#999999",outlineWidth:1,series:{type:void 0===s.areaspline?"line":"areaspline",fillOpacity:.05,lineWidth:1,compare:null,sonification:{enabled:!1},dataGrouping:{approximation:"average",enabled:!0,groupPixelWidth:2,firstAnchor:"firstPoint",anchor:"middle",lastAnchor:"lastPoint",units:[["millisecond",[1,2,5,10,20,25,50,100,200,500]],["second",[1,2,5,10,15,30]],["minute",[1,2,5,10,15,30]],["hour",[1,2,3,4,6,8,12]],["day",[1,2,3,4]],["week",[1,2,3]],["month",[1,3,6]],["year",null]]},dataLabels:{enabled:!1,zIndex:2},id:"highcharts-navigator-series",className:"highcharts-navigator-series",lineColor:null,marker:{enabled:!1},threshold:null},xAxis:{className:"highcharts-navigator-xaxis",tickLength:0,lineWidth:0,gridLineColor:"#e6e6e6",id:"navigator-x-axis",gridLineWidth:1,tickPixelInterval:200,labels:{align:"left",style:{color:"#000000",fontSize:"0.7em",opacity:.6,textOutline:"2px contrast"},x:3,y:-4},crosshair:!1},yAxis:{className:"highcharts-navigator-yaxis",gridLineWidth:0,startOnTick:!1,endOnTick:!1,minPadding:.1,id:"navigator-y-axis",maxPadding:.1,labels:{enabled:!1},crosshair:!1,title:{text:null},tickLength:0,tickWidth:0}}}),i(t,"Stock/Navigator/NavigatorSymbols.js",[t["Core/Renderer/SVG/Symbols.js"],t["Core/Utilities.js"]],function(e,t){let{relativeLength:i}=t;return{"navigator-handle":function(t,s,n,r,o={}){let a=o.width?o.width/2:n,l=i(o.borderRadius||0,Math.min(2*a,r));return[["M",-1.5,(r=o.height||r)/2-3.5],["L",-1.5,r/2+4.5],["M",.5,r/2-3.5],["L",.5,r/2+4.5],...e.rect(-a-1,.5,2*a+1,r,{r:l})]}}}),i(t,"Stock/Utilities/StockUtilities.js",[t["Core/Utilities.js"]],function(e){let{defined:t}=e;return{setFixedRange:function(e){let i=this.xAxis[0];t(i.dataMax)&&t(i.dataMin)&&e?this.fixedRange=Math.min(e,i.dataMax-i.dataMin):this.fixedRange=e}}}),i(t,"Stock/Navigator/NavigatorComposition.js",[t["Core/Defaults.js"],t["Core/Globals.js"],t["Core/Axis/NavigatorAxisComposition.js"],t["Stock/Navigator/NavigatorDefaults.js"],t["Stock/Navigator/NavigatorSymbols.js"],t["Core/Renderer/RendererRegistry.js"],t["Stock/Utilities/StockUtilities.js"],t["Core/Utilities.js"]],function(e,t,i,s,n,r,o,a){let{setOptions:l}=e,{composed:h}=t,{getRendererType:c}=r,{setFixedRange:d}=o,{addEvent:u,extend:p,pushUnique:g}=a;function m(){this.chart.navigator&&!this.options.isInternal&&this.chart.navigator.setBaseSeries(null,!1)}return{compose:function(e,t,r){i.compose(t),g(h,"Navigator")&&(e.prototype.setFixedRange=d,p(c().prototype.symbols,n),u(r,"afterUpdate",m),l({navigator:s}))}}}),i(t,"Core/Axis/ScrollbarAxis.js",[t["Core/Globals.js"],t["Core/Utilities.js"]],function(e,t){var i;let{composed:s}=e,{addEvent:n,defined:r,pick:o,pushUnique:a}=t;return function(e){let t;function i(e){let t=o(e.options&&e.options.min,e.min),i=o(e.options&&e.options.max,e.max);return{axisMin:t,axisMax:i,scrollMin:r(e.dataMin)?Math.min(t,e.min,e.dataMin,o(e.threshold,1/0)):t,scrollMax:r(e.dataMax)?Math.max(i,e.max,e.dataMax,o(e.threshold,-1/0)):i}}function l(){let e=this.scrollbar,t=e&&!e.options.opposite,i=this.horiz?2:t?3:1;e&&(this.chart.scrollbarsOffsets=[0,0],this.chart.axisOffset[i]+=e.size+(e.options.margin||0))}function h(){let e=this;e.options&&e.options.scrollbar&&e.options.scrollbar.enabled&&(e.options.scrollbar.vertical=!e.horiz,e.options.startOnTick=e.options.endOnTick=!1,e.scrollbar=new t(e.chart.renderer,e.options.scrollbar,e.chart),n(e.scrollbar,"changed",function(t){let s,n;let{axisMin:o,axisMax:a,scrollMin:l,scrollMax:h}=i(e),c=h-l;if(r(o)&&r(a)){if(e.horiz&&!e.reversed||!e.horiz&&e.reversed?(s=l+c*this.to,n=l+c*this.from):(s=l+c*(1-this.from),n=l+c*(1-this.to)),this.shouldUpdateExtremes(t.DOMType)){let i="mousemove"!==t.DOMType&&"touchmove"!==t.DOMType&&void 0;e.setExtremes(n,s,!0,i,t)}else this.setRange(this.from,this.to)}}))}function c(){let e,t,s;let{scrollMin:n,scrollMax:o}=i(this),a=this.scrollbar,l=this.axisTitleMargin+(this.titleOffset||0),h=this.chart.scrollbarsOffsets,c=this.options.margin||0;if(a&&h){if(this.horiz)this.opposite||(h[1]+=l),a.position(this.left,this.top+this.height+2+h[1]-(this.opposite?c:0),this.width,this.height),this.opposite||(h[1]+=c),e=1;else{let t;this.opposite&&(h[0]+=l),t=a.options.opposite?this.left+this.width+2+h[0]-(this.opposite?0:c):this.opposite?0:c,a.position(t,this.top,this.width,this.height),this.opposite&&(h[0]+=c),e=0}if(h[e]+=a.size+(a.options.margin||0),isNaN(n)||isNaN(o)||!r(this.min)||!r(this.max)||this.dataMin===this.dataMax)a.setRange(0,1);else if(this.min===this.max){let e=this.pointRange/(this.dataMax+1);t=e*this.min,s=e*(this.max+1),a.setRange(t,s)}else t=(this.min-n)/(o-n),s=(this.max-n)/(o-n),this.horiz&&!this.reversed||!this.horiz&&this.reversed?a.setRange(t,s):a.setRange(1-s,1-t)}}e.compose=function(e,i){a(s,"Axis.Scrollbar")&&(t=i,n(e,"afterGetOffset",l),n(e,"afterInit",h),n(e,"afterRender",c))}}(i||(i={})),i}),i(t,"Stock/Scrollbar/ScrollbarDefaults.js",[],function(){return{height:10,barBorderRadius:5,buttonBorderRadius:0,buttonsEnabled:!1,liveRedraw:void 0,margin:void 0,minWidth:6,opposite:!0,step:.2,zIndex:3,barBackgroundColor:"#cccccc",barBorderWidth:0,barBorderColor:"#cccccc",buttonArrowColor:"#333333",buttonBackgroundColor:"#e6e6e6",buttonBorderColor:"#cccccc",buttonBorderWidth:1,rifleColor:"none",trackBackgroundColor:"rgba(255, 255, 255, 0.001)",trackBorderColor:"#cccccc",trackBorderRadius:5,trackBorderWidth:1}}),i(t,"Stock/Scrollbar/Scrollbar.js",[t["Core/Defaults.js"],t["Core/Globals.js"],t["Core/Axis/ScrollbarAxis.js"],t["Stock/Scrollbar/ScrollbarDefaults.js"],t["Core/Utilities.js"]],function(e,t,i,s,n){let{defaultOptions:r}=e,{addEvent:o,correctFloat:a,crisp:l,defined:h,destroyObjectProperties:c,fireEvent:d,merge:u,pick:p,removeEvent:g}=n;class m{static compose(e){i.compose(e,m)}static swapXY(e,t){return t&&e.forEach(e=>{let t;let i=e.length;for(let s=0;sthis.calculatedWidth?t.minWidth:0;return{chartX:(e.chartX-this.x-this.xOffset)/(this.barWidth-i),chartY:(e.chartY-this.y-this.yOffset)/(this.barWidth-i)}}destroy(){let e=this,t=e.chart.scroller;e.removeEvents(),["track","scrollbarRifles","scrollbar","scrollbarGroup","group"].forEach(function(t){e[t]&&e[t].destroy&&(e[t]=e[t].destroy())}),t&&e===t.scrollbar&&(t.scrollbar=null,c(t.scrollbarButtons))}drawScrollbarButton(e){let t=this.renderer,i=this.scrollbarButtons,s=this.options,n=this.size,r=t.g().add(this.group);if(i.push(r),s.buttonsEnabled){let o=t.rect().addClass("highcharts-scrollbar-button").add(r);this.chart.styledMode||o.attr({stroke:s.buttonBorderColor,"stroke-width":s.buttonBorderWidth,fill:s.buttonBackgroundColor}),o.attr(o.crisp({x:-.5,y:-.5,width:n,height:n,r:s.buttonBorderRadius},o.strokeWidth()));let a=t.path(m.swapXY([["M",n/2+(e?-1:1),n/2-3],["L",n/2+(e?-1:1),n/2+3],["L",n/2+(e?2:-2),n/2]],s.vertical)).addClass("highcharts-scrollbar-arrow").add(i[e]);this.chart.styledMode||a.attr({fill:s.buttonArrowColor})}}init(e,t,i){this.scrollbarButtons=[],this.renderer=e,this.userOptions=t,this.options=u(s,r.scrollbar,t),this.options.margin=p(this.options.margin,10),this.chart=i,this.size=p(this.options.size,this.options.height),t.enabled&&(this.render(),this.addEvents())}mouseDownHandler(e){let t=this.chart.pointer?.normalize(e)||e,i=this.cursorToScrollbarPosition(t);this.chartX=i.chartX,this.chartY=i.chartY,this.initPositions=[this.from,this.to],this.grabbedCenter=!0}mouseMoveHandler(e){let t;let i=this.chart.pointer?.normalize(e)||e,s=this.options.vertical?"chartY":"chartX",n=this.initPositions||[];this.grabbedCenter&&(!e.touches||0!==e.touches[0][s])&&(t=this.cursorToScrollbarPosition(i)[s]-this[s],this.hasDragged=!0,this.updatePosition(n[0]+t,n[1]+t),this.hasDragged&&d(this,"changed",{from:this.from,to:this.to,trigger:"scrollbar",DOMType:e.type,DOMEvent:e}))}mouseUpHandler(e){this.hasDragged&&d(this,"changed",{from:this.from,to:this.to,trigger:"scrollbar",DOMType:e.type,DOMEvent:e}),this.grabbedCenter=this.hasDragged=this.chartX=this.chartY=null}position(e,t,i,s){let{buttonsEnabled:n,margin:r=0,vertical:o}=this.options,a=this.rendered?"animate":"attr",l=s,h=0;this.group.show(),this.x=e,this.y=t+this.trackBorderWidth,this.width=i,this.height=s,this.xOffset=l,this.yOffset=h,o?(this.width=this.yOffset=i=h=this.size,this.xOffset=l=0,this.yOffset=h=n?this.size:0,this.barWidth=s-(n?2*i:0),this.x=e+=r):(this.height=s=this.size,this.xOffset=l=n?this.size:0,this.barWidth=i-(n?2*s:0),this.y=this.y+r),this.group[a]({translateX:e,translateY:this.y}),this.track[a]({width:i,height:s}),this.scrollbarButtons[1][a]({translateX:o?0:i-l,translateY:o?s-h:0})}removeEvents(){this._events.forEach(function(e){g.apply(null,e)}),this._events.length=0}render(){let e=this.renderer,t=this.options,i=this.size,s=this.chart.styledMode,n=e.g("scrollbar").attr({zIndex:t.zIndex}).hide().add();this.group=n,this.track=e.rect().addClass("highcharts-scrollbar-track").attr({r:t.trackBorderRadius||0,height:i,width:i}).add(n),s||this.track.attr({fill:t.trackBackgroundColor,stroke:t.trackBorderColor,"stroke-width":t.trackBorderWidth});let r=this.trackBorderWidth=this.track.strokeWidth();this.track.attr({x:-l(0,r),y:-l(0,r)}),this.scrollbarGroup=e.g().add(n),this.scrollbar=e.rect().addClass("highcharts-scrollbar-thumb").attr({height:i-r,width:i-r,r:t.barBorderRadius||0}).add(this.scrollbarGroup),this.scrollbarRifles=e.path(m.swapXY([["M",-3,i/4],["L",-3,2*i/3],["M",0,i/4],["L",0,2*i/3],["M",3,i/4],["L",3,2*i/3]],t.vertical)).addClass("highcharts-scrollbar-rifles").add(this.scrollbarGroup),s||(this.scrollbar.attr({fill:t.barBackgroundColor,stroke:t.barBorderColor,"stroke-width":t.barBorderWidth}),this.scrollbarRifles.attr({stroke:t.rifleColor,"stroke-width":1})),this.scrollbarStrokeWidth=this.scrollbar.strokeWidth(),this.scrollbarGroup.translate(-l(0,this.scrollbarStrokeWidth),-l(0,this.scrollbarStrokeWidth)),this.drawScrollbarButton(0),this.drawScrollbarButton(1)}setRange(e,t){let i,s;let n=this.options,r=n.vertical,o=n.minWidth,l=this.barWidth,c=!this.rendered||this.hasDragged||this.chart.navigator&&this.chart.navigator.hasDragged?"attr":"animate";if(!h(l))return;let d=l*Math.min(t,1);i=Math.ceil(l*(e=Math.max(e,0))),this.calculatedWidth=s=a(d-i),s=1?this.group.hide():this.group.show()),this.rendered=!0}shouldUpdateExtremes(e){return p(this.options.liveRedraw,t.svg&&!t.isTouchDevice&&!this.chart.boosted)||"mouseup"===e||"touchend"===e||!h(e)}trackClick(e){let t=this.chart.pointer?.normalize(e)||e,i=this.to-this.from,s=this.y+this.scrollbarTop,n=this.x+this.scrollbarLeft;this.options.vertical&&t.chartY>s||!this.options.vertical&&t.chartX>n?this.updatePosition(this.from+i,this.to+i):this.updatePosition(this.from-i,this.to-i),d(this,"changed",{from:this.from,to:this.to,trigger:"scrollbar",DOMEvent:e})}update(e){this.destroy(),this.init(this.chart.renderer,u(!0,this.options,e),this.chart)}updatePosition(e,t){t>1&&(e=a(1-a(t-e)),t=1),e<0&&(t=a(t-e),e=0),this.from=e,this.to=t}}return m.defaultOptions=s,r.scrollbar=u(!0,m.defaultOptions,r.scrollbar),m}),i(t,"Stock/Navigator/Navigator.js",[t["Core/Axis/Axis.js"],t["Stock/Navigator/ChartNavigatorComposition.js"],t["Core/Defaults.js"],t["Core/Globals.js"],t["Core/Axis/NavigatorAxisComposition.js"],t["Stock/Navigator/NavigatorComposition.js"],t["Stock/Scrollbar/Scrollbar.js"],t["Core/Renderer/SVG/SVGRenderer.js"],t["Core/Utilities.js"]],function(e,t,i,s,n,r,o,a,l){let{defaultOptions:h}=i,{isTouchDevice:c}=s,{prototype:{symbols:d}}=a,{addEvent:u,clamp:p,correctFloat:g,defined:m,destroyObjectProperties:b,erase:y,extend:f,find:x,fireEvent:v,isArray:A,isNumber:C,merge:w,pick:E,removeEvent:T,splat:M}=l;function S(e,...t){let i=[].filter.call(t,C);if(i.length)return Math[e].apply(0,i)}class k{static compose(e,i,s){t.compose(e,k),r.compose(e,i,s)}constructor(e){this.isDirty=!1,this.scrollbarHeight=0,this.init(e)}drawHandle(e,t,i,s){let n=this.navigatorOptions.handles.height;this.handles[t][s](i?{translateX:Math.round(this.left+this.height/2),translateY:Math.round(this.top+parseInt(e,10)+.5-n)}:{translateX:Math.round(this.left+parseInt(e,10)),translateY:Math.round(this.top+this.height/2-n/2-1)})}drawOutline(e,t,i,s){let n=this.navigatorOptions.maskInside,r=this.outline.strokeWidth(),o=r/2,a=r%2/2,l=this.scrollButtonSize,h=this.size,c=this.top,d=this.height,u=c-o,p=c+d,g=this.left,m,b;i?(m=c+t+a,t=c+e+a,b=[["M",g+d,c-l-a],["L",g+d,m],["L",g,m],["M",g,t],["L",g+d,t],["L",g+d,c+h+l]],n&&b.push(["M",g+d,m-o],["L",g+d,t+o])):(g-=l,e+=g+l-a,t+=g+l-a,b=[["M",g,u],["L",e,u],["L",e,p],["M",t,p],["L",t,u],["L",g+h+2*l,u]],n&&b.push(["M",e-o,u],["L",t+o,u])),this.outline[s]({d:b})}drawMasks(e,t,i,s){let n,r,o,a;let l=this.left,h=this.top,c=this.height;i?(o=[l,l,l],a=[h,h+e,h+t],r=[c,c,c],n=[e,t-e,this.size-t]):(o=[l,l+e,l+t],a=[h,h,h],r=[e,t-e,this.size-t],n=[c,c,c]),this.shades.forEach((e,t)=>{e[s]({x:o[t],y:a[t],width:r[t],height:n[t]})})}renderElements(){let e=this,t=e.navigatorOptions,i=t.maskInside,s=e.chart,n=s.inverted,r=s.renderer,o={cursor:n?"ns-resize":"ew-resize"},a=e.navigatorGroup??(e.navigatorGroup=r.g("navigator").attr({zIndex:8,visibility:"hidden"}).add());if([!i,i,!i].forEach((i,n)=>{let l=e.shades[n]??(e.shades[n]=r.rect().addClass("highcharts-navigator-mask"+(1===n?"-inside":"-outside")).add(a));s.styledMode||(l.attr({fill:i?t.maskFill:"rgba(0,0,0,0)"}),1===n&&l.css(o))}),e.outline||(e.outline=r.path().addClass("highcharts-navigator-outline").add(a)),s.styledMode||e.outline.attr({"stroke-width":t.outlineWidth,stroke:t.outlineColor}),t.handles?.enabled){let i=t.handles,{height:n,width:l}=i;[0,1].forEach(t=>{let h=i.symbols[t];if(e.handles[t]&&e.handles[t].symbolUrl===h){if(!e.handles[t].isImg&&e.handles[t].symbolName!==h){let i=d[h].call(d,-l/2-1,0,l,n);e.handles[t].attr({d:i}),e.handles[t].symbolName=h}}else e.handles[t]?.destroy(),e.handles[t]=r.symbol(h,-l/2-1,0,l,n,i),e.handles[t].attr({zIndex:7-t}).addClass("highcharts-navigator-handle highcharts-navigator-handle-"+["left","right"][t]).add(a),e.addMouseEvents();s.inverted&&e.handles[t].attr({rotation:90,rotationOriginX:Math.floor(-l/2),rotationOriginY:(n+l)/2}),s.styledMode||e.handles[t].attr({fill:i.backgroundColor,stroke:i.borderColor,"stroke-width":i.lineWidth,width:i.width,height:i.height,x:-l/2-1,y:0}).css(o)})}}update(e,t=!1){let i=this.chart,s=i.options.chart.inverted!==i.scrollbar?.options.vertical;if(w(!0,i.options.navigator,e),this.navigatorOptions=i.options.navigator||{},this.setOpposite(),m(e.enabled)||s)return this.destroy(),this.navigatorEnabled=e.enabled||this.navigatorEnabled,this.init(i);if(this.navigatorEnabled&&(this.isDirty=!0,!1===e.adaptToUpdatedData&&this.baseSeries.forEach(e=>{T(e,"updatedData",this.updatedDataHandler)},this),e.adaptToUpdatedData&&this.baseSeries.forEach(e=>{e.eventsToUnbind.push(u(e,"updatedData",this.updatedDataHandler))},this),(e.series||e.baseSeries)&&this.setBaseSeries(void 0,!1),e.height||e.xAxis||e.yAxis)){this.height=e.height??this.height;let t=this.getXAxisOffsets();this.xAxis.update({...e.xAxis,offsets:t,[i.inverted?"width":"height"]:this.height,[i.inverted?"height":"width"]:void 0},!1),this.yAxis.update({...e.yAxis,[i.inverted?"width":"height"]:this.height},!1)}t&&i.redraw()}render(e,t,i,s){let n=this.chart,r=this.xAxis,o=r.pointRange||0,a=r.navigatorAxis.fake?n.xAxis[0]:r,l=this.navigatorEnabled,h=this.rendered,c=n.inverted,d=n.xAxis[0].minRange,u=n.xAxis[0].options.maxRange,b=this.scrollButtonSize,y,f,x,A=this.scrollbarHeight,w,T;if(this.hasDragged&&!m(i))return;if(this.isDirty&&this.renderElements(),e=g(e-o/2),t=g(t+o/2),!C(e)||!C(t)){if(!h)return;i=0,s=E(r.width,a.width)}this.left=E(r.left,n.plotLeft+b+(c?n.plotWidth:0));let M=this.size=w=E(r.len,(c?n.plotHeight:n.plotWidth)-2*b);y=c?A:w+2*b,i=E(i,r.toPixels(e,!0)),s=E(s,r.toPixels(t,!0)),C(i)&&Math.abs(i)!==1/0||(i=0,s=y);let S=r.toValue(i,!0),k=r.toValue(s,!0),P=Math.abs(g(k-S));Pu&&(this.grabbedLeft?i=r.toPixels(k-u-o,!0):this.grabbedRight&&(s=r.toPixels(S+u+o,!0))),this.zoomedMax=p(Math.max(i,s),0,M),this.zoomedMin=p(this.fixedWidth?this.zoomedMax-this.fixedWidth:Math.min(i,s),0,M),this.range=this.zoomedMax-this.zoomedMin,M=Math.round(this.zoomedMax);let D=Math.round(this.zoomedMin);l&&(this.navigatorGroup.attr({visibility:"inherit"}),T=h&&!this.hasDragged?"animate":"attr",this.drawMasks(D,M,c,T),this.drawOutline(D,M,c,T),this.navigatorOptions.handles.enabled&&(this.drawHandle(D,0,c,T),this.drawHandle(M,1,c,T))),this.scrollbar&&(c?(x=this.top-b,f=this.left-A+(l||!a.opposite?0:(a.titleOffset||0)+a.axisTitleMargin),A=w+2*b):(x=this.top+(l?this.height:-A),f=this.left-b),this.scrollbar.position(f,x,y,A),this.scrollbar.setRange(this.zoomedMin/(w||1),this.zoomedMax/(w||1))),this.rendered=!0,this.isDirty=!1,v(this,"afterRender")}addMouseEvents(){let e=this,t=e.chart,i=t.container,s=[],n,r;e.mouseMoveHandler=n=function(t){e.onMouseMove(t)},e.mouseUpHandler=r=function(t){e.onMouseUp(t)},(s=e.getPartsEvents("mousedown")).push(u(t.renderTo,"mousemove",n),u(i.ownerDocument,"mouseup",r),u(t.renderTo,"touchmove",n),u(i.ownerDocument,"touchend",r)),s.concat(e.getPartsEvents("touchstart")),e.eventsToUnbind=s,e.series&&e.series[0]&&s.push(u(e.series[0].xAxis,"foundExtremes",function(){t.navigator.modifyNavigatorAxisExtremes()}))}getPartsEvents(e){let t=this,i=[];return["shades","handles"].forEach(function(s){t[s].forEach(function(n,r){i.push(u(n.element,e,function(e){t[s+"Mousedown"](e,r)}))})}),i}shadesMousedown(e,t){e=this.chart.pointer?.normalize(e)||e;let i=this.chart,s=this.xAxis,n=this.zoomedMin,r=this.size,o=this.range,a=this.left,l=e.chartX,h,c,d,u;i.inverted&&(l=e.chartY,a=this.top),1===t?(this.grabbedCenter=l,this.fixedWidth=o,this.dragOffset=l-n):(u=l-a-o/2,0===t?u=Math.max(0,u):2===t&&u+o>=r&&(u=r-o,this.reversedExtremes?(u-=o,c=this.getUnionExtremes().dataMin):h=this.getUnionExtremes().dataMax),u!==n&&(this.fixedWidth=o,m((d=s.navigatorAxis.toFixedRange(u,u+o,c,h)).min)&&v(this,"setRange",{min:Math.min(d.min,d.max),max:Math.max(d.min,d.max),redraw:!0,eventArguments:{trigger:"navigator"}})))}handlesMousedown(e,t){e=this.chart.pointer?.normalize(e)||e;let i=this.chart,s=i.xAxis[0],n=this.reversedExtremes;0===t?(this.grabbedLeft=!0,this.otherHandlePos=this.zoomedMax,this.fixedExtreme=n?s.min:s.max):(this.grabbedRight=!0,this.otherHandlePos=this.zoomedMin,this.fixedExtreme=n?s.max:s.min),i.setFixedRange(void 0)}onMouseMove(e){let t=this,i=t.chart,s=t.navigatorSize,n=t.range,r=t.dragOffset,o=i.inverted,a=t.left,l;(!e.touches||0!==e.touches[0].pageX)&&(l=(e=i.pointer?.normalize(e)||e).chartX,o&&(a=t.top,l=e.chartY),t.grabbedLeft?(t.hasDragged=!0,t.render(0,0,l-a,t.otherHandlePos)):t.grabbedRight?(t.hasDragged=!0,t.render(0,0,t.otherHandlePos,l-a)):t.grabbedCenter&&(t.hasDragged=!0,ls+r-n&&(l=s+r-n),t.render(0,0,l-r,l-r+n)),t.hasDragged&&t.scrollbar&&E(t.scrollbar.options.liveRedraw,!c&&!this.chart.boosted)&&(e.DOMType=e.type,setTimeout(function(){t.onMouseUp(e)},0)))}onMouseUp(e){let t,i,s,n,r,o;let a=this.chart,l=this.xAxis,h=this.scrollbar,c=e.DOMEvent||e,d=a.inverted,u=this.rendered&&!this.hasDragged?"animate":"attr";(this.hasDragged&&(!h||!h.hasDragged)||"scrollbar"===e.trigger)&&(s=this.getUnionExtremes(),this.zoomedMin===this.otherHandlePos?n=this.fixedExtreme:this.zoomedMax===this.otherHandlePos&&(r=this.fixedExtreme),this.zoomedMax===this.size&&(r=this.reversedExtremes?s.dataMin:s.dataMax),0===this.zoomedMin&&(n=this.reversedExtremes?s.dataMax:s.dataMin),m((o=l.navigatorAxis.toFixedRange(this.zoomedMin,this.zoomedMax,n,r)).min)&&v(this,"setRange",{min:Math.min(o.min,o.max),max:Math.max(o.min,o.max),redraw:!0,animation:!this.hasDragged&&null,eventArguments:{trigger:"navigator",triggerOp:"navigator-drag",DOMEvent:c}})),"mousemove"!==e.DOMType&&"touchmove"!==e.DOMType&&(this.grabbedLeft=this.grabbedRight=this.grabbedCenter=this.fixedWidth=this.fixedExtreme=this.otherHandlePos=this.hasDragged=this.dragOffset=null),this.navigatorEnabled&&C(this.zoomedMin)&&C(this.zoomedMax)&&(i=Math.round(this.zoomedMin),t=Math.round(this.zoomedMax),this.shades&&this.drawMasks(i,t,d,u),this.outline&&this.drawOutline(i,t,d,u),this.navigatorOptions.handles.enabled&&Object.keys(this.handles).length===this.handles.length&&(this.drawHandle(i,0,d,u),this.drawHandle(t,1,d,u)))}removeEvents(){this.eventsToUnbind&&(this.eventsToUnbind.forEach(function(e){e()}),this.eventsToUnbind=void 0),this.removeBaseSeriesEvents()}removeBaseSeriesEvents(){let e=this.baseSeries||[];this.navigatorEnabled&&e[0]&&(!1!==this.navigatorOptions.adaptToUpdatedData&&e.forEach(function(e){T(e,"updatedData",this.updatedDataHandler)},this),e[0].xAxis&&T(e[0].xAxis,"foundExtremes",this.modifyBaseAxisExtremes))}getXAxisOffsets(){return this.chart.inverted?[this.scrollButtonSize,0,-this.scrollButtonSize,0]:[0,-this.scrollButtonSize,0,this.scrollButtonSize]}init(t){let i=t.options,s=i.navigator||{},r=s.enabled,a=i.scrollbar||{},l=a.enabled,h=r&&s.height||0,c=l&&a.height||0,d=a.buttonsEnabled&&c||0;this.handles=[],this.shades=[],this.chart=t,this.setBaseSeries(),this.height=h,this.scrollbarHeight=c,this.scrollButtonSize=d,this.scrollbarEnabled=l,this.navigatorEnabled=r,this.navigatorOptions=s,this.scrollbarOptions=a,this.setOpposite();let p=this,g=p.baseSeries,m=t.xAxis.length,b=t.yAxis.length,y=g&&g[0]&&g[0].xAxis||t.xAxis[0]||{options:{}};if(t.isDirtyBox=!0,p.navigatorEnabled){let i=this.getXAxisOffsets();p.xAxis=new e(t,w({breaks:y.options.breaks,ordinal:y.options.ordinal,overscroll:y.options.overscroll},s.xAxis,{type:"datetime",yAxis:s.yAxis?.id,index:m,isInternal:!0,offset:0,keepOrdinalPadding:!0,startOnTick:!1,endOnTick:!1,minPadding:y.options.ordinal?0:y.options.minPadding,maxPadding:y.options.ordinal?0:y.options.maxPadding,zoomEnabled:!1},t.inverted?{offsets:i,width:h}:{offsets:i,height:h}),"xAxis"),p.yAxis=new e(t,w(s.yAxis,{alignTicks:!1,offset:0,index:b,isInternal:!0,reversed:E(s.yAxis&&s.yAxis.reversed,t.yAxis[0]&&t.yAxis[0].reversed,!1),zoomEnabled:!1},t.inverted?{width:h}:{height:h}),"yAxis"),g||s.series.data?p.updateNavigatorSeries(!1):0===t.series.length&&(p.unbindRedraw=u(t,"beforeRedraw",function(){t.series.length>0&&!p.series&&(p.setBaseSeries(),p.unbindRedraw())})),p.reversedExtremes=t.inverted&&!p.xAxis.reversed||!t.inverted&&p.xAxis.reversed,p.renderElements(),p.addMouseEvents()}else p.xAxis={chart:t,navigatorAxis:{fake:!0},translate:function(e,i){let s=t.xAxis[0],n=s.getExtremes(),r=s.len-2*d,o=S("min",s.options.min,n.dataMin),a=S("max",s.options.max,n.dataMax)-o;return i?e*a/r+o:r*(e-o)/a},toPixels:function(e){return this.translate(e)},toValue:function(e){return this.translate(e,!0)}},p.xAxis.navigatorAxis.axis=p.xAxis,p.xAxis.navigatorAxis.toFixedRange=n.prototype.toFixedRange.bind(p.xAxis.navigatorAxis);if(t.options.scrollbar.enabled){let e=w(t.options.scrollbar,{vertical:t.inverted});!C(e.margin)&&p.navigatorEnabled&&(e.margin=t.inverted?-3:3),t.scrollbar=p.scrollbar=new o(t.renderer,e,t),u(p.scrollbar,"changed",function(e){let t=p.size,i=t*this.to,s=t*this.from;p.hasDragged=p.scrollbar.hasDragged,p.render(0,0,s,i),this.shouldUpdateExtremes(e.DOMType)&&setTimeout(function(){p.onMouseUp(e)})})}p.addBaseSeriesEvents(),p.addChartEvents()}setOpposite(){let e=this.navigatorOptions,t=this.navigatorEnabled,i=this.chart;this.opposite=E(e.opposite,!!(!t&&i.inverted))}getUnionExtremes(e){let t;let i=this.chart.xAxis[0],s=this.xAxis,n=s.options,r=i.options;return e&&null===i.dataMin||(t={dataMin:E(n&&n.min,S("min",r.min,i.dataMin,s.dataMin,s.min)),dataMax:E(n&&n.max,S("max",r.max,i.dataMax,s.dataMax,s.max))}),t}setBaseSeries(e,t){let i=this.chart,s=this.baseSeries=[];e=e||i.options&&i.options.navigator.baseSeries||(i.series.length?x(i.series,e=>!e.options.isInternal).index:0),(i.series||[]).forEach((t,i)=>{!t.options.isInternal&&(t.options.showInNavigator||(i===e||t.options.id===e)&&!1!==t.options.showInNavigator)&&s.push(t)}),this.xAxis&&!this.xAxis.navigatorAxis.fake&&this.updateNavigatorSeries(!0,t)}updateNavigatorSeries(e,t){let i=this,s=i.chart,n=i.baseSeries,r={enableMouseTracking:!1,index:null,linkedTo:null,group:"nav",padXAxis:!1,xAxis:this.navigatorOptions.xAxis?.id,yAxis:this.navigatorOptions.yAxis?.id,showInLegend:!1,stacking:void 0,isInternal:!0,states:{inactive:{opacity:1}}},o=i.series=(i.series||[]).filter(e=>{let t=e.baseSeries;return!(0>n.indexOf(t))||(t&&(T(t,"updatedData",i.updatedDataHandler),delete t.navigatorSeries),e.chart&&e.destroy(),!1)}),a,l,c=i.navigatorOptions.series,d;n&&n.length&&n.forEach(e=>{let u=e.navigatorSeries,p=f({color:e.color,visible:e.visible},A(c)?h.navigator.series:c);if(u&&!1===i.navigatorOptions.adaptToUpdatedData)return;r.name="Navigator "+n.length,d=(a=e.options||{}).navigatorOptions||{},p.dataLabels=M(p.dataLabels),(l=w(a,r,p,d)).pointRange=E(p.pointRange,d.pointRange,h.plotOptions[l.type||"line"].pointRange);let g=d.data||p.data;i.hasNavigatorData=i.hasNavigatorData||!!g,l.data=g||a.data&&a.data.slice(0),u&&u.options?u.update(l,t):(e.navigatorSeries=s.initSeries(l),s.setSortedData(),e.navigatorSeries.baseSeries=e,o.push(e.navigatorSeries))}),(c.data&&!(n&&n.length)||A(c))&&(i.hasNavigatorData=!1,(c=M(c)).forEach((e,t)=>{r.name="Navigator "+(o.length+1),(l=w(h.navigator.series,{color:s.series[t]&&!s.series[t].options.isInternal&&s.series[t].color||s.options.colors[t]||s.options.colors[0]},r,e)).data=e.data,l.data&&(i.hasNavigatorData=!0,o.push(s.initSeries(l)))})),e&&this.addBaseSeriesEvents()}addBaseSeriesEvents(){let e=this,t=e.baseSeries||[];t[0]&&t[0].xAxis&&t[0].eventsToUnbind.push(u(t[0].xAxis,"foundExtremes",this.modifyBaseAxisExtremes)),t.forEach(i=>{i.eventsToUnbind.push(u(i,"show",function(){this.navigatorSeries&&this.navigatorSeries.setVisible(!0,!1)})),i.eventsToUnbind.push(u(i,"hide",function(){this.navigatorSeries&&this.navigatorSeries.setVisible(!1,!1)})),!1!==this.navigatorOptions.adaptToUpdatedData&&i.xAxis&&i.eventsToUnbind.push(u(i,"updatedData",this.updatedDataHandler)),i.eventsToUnbind.push(u(i,"remove",function(){t&&y(t,i),this.navigatorSeries&&(y(e.series,this.navigatorSeries),m(this.navigatorSeries.options)&&this.navigatorSeries.remove(!1),delete this.navigatorSeries)}))})}getBaseSeriesMin(e){return this.baseSeries.reduce(function(e,t){return Math.min(e,t.xData&&t.xData.length?t.xData[0]:e)},e)}modifyNavigatorAxisExtremes(){let e=this.xAxis;if(void 0!==e.getExtremes){let t=this.getUnionExtremes(!0);t&&(t.dataMin!==e.min||t.dataMax!==e.max)&&(e.min=t.dataMin,e.max=t.dataMax)}}modifyBaseAxisExtremes(){let e,t;let i=this.chart.navigator,s=this.getExtremes(),n=s.min,r=s.max,o=s.dataMin,a=s.dataMax,l=r-n,h=i.stickToMin,c=i.stickToMax,d=E(this.ordinal?.convertOverscroll(this.options.overscroll),0),u=i.series&&i.series[0],p=!!this.setExtremes;!(this.eventArgs&&"rangeSelectorButton"===this.eventArgs.trigger)&&(h&&(e=(t=o)+l),c&&(e=a+d,h||(t=Math.max(o,e-l,i.getBaseSeriesMin(u&&u.xData?u.xData[0]:-Number.MAX_VALUE)))),p&&(h||c)&&C(t)&&(this.min=this.userMin=t,this.max=this.userMax=e)),i.stickToMin=i.stickToMax=null}updatedDataHandler(){let e=this.chart.navigator,t=this.navigatorSeries,i=e.reversedExtremes?0===Math.round(e.zoomedMin):Math.round(e.zoomedMax)>=Math.round(e.size);e.stickToMax=E(this.chart.options.navigator&&this.chart.options.navigator.stickToMax,i),e.stickToMin=e.shouldStickToMin(this,e),t&&!e.hasNavigatorData&&(t.options.pointStart=this.xData[0],t.setData(this.options.data,!1,null,!1))}shouldStickToMin(e,t){let i=t.getBaseSeriesMin(e.xData[0]),s=e.xAxis,n=s.max,r=s.min,o=s.options.range;return!!(C(n)&&C(r))&&(o&&n-i>0?n-i{e.destroy&&e.destroy()}),["series","xAxis","yAxis","shades","outline","scrollbarTrack","scrollbarRifles","scrollbarGroup","scrollbar","navigatorGroup","rendered"].forEach(e=>{this[e]&&this[e].destroy&&this[e].destroy(),this[e]=null}),[this.handles].forEach(e=>{b(e)}),this.navigatorEnabled=!1}}return k}),i(t,"Accessibility/Components/NavigatorComponent.js",[t["Accessibility/AccessibilityComponent.js"],t["Accessibility/Utils/Announcer.js"],t["Accessibility/KeyboardNavigationHandler.js"],t["Stock/Navigator/Navigator.js"],t["Core/Animation/AnimationUtilities.js"],t["Core/Templating.js"],t["Core/Utilities.js"],t["Accessibility/Utils/HTMLUtilities.js"],t["Accessibility/Utils/ChartUtilities.js"]],function(e,t,i,s,n,r,o,a,l){let{animObject:h}=n,{format:c}=r,{clamp:d,pick:u,syncTimeout:p}=o,{getFakeMouseEvent:g}=a,{getAxisRangeDescription:m,fireEventOnWrappedOrUnwrappedElement:b}=l;return class extends e{init(){let e=this.chart,i=this;this.announcer=new t(e,"polite"),this.addEvent(s,"afterRender",function(){this.chart===i.chart&&this.chart.renderer&&p(()=>{i.proxyProvider.updateGroupProxyElementPositions("navigator"),i.updateHandleValues()},h(u(this.chart.renderer.globalAnimation,!0)).duration)})}onChartUpdate(){let e=this.chart,t=e.options,i=t.navigator;if(i.enabled&&i.accessibility?.enabled){let i=t.accessibility.landmarkVerbosity,s=t.lang.accessibility?.navigator.groupLabel;this.proxyProvider.removeGroup("navigator"),this.proxyProvider.addGroup("navigator","div",{role:"all"===i?"region":"group","aria-label":c(s,{chart:e},e)});let n=t.lang.accessibility?.navigator.handleLabel;[0,1].forEach(t=>{let i=this.getHandleByIx(t);if(i){let s=this.proxyProvider.addProxyElement("navigator",{click:i},"input",{type:"range","aria-label":c(n,{handleIx:t,chart:e},e)});this[t?"maxHandleProxy":"minHandleProxy"]=s.innerElement,s.innerElement.style.pointerEvents="none",s.innerElement.oninput=()=>this.updateNavigator()}}),this.updateHandleValues()}else this.proxyProvider.removeGroup("navigator")}getNavigatorHandleNavigation(e){let t=this,s=this.chart,n=e?this.maxHandleProxy:this.minHandleProxy,r=this.keyCodes;return new i(s,{keyCodeMap:[[[r.left,r.right,r.up,r.down],function(i){if(n){let o=i===r.left||i===r.up?-1:1;n.value=""+d(parseFloat(n.value)+o,0,100),t.updateNavigator(()=>{let i=t.getHandleByIx(e);i&&s.setFocusToElement(i,n)})}return this.response.success}]],init:()=>{s.setFocusToElement(this.getHandleByIx(e),n)},validate:()=>!!(this.getHandleByIx(e)&&n&&s.options.navigator.accessibility?.enabled)})}getKeyboardNavigation(){return[this.getNavigatorHandleNavigation(0),this.getNavigatorHandleNavigation(1)]}destroy(){this.updateNavigatorThrottleTimer&&clearTimeout(this.updateNavigatorThrottleTimer),this.proxyProvider.removeGroup("navigator"),this.announcer&&this.announcer.destroy()}updateHandleValues(){let e=this.chart.navigator;if(e&&this.minHandleProxy&&this.maxHandleProxy){let t=e.size;this.minHandleProxy.value=""+Math.round(e.zoomedMin/t*100),this.maxHandleProxy.value=""+Math.round(e.zoomedMax/t*100)}}getHandleByIx(e){let t=this.chart.navigator;return t&&t.handles&&t.handles[e]}updateNavigator(e){this.updateNavigatorThrottleTimer&&clearTimeout(this.updateNavigatorThrottleTimer),this.updateNavigatorThrottleTimer=setTimeout((e=>{let t=this.chart,{navigator:i,pointer:s}=t;if(i&&s&&this.minHandleProxy&&this.maxHandleProxy){let n=s.getChartPosition(),r=parseFloat(this.minHandleProxy.value)/100*i.size,o=parseFloat(this.maxHandleProxy.value)/100*i.size;[[0,"mousedown",i.zoomedMin],[0,"mousemove",r],[0,"mouseup",r],[1,"mousedown",i.zoomedMax],[1,"mousemove",o],[1,"mouseup",o]].forEach(([e,t,s])=>{let r=this.getHandleByIx(e)?.element;r&&b(r,g(t,{x:n.left+i.left+s,y:n.top+i.top},r))}),e&&e();let a=t.options.lang.accessibility?.navigator.changeAnnouncement,l=m(t.xAxis[0]);this.announcer.announce(c(a,{axisRangeDescription:l,chart:t},t))}}).bind(this,e),20)}}}),i(t,"Accessibility/Components/SeriesComponent/SeriesDescriber.js",[t["Accessibility/Components/AnnotationsA11y.js"],t["Accessibility/Utils/ChartUtilities.js"],t["Core/Templating.js"],t["Accessibility/Utils/HTMLUtilities.js"],t["Core/Utilities.js"]],function(e,t,i,s,n){let{getPointAnnotationTexts:r}=e,{getAxisDescription:o,getSeriesFirstPointElement:a,getSeriesA11yElement:l,unhideChartElementFromAT:h}=t,{format:c,numberFormat:d}=i,{reverseChildNodes:u,stripHTMLTagsFromString:p}=s,{find:g,isNumber:m,isString:b,pick:y,defined:f}=n;function x(e){let t=e.chart.options.accessibility.series.pointDescriptionEnabledThreshold;return!!(!1!==t&&e.points&&e.points.length>=+t)}function v(e,t){let i=e.series,s=i.chart,n=s.options.accessibility.point||{},r=i.options.accessibility&&i.options.accessibility.point||{},o=i.tooltipOptions||{},a=s.options.lang;return m(t)?d(t,r.valueDecimals||n.valueDecimals||o.valueDecimals||-1,a.decimalPoint,a.accessibility.thousandsSep||a.thousandsSep):t}function A(e,t){let i=e[t];return e.chart.langFormat("accessibility.series."+t+"Description",{name:o(i),series:e})}function C(e){let t=e.series,i=t.chart.series.length>1||t.options.name,s=function(e){let t=e.series,i=t.chart,s=t.options.accessibility,n=s&&s.point&&s.point.valueDescriptionFormat||i.options.accessibility.point.valueDescriptionFormat,r=y(t.xAxis&&t.xAxis.options.accessibility&&t.xAxis.options.accessibility.enabled,!i.angular&&"flowmap"!==t.type),o=r?function(e){let t=function(e){let t=e.series,i=t.chart,s=t.options.accessibility&&t.options.accessibility.point||{},n=i.options.accessibility.point||{},r=t.xAxis&&t.xAxis.dateTime;if(r){let t=r.getXDateFormat(e.x||0,i.options.tooltip.dateTimeLabelFormats),o=s.dateFormatter&&s.dateFormatter(e)||n.dateFormatter&&n.dateFormatter(e)||s.dateFormat||n.dateFormat||t;return i.time.dateFormat(o,e.x||0,void 0)}}(e),i=(e.series.xAxis||{}).categories&&f(e.category)&&(""+e.category).replace("
    "," "),s=f(e.id)&&0>(""+e.id).indexOf("highcharts-"),n="x, "+e.x;return e.name||t||i||(s?e.id:n)}(e):"";return c(n,{point:e,index:f(e.index)?e.index+1:"",xDescription:o,value:function(e){let t=e.series,i=t.chart.options.accessibility.point||{},s=t.chart.options.accessibility&&t.chart.options.accessibility.point||{},n=t.tooltipOptions||{},r=s.valuePrefix||i.valuePrefix||n.valuePrefix||"",o=s.valueSuffix||i.valueSuffix||n.valueSuffix||"",a=void 0!==e.value?"value":"y",l=v(e,e[a]);return e.isNull?t.chart.langFormat("accessibility.series.nullPointValue",{point:e}):t.pointArrayMap?function(e,t,i){let s=t||"",n=i||"",r=function(t){let i=v(e,y(e[t],e.options[t]));return void 0!==i?t+": "+s+i+n:i};return e.series.pointArrayMap.reduce(function(e,t){let i=r(t);return i?e+(e.length?", ":"")+i:e},"")}(e,r,o):r+l+o}(e),separator:r?", ":""},i)}(e),n=e.options&&e.options.accessibility&&e.options.accessibility.description,o=i?" "+t.name+".":"",a=function(e){let t=e.series.chart,i=r(e);return i.length?t.langFormat("accessibility.series.pointAnnotationsDescription",{point:e,annotations:i}):""}(e);return e.accessibility=e.accessibility||{},e.accessibility.valueDescription=s,s+(n?" "+n:"")+o+(a?" "+a:"")}function w(e){let t=e.chart,i=t.types||[],s=function(e){let t=(e.options.accessibility||{}).description;return t&&e.chart.langFormat("accessibility.series.description",{description:t,series:e})||""}(e),n=function(i){return t[i]&&t[i].length>1&&e[i]},r=e.index+1,o=A(e,"xAxis"),a=A(e,"yAxis"),l={seriesNumber:r,series:e,chart:t},h=i.length>1?"Combination":"",d=t.langFormat("accessibility.series.summary."+e.type+h,l)||t.langFormat("accessibility.series.summary.default"+h,l),u=(n("yAxis")?" "+a+".":"")+(n("xAxis")?" "+o+".":"");return c(y(e.options.accessibility&&e.options.accessibility.descriptionFormat,t.options.accessibility.series.descriptionFormat,""),{seriesDescription:d,authorDescription:s?" "+s:"",axisDescription:u,series:e,chart:t,seriesNumber:r},void 0)}return{defaultPointDescriptionFormatter:C,defaultSeriesDescriptionFormatter:w,describeSeries:function(e){let t=e.chart,i=a(e),s=l(e),n=t.is3d&&t.is3d();s&&(s.lastChild!==i||n||u(s),function(e){let t=function(e){let t=e.options.accessibility||{};return!x(e)&&!t.exposeAsGroupOnly}(e),i=function(e){let t=e.chart.options.accessibility.keyboardNavigation.seriesNavigation;return!!(e.points&&(e.points.length<+t.pointNavigationEnabledThreshold||!1===t.pointNavigationEnabledThreshold))}(e),s=e.chart.options.accessibility.point.describeNull;(t||i)&&e.points.forEach(i=>{let n=i.graphic&&i.graphic.element||function(e){let t=e.series,i=t&&t.chart,s=t&&t.is("sunburst"),n=e.isNull,r=i&&i.options.accessibility.point.describeNull;return n&&!s&&r}(i)&&function(e){let t=e.series,i=function(e){let t=e.index;return e.series&&e.series.data&&f(t)&&g(e.series.data,function(e){return!!(e&&void 0!==e.index&&e.index>t&&e.graphic&&e.graphic.element)})||null}(e),s=i&&i.graphic,n=s?s.parentGroup:t.graph||t.group,r=i?{x:y(e.plotX,i.plotX,0),y:y(e.plotY,i.plotY,0)}:{x:y(e.plotX,0),y:y(e.plotY,0)},o=function(e,t){let i=e.series.chart.renderer.rect(t.x,t.y,1,1);return i.attr({class:"highcharts-a11y-mock-point",fill:"none",opacity:0,"fill-opacity":0,"stroke-opacity":0}),i}(e,r);if(n&&n.element)return e.graphic=o,e.hasMockGraphic=!0,o.add(n),n.element.insertBefore(o.element,s?s.element:null),o.element}(i),r=i.options&&i.options.accessibility&&!1===i.options.accessibility.enabled;if(n){if(i.isNull&&!s){n.setAttribute("aria-hidden",!0);return}n.setAttribute("tabindex","-1"),e.chart.styledMode||(n.style.outline="none"),t&&!r?function(e,t){let i=e.series,s=i.options.accessibility?.point||{},n=i.chart.options.accessibility.point||{},r=p(b(s.descriptionFormat)&&c(s.descriptionFormat,e,i.chart)||s.descriptionFormatter?.(e)||b(n.descriptionFormat)&&c(n.descriptionFormat,e,i.chart)||n.descriptionFormatter?.(e)||C(e),i.chart.renderer.forExport);t.setAttribute("role","img"),t.setAttribute("aria-label",r)}(i,n):n.setAttribute("aria-hidden",!0)}})}(e),h(t,s),function(e){let t=e.chart,i=t.options.chart,s=i.options3d&&i.options3d.enabled,n=t.series.length>1,r=t.options.accessibility.series.describeSingleSeries,o=(e.options.accessibility||{}).exposeAsGroupOnly;return!(s&&n)&&(n||r||o||x(e))}(e)?function(e,t){let i=e.options.accessibility||{},s=e.chart.options.accessibility,n=s.landmarkVerbosity;i.exposeAsGroupOnly?t.setAttribute("role","img"):"all"===n?t.setAttribute("role","region"):t.setAttribute("role","group"),t.setAttribute("tabindex","-1"),e.chart.styledMode||(t.style.outline="none"),t.setAttribute("aria-label",p(s.series.descriptionFormatter&&s.series.descriptionFormatter(e)||w(e),e.chart.renderer.forExport))}(e,s):s.removeAttribute("aria-label"))}}}),i(t,"Accessibility/Components/SeriesComponent/NewDataAnnouncer.js",[t["Core/Globals.js"],t["Core/Utilities.js"],t["Accessibility/Utils/Announcer.js"],t["Accessibility/Utils/ChartUtilities.js"],t["Accessibility/Utils/EventProvider.js"],t["Accessibility/Components/SeriesComponent/SeriesDescriber.js"]],function(e,t,i,s,n,r){let{composed:o}=e,{addEvent:a,defined:l,pushUnique:h}=t,{getChartTitle:c}=s,{defaultPointDescriptionFormatter:d,defaultSeriesDescriptionFormatter:u}=r;function p(e){return!!e.options.accessibility.announceNewData.enabled}class g{constructor(e){this.dirty={allSeries:{}},this.lastAnnouncementTime=0,this.chart=e}init(){let e=this.chart,t=e.options.accessibility.announceNewData.interruptUser?"assertive":"polite";this.lastAnnouncementTime=0,this.dirty={allSeries:{}},this.eventProvider=new n,this.announcer=new i(e,t),this.addEventListeners()}destroy(){this.eventProvider.removeAddedEvents(),this.announcer.destroy()}addEventListeners(){let e=this,t=this.chart,i=this.eventProvider;i.addEvent(t,"afterApplyDrilldown",function(){e.lastAnnouncementTime=0}),i.addEvent(t,"afterAddSeries",function(t){e.onSeriesAdded(t.series)}),i.addEvent(t,"redraw",function(){e.announceDirtyData()})}onSeriesAdded(e){p(this.chart)&&(this.dirty.hasDirty=!0,this.dirty.allSeries[e.name+e.index]=e,this.dirty.newSeries=l(this.dirty.newSeries)?void 0:e)}announceDirtyData(){let e=this.chart,t=this;if(e.options.accessibility.announceNewData&&this.dirty.hasDirty){let e=this.dirty.newPoint;e&&(e=function(e){let t=e.series.data.filter(t=>e.x===t.x&&e.y===t.y);return 1===t.length?t[0]:e}(e)),this.queueAnnouncement(Object.keys(this.dirty.allSeries).map(e=>t.dirty.allSeries[e]),this.dirty.newSeries,e),this.dirty={allSeries:{}}}}queueAnnouncement(e,t,i){let s=this.chart.options.accessibility.announceNewData;if(s.enabled){let n=+new Date,r=n-this.lastAnnouncementTime,o=Math.max(0,s.minAnnounceInterval-r),a=function(e,t){let i=(e||[]).concat(t||[]).reduce((e,t)=>(e[t.name+t.index]=t,e),{});return Object.keys(i).map(e=>i[e])}(this.queuedAnnouncement&&this.queuedAnnouncement.series,e),l=this.buildAnnouncementMessage(a,t,i);l&&(this.queuedAnnouncement&&clearTimeout(this.queuedAnnouncementTimer),this.queuedAnnouncement={time:n,message:l,series:a},this.queuedAnnouncementTimer=setTimeout(()=>{this&&this.announcer&&(this.lastAnnouncementTime=+new Date,this.announcer.announce(this.queuedAnnouncement.message),delete this.queuedAnnouncement,delete this.queuedAnnouncementTimer)},o))}}buildAnnouncementMessage(t,i,s){let n=this.chart,r=n.options.accessibility.announceNewData;if(r.announcementFormatter){let e=r.announcementFormatter(t,i,s);if(!1!==e)return e.length?e:null}let o=e.charts&&e.charts.length>1?"Multiple":"Single",a=i?"newSeriesAnnounce"+o:s?"newPointAnnounce"+o:"newDataAnnounce",l=c(n);return n.langFormat("accessibility.announceNewData."+a,{chartTitle:l,seriesDesc:i?u(i):null,pointDesc:s?d(s):null,point:s,series:i})}}return function(e){function t(e){let t=this.chart,i=t.accessibility?.components.series.newDataAnnouncer;i&&i.chart===t&&p(t)&&(i.dirty.newPoint=l(i.dirty.newPoint)?void 0:e.point)}function i(){let e=this.chart,t=e.accessibility?.components.series.newDataAnnouncer;t&&t.chart===e&&p(e)&&(t.dirty.hasDirty=!0,t.dirty.allSeries[this.name+this.index]=this)}e.compose=function(e){h(o,"A11y.NDA")&&(a(e,"addPoint",t),a(e,"updatedData",i))}}(g||(g={})),g}),i(t,"Accessibility/ProxyElement.js",[t["Core/Globals.js"],t["Core/Utilities.js"],t["Accessibility/Utils/EventProvider.js"],t["Accessibility/Utils/ChartUtilities.js"],t["Accessibility/Utils/HTMLUtilities.js"]],function(e,t,i,s,n){let{doc:r}=e,{attr:o,css:a,merge:l}=t,{fireEventOnWrappedOrUnwrappedElement:h}=s,{cloneMouseEvent:c,cloneTouchEvent:d,getFakeMouseEvent:u,removeElement:p}=n;return class{constructor(e,t,s="button",n,o){this.chart=e,this.target=t,this.eventProvider=new i;let a=this.innerElement=r.createElement(s),l=this.element=n?r.createElement(n):a;e.styledMode||this.hideElementVisually(a),n&&("li"!==n||e.styledMode||(l.style.listStyle="none"),l.appendChild(a),this.element=l),this.updateTarget(t,o)}click(){let e=this.getTargetPosition();e.x+=e.width/2,e.y+=e.height/2;let t=u("click",e);h(this.target.click,t)}updateTarget(e,t){this.target=e,this.updateCSSClassName();let i=t||{};Object.keys(i).forEach(e=>{null===i[e]&&delete i[e]});let s=this.getTargetAttr(e.click,"aria-label");o(this.innerElement,l(s?{"aria-label":s}:{},i)),this.eventProvider.removeAddedEvents(),this.addProxyEventsToElement(this.innerElement,e.click),this.refreshPosition()}refreshPosition(){let e=this.getTargetPosition();a(this.innerElement,{width:(e.width||1)+"px",height:(e.height||1)+"px",left:(Math.round(e.x)||0)+"px",top:(Math.round(e.y)||0)+"px"})}remove(){this.eventProvider.removeAddedEvents(),p(this.element)}updateCSSClassName(){let e=e=>e.indexOf("highcharts-no-tooltip")>-1,t=this.chart.legend,i=t.group&&t.group.div,s=e(i&&i.className||""),n=e(this.getTargetAttr(this.target.click,"class")||"");this.innerElement.className=s||n?"highcharts-a11y-proxy-element highcharts-no-tooltip":"highcharts-a11y-proxy-element"}addProxyEventsToElement(e,t){["click","touchstart","touchend","touchcancel","touchmove","mouseover","mouseenter","mouseleave","mouseout"].forEach(i=>{let s=0===i.indexOf("touch");this.eventProvider.addEvent(e,i,e=>{let i=s?d(e):c(e);t&&h(t,i),e.stopPropagation(),s||e.preventDefault()},{passive:!1})})}hideElementVisually(e){a(e,{borderWidth:0,backgroundColor:"transparent",cursor:"pointer",outline:"none",opacity:.001,filter:"alpha(opacity=1)",zIndex:999,overflow:"hidden",padding:0,margin:0,display:"block",position:"absolute","-ms-filter":"progid:DXImageTransform.Microsoft.Alpha(Opacity=1)"})}getTargetPosition(){let e=this.target.click,t=e.element?e.element:e,i=this.target.visual||t,s=this.chart.renderTo,n=this.chart.pointer;if(s&&i?.getBoundingClientRect&&n){let e=i.getBoundingClientRect(),t=n.getChartPosition();return{x:(e.left-t.left)/t.scaleX,y:(e.top-t.top)/t.scaleY,width:e.right/t.scaleX-e.left/t.scaleX,height:e.bottom/t.scaleY-e.top/t.scaleY}}return{x:0,y:0,width:1,height:1}}getTargetAttr(e,t){return e.element?e.element.getAttribute(t):e.getAttribute(t)}}}),i(t,"Accessibility/ProxyProvider.js",[t["Core/Globals.js"],t["Core/Utilities.js"],t["Accessibility/Utils/ChartUtilities.js"],t["Accessibility/Utils/DOMElementProvider.js"],t["Accessibility/Utils/HTMLUtilities.js"],t["Accessibility/ProxyElement.js"]],function(e,t,i,s,n,r){let{doc:o}=e,{attr:a,css:l}=t,{unhideChartElementFromAT:h}=i,{removeChildNodes:c}=n;return class{constructor(e){this.chart=e,this.domElementProvider=new s,this.groups={},this.groupOrder=[],this.beforeChartProxyPosContainer=this.createProxyPosContainer("before"),this.afterChartProxyPosContainer=this.createProxyPosContainer("after"),this.update()}addProxyElement(e,t,i="button",s){let n=this.groups[e];if(!n)throw Error("ProxyProvider.addProxyElement: Invalid group key "+e);let o="ul"===n.type||"ol"===n.type?"li":void 0,a=new r(this.chart,t,i,o,s);return n.proxyContainerElement.appendChild(a.element),n.proxyElements.push(a),a}addGroup(e,t="div",i){let s;let n=this.groups[e];if(n)return n.groupElement;let r=this.domElementProvider.createElement(t);return i&&i.role&&"div"!==t?(s=this.domElementProvider.createElement("div")).appendChild(r):s=r,s.className="highcharts-a11y-proxy-group highcharts-a11y-proxy-group-"+e.replace(/\W/g,"-"),this.groups[e]={proxyContainerElement:r,groupElement:s,type:t,proxyElements:[]},a(s,i||{}),"ul"===t&&r.setAttribute("role","list"),this.afterChartProxyPosContainer.appendChild(s),this.updateGroupOrder(this.groupOrder),s}updateGroupAttrs(e,t){let i=this.groups[e];if(!i)throw Error("ProxyProvider.updateGroupAttrs: Invalid group key "+e);a(i.groupElement,t)}updateGroupOrder(e){if(this.groupOrder=e.slice(),this.isDOMOrderGroupOrder())return;let t=e.indexOf("series"),i=t>-1?e.slice(0,t):e,s=t>-1?e.slice(t+1):[],n=o.activeElement;["before","after"].forEach(e=>{let t=this["before"===e?"beforeChartProxyPosContainer":"afterChartProxyPosContainer"];c(t),("before"===e?i:s).forEach(e=>{let i=this.groups[e];i&&t.appendChild(i.groupElement)})}),(this.beforeChartProxyPosContainer.contains(n)||this.afterChartProxyPosContainer.contains(n))&&n&&n.focus&&n.focus()}clearGroup(e){let t=this.groups[e];if(!t)throw Error("ProxyProvider.clearGroup: Invalid group key "+e);c(t.proxyContainerElement)}removeGroup(e){let t=this.groups[e];t&&(this.domElementProvider.removeElement(t.groupElement),t.groupElement!==t.proxyContainerElement&&this.domElementProvider.removeElement(t.proxyContainerElement),delete this.groups[e])}update(){this.updatePosContainerPositions(),this.updateGroupOrder(this.groupOrder),this.updateProxyElementPositions()}updateProxyElementPositions(){Object.keys(this.groups).forEach(this.updateGroupProxyElementPositions.bind(this))}updateGroupProxyElementPositions(e){let t=this.groups[e];t&&t.proxyElements.forEach(e=>e.refreshPosition())}destroy(){this.domElementProvider.destroyCreatedElements()}createProxyPosContainer(e){let t=this.domElementProvider.createElement("div");return t.setAttribute("aria-hidden","false"),t.className="highcharts-a11y-proxy-container"+(e?"-"+e:""),l(t,{top:"0",left:"0"}),this.chart.styledMode||(t.style.whiteSpace="nowrap",t.style.position="absolute"),t}getCurrentGroupOrderInDOM(){let e=e=>{let t=Object.keys(this.groups),i=t.length;for(;i--;){let s=t[i],n=this.groups[s];if(n&&e===n.groupElement)return s}},t=t=>{let i=[],s=t.children;for(let t=0;t"series"===e||!!this.groups[e]),i=e.length;if(i!==t.length)return!1;for(;i--;)if(e[i]!==t[i])return!1;return!0}updatePosContainerPositions(){let e=this.chart;if(e.renderer.forExport)return;let t=e.renderer.box;e.container.insertBefore(this.afterChartProxyPosContainer,t.nextSibling),e.container.insertBefore(this.beforeChartProxyPosContainer,t),h(this.chart,this.afterChartProxyPosContainer),h(this.chart,this.beforeChartProxyPosContainer)}}}),i(t,"Accessibility/Components/RangeSelectorComponent.js",[t["Accessibility/AccessibilityComponent.js"],t["Accessibility/Utils/Announcer.js"],t["Accessibility/Utils/ChartUtilities.js"],t["Accessibility/KeyboardNavigationHandler.js"],t["Core/Utilities.js"]],function(e,t,i,s,n){let{unhideChartElementFromAT:r,getAxisRangeDescription:o}=i,{addEvent:a,attr:l}=n;class h extends e{init(){let e=this.chart;this.announcer=new t(e,"polite")}onChartUpdate(){let e=this.chart,t=this,i=e.rangeSelector;i&&(this.updateSelectorVisibility(),this.setDropdownAttrs(),i.buttons&&i.buttons.length&&i.buttons.forEach(e=>{t.setRangeButtonAttrs(e)}),i.maxInput&&i.minInput&&["minInput","maxInput"].forEach(function(s,n){let o=i[s];o&&(r(e,o),t.setRangeInputAttrs(o,"accessibility.rangeSelector."+(n?"max":"min")+"InputLabel"))}))}updateSelectorVisibility(){let e=this.chart,t=e.rangeSelector,i=t&&t.dropdown,s=t&&t.buttons||[],n=e=>e.setAttribute("aria-hidden",!0);t&&t.hasVisibleDropdown&&i?(r(e,i),s.forEach(e=>n(e.element))):(i&&n(i),s.forEach(t=>r(e,t.element)))}setDropdownAttrs(){let e=this.chart,t=e.rangeSelector&&e.rangeSelector.dropdown;if(t){let i=e.langFormat("accessibility.rangeSelector.dropdownLabel",{rangeTitle:e.options.lang.rangeSelectorZoom});t.setAttribute("aria-label",i),t.setAttribute("tabindex",-1)}}setRangeButtonAttrs(e){l(e.element,{tabindex:-1,role:"button"})}setRangeInputAttrs(e,t){let i=this.chart;l(e,{tabindex:-1,"aria-label":i.langFormat(t,{chart:i})})}onButtonNavKbdArrowKey(e,t){let i=e.response,s=this.keyCodes,n=this.chart,r=n.options.accessibility.keyboardNavigation.wrapAround,o=t===s.left||t===s.up?-1:1;return n.highlightRangeSelectorButton(n.highlightedRangeSelectorItemIx+o)?i.success:r?(e.init(o),i.success):i[o>0?"next":"prev"]}onButtonNavKbdClick(e){let t=e.response,i=this.chart;return 3!==i.oldRangeSelectorItemState&&this.fakeClickEvent(i.rangeSelector.buttons[i.highlightedRangeSelectorItemIx].element),t.success}onAfterBtnClick(){let e=this.chart,t=o(e.xAxis[0]),i=e.langFormat("accessibility.rangeSelector.clickButtonAnnouncement",{chart:e,axisRangeDescription:t});i&&this.announcer.announce(i)}onInputKbdMove(e){let t=this.chart,i=t.rangeSelector,s=t.highlightedInputRangeIx=(t.highlightedInputRangeIx||0)+e;if(s>1||s<0){if(t.accessibility)return t.accessibility.keyboardNavigation.exiting=!0,t.accessibility.keyboardNavigation.tabindexContainer.focus(),t.accessibility.keyboardNavigation.move(e)}else if(i){let e=i[s?"maxDateBox":"minDateBox"],n=i[s?"maxInput":"minInput"];e&&n&&t.setFocusToElement(e,n)}return!0}onInputNavInit(e){let t=this,i=this.chart,s=e>0?0:1,n=i.rangeSelector,r=n&&n[s?"maxDateBox":"minDateBox"],o=n&&n.minInput,l=n&&n.maxInput;if(i.highlightedInputRangeIx=s,r&&o&&l){i.setFocusToElement(r,s?l:o),this.removeInputKeydownHandler&&this.removeInputKeydownHandler();let e=e=>{(e.which||e.keyCode)===this.keyCodes.tab&&t.onInputKbdMove(e.shiftKey?-1:1)&&(e.preventDefault(),e.stopPropagation())},n=a(o,"keydown",e),h=a(l,"keydown",e);this.removeInputKeydownHandler=()=>{n(),h()}}}onInputNavTerminate(){let e=this.chart.rangeSelector||{};e.maxInput&&e.hideInput("max"),e.minInput&&e.hideInput("min"),this.removeInputKeydownHandler&&(this.removeInputKeydownHandler(),delete this.removeInputKeydownHandler)}initDropdownNav(){let e=this.chart,t=e.rangeSelector,i=t&&t.dropdown;t&&i&&(e.setFocusToElement(t.buttonGroup,i),this.removeDropdownKeydownHandler&&this.removeDropdownKeydownHandler(),this.removeDropdownKeydownHandler=a(i,"keydown",t=>{let i=(t.which||t.keyCode)===this.keyCodes.tab,s=e.accessibility;i&&(t.preventDefault(),t.stopPropagation(),s&&(s.keyboardNavigation.tabindexContainer.focus(),s.keyboardNavigation.move(t.shiftKey?-1:1)))}))}getRangeSelectorButtonNavigation(){let e=this.chart,t=this.keyCodes,i=this;return new s(e,{keyCodeMap:[[[t.left,t.right,t.up,t.down],function(e){return i.onButtonNavKbdArrowKey(this,e)}],[[t.enter,t.space],function(){return i.onButtonNavKbdClick(this)}]],validate:function(){return!!(e.rangeSelector&&e.rangeSelector.buttons&&e.rangeSelector.buttons.length)},init:function(t){let s=e.rangeSelector;if(s&&s.hasVisibleDropdown)i.initDropdownNav();else if(s){let i=s.buttons.length-1;e.highlightRangeSelectorButton(t>0?0:i)}},terminate:function(){i.removeDropdownKeydownHandler&&(i.removeDropdownKeydownHandler(),delete i.removeDropdownKeydownHandler)}})}getRangeSelectorInputNavigation(){let e=this.chart,t=this;return new s(e,{keyCodeMap:[],validate:function(){return!!(e.rangeSelector&&e.rangeSelector.inputGroup&&"hidden"!==e.rangeSelector.inputGroup.element.style.visibility&&!1!==e.options.rangeSelector.inputEnabled&&e.rangeSelector.minInput&&e.rangeSelector.maxInput)},init:function(e){t.onInputNavInit(e)},terminate:function(){t.onInputNavTerminate()}})}getKeyboardNavigation(){return[this.getRangeSelectorButtonNavigation(),this.getRangeSelectorInputNavigation()]}destroy(){this.removeDropdownKeydownHandler&&this.removeDropdownKeydownHandler(),this.removeInputKeydownHandler&&this.removeInputKeydownHandler(),this.announcer&&this.announcer.destroy()}}return function(e){function t(e){let t=this.rangeSelector&&this.rangeSelector.buttons||[],i=this.highlightedRangeSelectorItemIx,s=this.rangeSelector&&this.rangeSelector.selected;return void 0!==i&&t[i]&&i!==s&&t[i].setState(this.oldRangeSelectorItemState||0),this.highlightedRangeSelectorItemIx=e,!!t[e]&&(this.setFocusToElement(t[e].box,t[e].element),e!==s&&(this.oldRangeSelectorItemState=t[e].state,t[e].setState(1)),!0)}function i(){let e=this.chart.accessibility;if(e&&e.components.rangeSelector)return e.components.rangeSelector.onAfterBtnClick()}e.compose=function(e,s){let n=e.prototype;n.highlightRangeSelectorButton||(n.highlightRangeSelectorButton=t,a(s,"afterBtnClick",i))}}(h||(h={})),h}),i(t,"Accessibility/Components/SeriesComponent/ForcedMarkers.js",[t["Core/Globals.js"],t["Core/Utilities.js"]],function(e,t){var i;let{composed:s}=e,{addEvent:n,merge:r,pushUnique:o}=t;return function(e){function t(e){r(!0,e,{marker:{enabled:!0,states:{normal:{opacity:0}}}})}function i(e){return e.marker.states&&e.marker.states.normal&&e.marker.states.normal.opacity}function a(e){return!!(e._hasPointMarkers&&e.points&&e.points.length)}function l(){this.chart.styledMode&&(this.markerGroup&&this.markerGroup[this.a11yMarkersForced?"addClass":"removeClass"]("highcharts-a11y-markers-hidden"),a(this)&&this.points.forEach(e=>{e.graphic&&(e.graphic[e.hasForcedA11yMarker?"addClass":"removeClass"]("highcharts-a11y-marker-hidden"),e.graphic[!1===e.hasForcedA11yMarker?"addClass":"removeClass"]("highcharts-a11y-marker-visible"))}))}function h(e){this.resetA11yMarkerOptions=r(e.options.marker||{},this.userOptions.marker||{})}function c(){let e=this.options;(function(e){let t=e.chart.options.accessibility.enabled,i=!1!==(e.options.accessibility&&e.options.accessibility.enabled);return t&&i&&function(e){let t=e.chart.options.accessibility;return e.points.length-1;s.highlightedPoint===this&&t!==e&&!n&&e&&e.focus&&e.focus()})}onDrillupAll(){let e;let t=this.lastDrilledDownPoint,i=this.chart,s=t&&p(i,t.seriesName);t&&s&&c(t.x)&&c(t.y)&&(e=u(s,t.x,t.y)),e=e||f(i),i.container&&i.container.focus(),e&&e.highlight&&e.highlight(!1)}getKeyboardNavigationHandler(){let e=this,t=this.keyCodes,i=this.chart,s=i.inverted;return new r(i,{keyCodeMap:[[s?[t.up,t.down]:[t.left,t.right],function(t){return e.onKbdSideways(this,t)}],[s?[t.left,t.right]:[t.up,t.down],function(t){return e.onKbdVertical(this,t)}],[[t.enter,t.space],function(e,t){let s=i.highlightedPoint;if(s){let{plotLeft:e,plotTop:i}=this.chart,{plotX:n=0,plotY:r=0}=s;t={...t,chartX:e+n,chartY:i+r,point:s,target:s.graphic?.element||t.target},d(s.series,"click",t),s.firePointEvent("click",t)}return this.response.success}],[[t.home],function(){return v(i),this.response.success}],[[t.end],function(){return x(i),this.response.success}],[[t.pageDown,t.pageUp],function(e){return i.highlightAdjacentSeries(e===t.pageDown),this.response.success}]],init:function(){return e.onHandlerInit(this)},validate:function(){return!!f(i)},terminate:function(){return e.onHandlerTerminate()}})}onKbdSideways(e,t){let i=this.keyCodes,s=t===i.right||t===i.down;return this.attemptHighlightAdjacentPoint(e,s)}onHandlerInit(e){let t=this.chart;return t.options.accessibility.keyboardNavigation.seriesNavigation.rememberPointFocus&&t.highlightedPoint?t.highlightedPoint.highlight():v(t),e.response.success}onKbdVertical(e,t){let i=this.chart,s=this.keyCodes,n=t===s.down||t===s.right,r=i.options.accessibility.keyboardNavigation.seriesNavigation;if(r.mode&&"serialize"===r.mode)return this.attemptHighlightAdjacentPoint(e,n);let o=i.highlightedPoint&&i.highlightedPoint.series.keyboardMoveVertical?"highlightAdjacentPointVertical":"highlightAdjacentSeries";return i[o](n),e.response.success}onHandlerTerminate(){let e=this.chart,t=e.options.accessibility.keyboardNavigation;e.tooltip&&e.tooltip.hide(0);let i=e.highlightedPoint&&e.highlightedPoint.series;i&&i.onMouseOut&&i.onMouseOut(),e.highlightedPoint&&e.highlightedPoint.onMouseOut&&e.highlightedPoint.onMouseOut(),t.seriesNavigation.rememberPointFocus||delete e.highlightedPoint}attemptHighlightAdjacentPoint(e,t){let i=this.chart,s=i.options.accessibility.keyboardNavigation.wrapAround;return i.highlightAdjacentPoint(t)?e.response.success:s&&(t?v(i):x(i))?e.response.success:e.response[t?"next":"prev"]}onSeriesDestroy(e){let t=this.chart;t.highlightedPoint&&t.highlightedPoint.series===e&&(delete t.highlightedPoint,t.focusElement&&t.focusElement.removeFocusBorder())}destroy(){this.eventProvider.removeAddedEvents()}}return function(e){function t(e){let t,i;let s=this.series,n=this.highlightedPoint,r=n&&m(n)||0,o=n&&n.series.points||[],a=this.series&&this.series[this.series.length-1],l=a&&a.points&&a.points[a.points.length-1];if(!s[0]||!s[0].points)return!1;if(n){if(t=s[n.series.index+(e?1:-1)],(i=o[r+(e?1:-1)])||!t||(i=t.points[e?0:t.points.length-1]),!i)return!1}else i=e?s[0].points[0]:l;return y(i)?(b(t=i.series)?this.highlightedPoint=e?t.points[t.points.length-1]:t.points[0]:this.highlightedPoint=i,this.highlightAdjacentPoint(e)):i.highlight()}function i(e){let t=this.highlightedPoint,i=1/0,s;return!!(c(t.plotX)&&c(t.plotY))&&(this.series.forEach(n=>{b(n)||n.points.forEach(r=>{if(!c(r.plotY)||!c(r.plotX)||r===t)return;let o=r.plotY-t.plotY,a=Math.abs(r.plotX-t.plotX),l=Math.abs(o)*Math.abs(o)+a*a*4;n.yAxis&&n.yAxis.reversed&&(o*=-1),!(o<=0&&e||o>=0&&!e||l<5||y(r))&&l!(c(e.plotX)&&c(e.plotY));if(!h(e)){for(;l--;)!h(r=t.points[l])&&(a=(e.plotX-r.plotX)*(e.plotX-r.plotX)*4+(e.plotY-r.plotY)*(e.plotY-r.plotY)*1)=0;--e)if(!y(i[e]))return i[e].highlight()}return!1}e.compose=function(e,o,a){let h=e.prototype,c=o.prototype,d=a.prototype;h.highlightAdjacentPoint||(h.highlightAdjacentPoint=t,h.highlightAdjacentPointVertical=i,h.highlightAdjacentSeries=s,c.highlight=n,d.keyboardMoveVertical=!0,["column","gantt","pie"].forEach(e=>{l[e]&&(l[e].prototype.keyboardMoveVertical=!1)}),d.highlightNextValidPoint=r)}}(A||(A={})),A}),i(t,"Accessibility/Components/SeriesComponent/SeriesComponent.js",[t["Accessibility/AccessibilityComponent.js"],t["Accessibility/Utils/ChartUtilities.js"],t["Accessibility/Components/SeriesComponent/ForcedMarkers.js"],t["Accessibility/Components/SeriesComponent/NewDataAnnouncer.js"],t["Accessibility/Components/SeriesComponent/SeriesDescriber.js"],t["Accessibility/Components/SeriesComponent/SeriesKeyboardNavigation.js"]],function(e,t,i,s,n,r){let{hideSeriesFromAT:o}=t,{describeSeries:a}=n;return class extends e{static compose(e,t,n){s.compose(n),i.compose(n),r.compose(e,t,n)}init(){this.newDataAnnouncer=new s(this.chart),this.newDataAnnouncer.init(),this.keyboardNavigation=new r(this.chart,this.keyCodes),this.keyboardNavigation.init(),this.hideTooltipFromATWhenShown(),this.hideSeriesLabelsFromATWhenShown()}hideTooltipFromATWhenShown(){let e=this;this.chart.tooltip&&this.addEvent(this.chart.tooltip.constructor,"refresh",function(){this.chart===e.chart&&this.label&&this.label.element&&this.label.element.setAttribute("aria-hidden",!0)})}hideSeriesLabelsFromATWhenShown(){this.addEvent(this.chart,"afterDrawSeriesLabels",function(){this.series.forEach(function(e){e.labelBySeries&&e.labelBySeries.attr("aria-hidden",!0)})})}onChartRender(){this.chart.series.forEach(function(e){!1!==(e.options.accessibility&&e.options.accessibility.enabled)&&e.visible&&0!==e.getPointsCollection().length?a(e):o(e)})}getKeyboardNavigation(){return this.keyboardNavigation.getKeyboardNavigationHandler()}destroy(){this.newDataAnnouncer.destroy(),this.keyboardNavigation.destroy()}}}),i(t,"Accessibility/Components/ZoomComponent.js",[t["Accessibility/AccessibilityComponent.js"],t["Accessibility/Utils/ChartUtilities.js"],t["Accessibility/Utils/HTMLUtilities.js"],t["Accessibility/KeyboardNavigationHandler.js"],t["Core/Utilities.js"]],function(e,t,i,s,n){let{unhideChartElementFromAT:r}=t,{getFakeMouseEvent:o}=i,{attr:a,pick:l}=n;return class extends e{constructor(){super(...arguments),this.focusedMapNavButtonIx=-1}init(){let e=this,t=this.chart;this.proxyProvider.addGroup("zoom","div"),["afterShowResetZoom","afterApplyDrilldown","drillupall"].forEach(i=>{e.addEvent(t,i,function(){e.updateProxyOverlays()})})}onChartUpdate(){let e=this.chart,t=this;e.mapNavigation&&e.mapNavigation.navButtons.forEach((i,s)=>{r(e,i.element),t.setMapNavButtonAttrs(i.element,"accessibility.zoom.mapZoom"+(s?"Out":"In"))})}setMapNavButtonAttrs(e,t){let i=this.chart;a(e,{tabindex:-1,role:"button","aria-label":i.langFormat(t,{chart:i})})}onChartRender(){this.updateProxyOverlays()}updateProxyOverlays(){let e=this.chart;if(this.proxyProvider.clearGroup("zoom"),e.resetZoomButton&&this.createZoomProxyButton(e.resetZoomButton,"resetZoomProxyButton",e.langFormat("accessibility.zoom.resetZoomButton",{chart:e})),e.drillUpButton&&e.breadcrumbs&&e.breadcrumbs.list){let t=e.breadcrumbs.list[e.breadcrumbs.list.length-1];this.createZoomProxyButton(e.drillUpButton,"drillUpProxyButton",e.langFormat("accessibility.drillUpButton",{chart:e,buttonText:e.breadcrumbs.getButtonText(t)}))}}createZoomProxyButton(e,t,i){this[t]=this.proxyProvider.addProxyElement("zoom",{click:e},"button",{"aria-label":i,tabindex:-1})}getMapZoomNavigation(){let e=this.keyCodes,t=this.chart,i=this;return new s(t,{keyCodeMap:[[[e.up,e.down,e.left,e.right],function(e){return i.onMapKbdArrow(this,e)}],[[e.tab],function(e,t){return i.onMapKbdTab(this,t)}],[[e.space,e.enter],function(){return i.onMapKbdClick(this)}]],validate:function(){return!!(t.mapView&&t.mapNavigation&&t.mapNavigation.navButtons.length)},init:function(e){return i.onMapNavInit(e)}})}onMapKbdArrow(e,t){let i=this.chart,s=this.keyCodes,n=i.container,r=t===s.up||t===s.down,a=t===s.left||t===s.up?1:-1,l=(r?i.plotHeight:i.plotWidth)/10*a,h=10*Math.random(),c={x:n.offsetLeft+i.plotLeft+i.plotWidth/2+h,y:n.offsetTop+i.plotTop+i.plotHeight/2+h},d=r?{x:c.x,y:c.y+l}:{x:c.x+l,y:c.y};return[o("mousedown",c),o("mousemove",d),o("mouseup",d)].forEach(e=>n.dispatchEvent(e)),e.response.success}onMapKbdTab(e,t){let i=this.chart,s=e.response,n=t.shiftKey,r=n&&!this.focusedMapNavButtonIx||!n&&this.focusedMapNavButtonIx;if(i.mapNavigation.navButtons[this.focusedMapNavButtonIx].setState(0),r)return i.mapView&&i.mapView.zoomBy(),s[n?"prev":"next"];this.focusedMapNavButtonIx+=n?-1:1;let o=i.mapNavigation.navButtons[this.focusedMapNavButtonIx];return i.setFocusToElement(o.box,o.element),o.setState(2),s.success}onMapKbdClick(e){let t=this.chart.mapNavigation.navButtons[this.focusedMapNavButtonIx].element;return this.fakeClickEvent(t),e.response.success}onMapNavInit(e){let t=this.chart,i=t.mapNavigation.navButtons[0],s=t.mapNavigation.navButtons[1],n=e>0?i:s;t.setFocusToElement(n.box,n.element),n.setState(2),this.focusedMapNavButtonIx=e>0?0:1}simpleButtonNavigation(e,t,i){let n=this.keyCodes,r=this,o=this.chart;return new s(o,{keyCodeMap:[[[n.tab,n.up,n.down,n.left,n.right],function(e,t){let i=e===n.tab&&t.shiftKey||e===n.left||e===n.up;return this.response[i?"prev":"next"]}],[[n.space,n.enter],function(){return l(i(this,o),this.response.success)}]],validate:function(){return o[e]&&o[e].box&&r[t].innerElement},init:function(){o.setFocusToElement(o[e].box,r[t].innerElement)}})}getKeyboardNavigation(){return[this.simpleButtonNavigation("resetZoomButton","resetZoomProxyButton",function(e,t){t.zoomOut()}),this.simpleButtonNavigation("drillUpButton","drillUpProxyButton",function(e,t){return t.drillUp(),e.response.prev}),this.getMapZoomNavigation()]}}}),i(t,"Accessibility/HighContrastMode.js",[t["Core/Globals.js"]],function(e){let{doc:t,isMS:i,win:s}=e;return{isHighContrastModeActive:function(){let e=/(Edg)/.test(s.navigator.userAgent);if(s.matchMedia&&e)return s.matchMedia("(-ms-high-contrast: active)").matches;if(i&&s.getComputedStyle){let e=t.createElement("div");e.style.backgroundImage="url(data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==)",t.body.appendChild(e);let i=(e.currentStyle||s.getComputedStyle(e)).backgroundImage;return t.body.removeChild(e),"none"===i}return s.matchMedia&&s.matchMedia("(forced-colors: active)").matches},setHighContrastTheme:function(e){e.highContrastModeActive=!0;let t=e.options.accessibility.highContrastTheme;e.update(t,!1);let i=t.colors?.length>1;e.series.forEach(function(e){let s=t.plotOptions[e.type]||{},n=i&&void 0!==e.colorIndex?t.colors[e.colorIndex]:s.color||"window",r={color:s.color||"windowText",colors:i?t.colors:[s.color||"windowText"],borderColor:s.borderColor||"window",fillColor:n};e.update(r,!1),e.points&&e.points.forEach(function(e){e.options&&e.options.color&&e.update({color:s.color||"windowText",borderColor:s.borderColor||"window"},!1)})}),e.redraw()}}}),i(t,"Accessibility/HighContrastTheme.js",[],function(){return{chart:{backgroundColor:"window"},title:{style:{color:"windowText"}},subtitle:{style:{color:"windowText"}},colorAxis:{minColor:"windowText",maxColor:"windowText",stops:[],dataClasses:[]},colors:["windowText"],xAxis:{gridLineColor:"windowText",labels:{style:{color:"windowText"}},lineColor:"windowText",minorGridLineColor:"windowText",tickColor:"windowText",title:{style:{color:"windowText"}}},yAxis:{gridLineColor:"windowText",labels:{style:{color:"windowText"}},lineColor:"windowText",minorGridLineColor:"windowText",tickColor:"windowText",title:{style:{color:"windowText"}}},tooltip:{backgroundColor:"window",borderColor:"windowText",style:{color:"windowText"}},plotOptions:{series:{lineColor:"windowText",fillColor:"window",borderColor:"windowText",edgeColor:"windowText",borderWidth:1,dataLabels:{connectorColor:"windowText",color:"windowText",style:{color:"windowText",textOutline:"none"}},marker:{lineColor:"windowText",fillColor:"windowText"}},pie:{color:"window",colors:["window"],borderColor:"windowText",borderWidth:1},boxplot:{fillColor:"window"},candlestick:{lineColor:"windowText",fillColor:"window"},errorbar:{fillColor:"window"}},legend:{backgroundColor:"window",itemStyle:{color:"windowText"},itemHoverStyle:{color:"windowText"},itemHiddenStyle:{color:"#555"},title:{style:{color:"windowText"}}},credits:{style:{color:"windowText"}},drilldown:{activeAxisLabelStyle:{color:"windowText"},activeDataLabelStyle:{color:"windowText"}},navigation:{buttonOptions:{symbolStroke:"windowText",theme:{fill:"window"}}},rangeSelector:{buttonTheme:{fill:"window",stroke:"windowText",style:{color:"windowText"},states:{hover:{fill:"window",stroke:"windowText",style:{color:"windowText"}},select:{fill:"#444",stroke:"windowText",style:{color:"windowText"}}}},inputBoxBorderColor:"windowText",inputStyle:{backgroundColor:"window",color:"windowText"},labelStyle:{color:"windowText"}},navigator:{handles:{backgroundColor:"window",borderColor:"windowText"},outlineColor:"windowText",maskFill:"transparent",series:{color:"windowText",lineColor:"windowText"},xAxis:{gridLineColor:"windowText"}},scrollbar:{barBackgroundColor:"#444",barBorderColor:"windowText",buttonArrowColor:"windowText",buttonBackgroundColor:"window",buttonBorderColor:"windowText",rifleColor:"windowText",trackBackgroundColor:"window",trackBorderColor:"windowText"}}}),i(t,"Accessibility/Options/A11yDefaults.js",[],function(){return{accessibility:{enabled:!0,screenReaderSection:{beforeChartFormat:"<{headingTagName}>{chartTitle}
    {typeDescription}
    {chartSubtitle}
    {chartLongdesc}
    {playAsSoundButton}
    {viewTableButton}
    {xAxisDescription}
    {yAxisDescription}
    {annotationsTitle}{annotationsList}
    ",afterChartFormat:"{endOfChartMarker}",axisRangeDateFormat:"%Y-%m-%d %H:%M:%S"},series:{descriptionFormat:"{seriesDescription}{authorDescription}{axisDescription}",describeSingleSeries:!1,pointDescriptionEnabledThreshold:200},point:{valueDescriptionFormat:"{xDescription}{separator}{value}.",describeNull:!0},landmarkVerbosity:"all",linkedDescription:'*[data-highcharts-chart="{index}"] + .highcharts-description',highContrastMode:"auto",keyboardNavigation:{enabled:!0,focusBorder:{enabled:!0,hideBrowserFocusOutline:!0,style:{color:"#334eff",lineWidth:2,borderRadius:3},margin:2},order:["series","zoom","rangeSelector","navigator","legend","chartMenu"],wrapAround:!0,seriesNavigation:{skipNullPoints:!0,pointNavigationEnabledThreshold:!1,rememberPointFocus:!1}},announceNewData:{enabled:!1,minAnnounceInterval:5e3,interruptUser:!1}},legend:{accessibility:{enabled:!0,keyboardNavigation:{enabled:!0}}},exporting:{accessibility:{enabled:!0}},navigator:{accessibility:{enabled:!0}}}}),i(t,"Accessibility/Options/LangDefaults.js",[],function(){return{accessibility:{defaultChartTitle:"Chart",chartContainerLabel:"{title}. Highcharts interactive chart.",svgContainerLabel:"Interactive chart",drillUpButton:"{buttonText}",credits:"Chart credits: {creditsStr}",thousandsSep:",",svgContainerTitle:"",graphicContainerLabel:"",screenReaderSection:{beforeRegionLabel:"",afterRegionLabel:"",annotations:{heading:"Chart annotations summary",descriptionSinglePoint:"{annotationText}. Related to {annotationPoint}",descriptionMultiplePoints:"{annotationText}. Related to {annotationPoint}{#each additionalAnnotationPoints}, also related to {this}{/each}",descriptionNoPoints:"{annotationText}"},endOfChartMarker:"End of interactive chart."},sonification:{playAsSoundButtonText:"Play as sound, {chartTitle}",playAsSoundClickAnnouncement:"Play"},legend:{legendLabelNoTitle:"Toggle series visibility, {chartTitle}",legendLabel:"Chart legend: {legendTitle}",legendItem:"Show {itemName}"},zoom:{mapZoomIn:"Zoom chart",mapZoomOut:"Zoom out chart",resetZoomButton:"Reset zoom"},rangeSelector:{dropdownLabel:"{rangeTitle}",minInputLabel:"Select start date.",maxInputLabel:"Select end date.",clickButtonAnnouncement:"Viewing {axisRangeDescription}"},navigator:{handleLabel:"{#eq handleIx 0}Start, percent{else}End, percent{/eq}",groupLabel:"Axis zoom",changeAnnouncement:"{axisRangeDescription}"},table:{viewAsDataTableButtonText:"View as data table, {chartTitle}",tableSummary:"Table representation of chart."},announceNewData:{newDataAnnounce:"Updated data for chart {chartTitle}",newSeriesAnnounceSingle:"New data series: {seriesDesc}",newPointAnnounceSingle:"New data point: {pointDesc}",newSeriesAnnounceMultiple:"New data series in chart {chartTitle}: {seriesDesc}",newPointAnnounceMultiple:"New data point in chart {chartTitle}: {pointDesc}"},seriesTypeDescriptions:{boxplot:"Box plot charts are typically used to display groups of statistical data. Each data point in the chart can have up to 5 values: minimum, lower quartile, median, upper quartile, and maximum.",arearange:"Arearange charts are line charts displaying a range between a lower and higher value for each point.",areasplinerange:"These charts are line charts displaying a range between a lower and higher value for each point.",bubble:"Bubble charts are scatter charts where each data point also has a size value.",columnrange:"Columnrange charts are column charts displaying a range between a lower and higher value for each point.",errorbar:"Errorbar series are used to display the variability of the data.",funnel:"Funnel charts are used to display reduction of data in stages.",pyramid:"Pyramid charts consist of a single pyramid with item heights corresponding to each point value.",waterfall:"A waterfall chart is a column chart where each column contributes towards a total end value."},chartTypes:{emptyChart:"Empty chart",mapTypeDescription:"Map of {mapTitle} with {numSeries} data series.",unknownMap:"Map of unspecified region with {numSeries} data series.",combinationChart:"Combination chart with {numSeries} data series.",defaultSingle:"Chart with {numPoints} data {#eq numPoints 1}point{else}points{/eq}.",defaultMultiple:"Chart with {numSeries} data series.",splineSingle:"Line chart with {numPoints} data {#eq numPoints 1}point{else}points{/eq}.",splineMultiple:"Line chart with {numSeries} lines.",lineSingle:"Line chart with {numPoints} data {#eq numPoints 1}point{else}points{/eq}.",lineMultiple:"Line chart with {numSeries} lines.",columnSingle:"Bar chart with {numPoints} {#eq numPoints 1}bar{else}bars{/eq}.",columnMultiple:"Bar chart with {numSeries} data series.",barSingle:"Bar chart with {numPoints} {#eq numPoints 1}bar{else}bars{/eq}.",barMultiple:"Bar chart with {numSeries} data series.",pieSingle:"Pie chart with {numPoints} {#eq numPoints 1}slice{else}slices{/eq}.",pieMultiple:"Pie chart with {numSeries} pies.",scatterSingle:"Scatter chart with {numPoints} {#eq numPoints 1}point{else}points{/eq}.",scatterMultiple:"Scatter chart with {numSeries} data series.",boxplotSingle:"Boxplot with {numPoints} {#eq numPoints 1}box{else}boxes{/eq}.",boxplotMultiple:"Boxplot with {numSeries} data series.",bubbleSingle:"Bubble chart with {numPoints} {#eq numPoints 1}bubbles{else}bubble{/eq}.",bubbleMultiple:"Bubble chart with {numSeries} data series."},axis:{xAxisDescriptionSingular:"The chart has 1 X axis displaying {names[0]}. {ranges[0]}",xAxisDescriptionPlural:"The chart has {numAxes} X axes displaying {#each names}{#unless @first},{/unless}{#if @last} and{/if} {this}{/each}.",yAxisDescriptionSingular:"The chart has 1 Y axis displaying {names[0]}. {ranges[0]}",yAxisDescriptionPlural:"The chart has {numAxes} Y axes displaying {#each names}{#unless @first},{/unless}{#if @last} and{/if} {this}{/each}.",timeRangeDays:"Data range: {range} days.",timeRangeHours:"Data range: {range} hours.",timeRangeMinutes:"Data range: {range} minutes.",timeRangeSeconds:"Data range: {range} seconds.",rangeFromTo:"Data ranges from {rangeFrom} to {rangeTo}.",rangeCategories:"Data range: {numCategories} categories."},exporting:{chartMenuLabel:"Chart menu",menuButtonLabel:"View chart menu, {chartTitle}"},series:{summary:{default:"{series.name}, series {seriesNumber} of {chart.series.length} with {series.points.length} data {#eq series.points.length 1}point{else}points{/eq}.",defaultCombination:"{series.name}, series {seriesNumber} of {chart.series.length} with {series.points.length} data {#eq series.points.length 1}point{else}points{/eq}.",line:"{series.name}, line {seriesNumber} of {chart.series.length} with {series.points.length} data {#eq series.points.length 1}point{else}points{/eq}.",lineCombination:"{series.name}, series {seriesNumber} of {chart.series.length}. Line with {series.points.length} data {#eq series.points.length 1}point{else}points{/eq}.",spline:"{series.name}, line {seriesNumber} of {chart.series.length} with {series.points.length} data {#eq series.points.length 1}point{else}points{/eq}.",splineCombination:"{series.name}, series {seriesNumber} of {chart.series.length}. Line with {series.points.length} data {#eq series.points.length 1}point{else}points{/eq}.",column:"{series.name}, bar series {seriesNumber} of {chart.series.length} with {series.points.length} {#eq series.points.length 1}bar{else}bars{/eq}.",columnCombination:"{series.name}, series {seriesNumber} of {chart.series.length}. Bar series with {series.points.length} {#eq series.points.length 1}bar{else}bars{/eq}.",bar:"{series.name}, bar series {seriesNumber} of {chart.series.length} with {series.points.length} {#eq series.points.length 1}bar{else}bars{/eq}.",barCombination:"{series.name}, series {seriesNumber} of {chart.series.length}. Bar series with {series.points.length} {#eq series.points.length 1}bar{else}bars{/eq}.",pie:"{series.name}, pie {seriesNumber} of {chart.series.length} with {series.points.length} {#eq series.points.length 1}slice{else}slices{/eq}.",pieCombination:"{series.name}, series {seriesNumber} of {chart.series.length}. Pie with {series.points.length} {#eq series.points.length 1}slice{else}slices{/eq}.",scatter:"{series.name}, scatter plot {seriesNumber} of {chart.series.length} with {series.points.length} {#eq series.points.length 1}point{else}points{/eq}.",scatterCombination:"{series.name}, series {seriesNumber} of {chart.series.length}, scatter plot with {series.points.length} {#eq series.points.length 1}point{else}points{/eq}.",boxplot:"{series.name}, boxplot {seriesNumber} of {chart.series.length} with {series.points.length} {#eq series.points.length 1}box{else}boxes{/eq}.",boxplotCombination:"{series.name}, series {seriesNumber} of {chart.series.length}. Boxplot with {series.points.length} {#eq series.points.length 1}box{else}boxes{/eq}.",bubble:"{series.name}, bubble series {seriesNumber} of {chart.series.length} with {series.points.length} {#eq series.points.length 1}bubble{else}bubbles{/eq}.",bubbleCombination:"{series.name}, series {seriesNumber} of {chart.series.length}. Bubble series with {series.points.length} {#eq series.points.length 1}bubble{else}bubbles{/eq}.",map:"{series.name}, map {seriesNumber} of {chart.series.length} with {series.points.length} {#eq series.points.length 1}area{else}areas{/eq}.",mapCombination:"{series.name}, series {seriesNumber} of {chart.series.length}. Map with {series.points.length} {#eq series.points.length 1}area{else}areas{/eq}.",mapline:"{series.name}, line {seriesNumber} of {chart.series.length} with {series.points.length} data {#eq series.points.length 1}point{else}points{/eq}.",maplineCombination:"{series.name}, series {seriesNumber} of {chart.series.length}. Line with {series.points.length} data {#eq series.points.length 1}point{else}points{/eq}.",mapbubble:"{series.name}, bubble series {seriesNumber} of {chart.series.length} with {series.points.length} {#eq series.points.length 1}bubble{else}bubbles{/eq}.",mapbubbleCombination:"{series.name}, series {seriesNumber} of {chart.series.length}. Bubble series with {series.points.length} {#eq series.points.length 1}bubble{else}bubbles{/eq}."},description:"{description}",xAxisDescription:"X axis, {name}",yAxisDescription:"Y axis, {name}",nullPointValue:"No value",pointAnnotationsDescription:"{#each annotations}Annotation: {this}{/each}"}}}}),i(t,"Accessibility/Options/DeprecatedOptions.js",[t["Core/Utilities.js"]],function(e){let{error:t,pick:i}=e;function s(e,t,s){let n=e,r,o=0;for(;o"series"!==e)):Object.keys(this.components):[]}update(){let e=this.components,t=this.chart,i=t.options.accessibility;M(t,"beforeA11yUpdate"),t.types=this.getChartTypes();let s=i.keyboardNavigation.order;this.proxyProvider.updateGroupOrder(s),this.getComponentOrder().forEach(function(i){e[i].onChartUpdate(),M(t,"afterA11yComponentUpdate",{name:i,component:e[i]})}),this.keyboardNavigation.update(s),!t.highContrastModeActive&&!1!==i.highContrastMode&&(y.isHighContrastModeActive()||!0===i.highContrastMode)&&y.setHighContrastTheme(t),M(t,"afterA11yUpdate",{accessibility:this})}destroy(){let e=this.chart||{},t=this.components;Object.keys(t).forEach(function(e){t[e].destroy(),t[e].destroyBase()}),this.proxyProvider&&this.proxyProvider.destroy(),e.announcerContainer&&k(e.announcerContainer),this.keyboardNavigation&&this.keyboardNavigation.destroy(),e.renderTo&&e.renderTo.setAttribute("aria-hidden",!0),e.focusElement&&e.focusElement.removeFocusBorder()}getChartTypes(){let e={};return this.chart.series.forEach(function(t){e[t.type]=1}),Object.keys(e)}}return function(e){function t(){this.accessibility&&this.accessibility.destroy()}function i(){this.a11yDirty&&this.renderTo&&(delete this.a11yDirty,this.updateA11yEnabled());let e=this.accessibility;e&&!e.zombie&&(e.proxyProvider.updateProxyElementPositions(),e.getComponentOrder().forEach(function(t){e.components[t].onChartRender()}))}function s(e){let t=e.options.accessibility;t&&(t.customComponents&&(this.options.accessibility.customComponents=t.customComponents,delete t.customComponents),S(!0,this.options.accessibility,t),this.accessibility&&this.accessibility.destroy&&(this.accessibility.destroy(),delete this.accessibility)),this.a11yDirty=!0}function r(){let t=this.accessibility,i=this.options.accessibility,s=this.renderer.boxWrapper.element,n=this.title;if(i&&i.enabled)t&&!t.zombie?t.update():(this.accessibility=t=new e(this),t&&!t.zombie&&t.update(),"img"===s.getAttribute("role")&&s.removeAttribute("role"));else if(t)t.destroy&&t.destroy(),delete this.accessibility;else{this.renderTo.setAttribute("role","img"),this.renderTo.setAttribute("aria-hidden",!1),this.renderTo.setAttribute("aria-label",(n&&n.element.textContent||"").replace(/{E(e,t,function(){this.a11yDirty=!0})}),["afterApplyDrilldown","drillupall"].forEach(t=>{E(e,t,function(){let e=this.accessibility;e&&!e.zombie&&e.update()})}),E(p,"update",a),["update","updatedData","remove"].forEach(e=>{E(b,e,function(){this.chart.accessibility&&(this.chart.a11yDirty=!0)})}))}}(P||(P={})),S(!0,C,x,{accessibility:{highContrastTheme:f},lang:v}),P}),i(t,"masters/modules/accessibility.src.js",[t["Core/Globals.js"],t["Accessibility/Accessibility.js"],t["Accessibility/AccessibilityComponent.js"],t["Accessibility/Utils/ChartUtilities.js"],t["Accessibility/Utils/HTMLUtilities.js"],t["Accessibility/KeyboardNavigationHandler.js"],t["Accessibility/Components/SeriesComponent/SeriesDescriber.js"]],function(e,t,i,s,n,r,o){return e.i18nFormat=t.i18nFormat,e.A11yChartUtilities=s,e.A11yHTMLUtilities=n,e.AccessibilityComponent=i,e.KeyboardNavigationHandler=r,e.SeriesAccessibilityDescriber=o,t.compose(e.Chart,e.Legend,e.Point,e.Series,e.SVGElement,e.RangeSelector),e})}); \ No newline at end of file diff --git a/factgenie/static/js/lib/modules/data.js b/factgenie/static/js/lib/modules/data.js new file mode 100644 index 00000000..68c50e84 --- /dev/null +++ b/factgenie/static/js/lib/modules/data.js @@ -0,0 +1,9 @@ +!/** + * Highcharts JS v11.4.8 (2024-08-29) + * + * Data module + * + * (c) 2012-2024 Torstein Honsi + * + * License: www.highcharts.com/license + */function(e){"object"==typeof module&&module.exports?(e.default=e,module.exports=e):"function"==typeof define&&define.amd?define("highcharts/modules/data",["highcharts"],function(t){return e(t),e.Highcharts=t,e}):e("undefined"!=typeof Highcharts?Highcharts:void 0)}(function(e){"use strict";var t=e?e._modules:{};function s(t,s,i,a){t.hasOwnProperty(s)||(t[s]=a.apply(null,i),"function"==typeof CustomEvent&&e.win.dispatchEvent(new CustomEvent("HighchartsModuleLoaded",{detail:{path:s,module:t[s]}})))}s(t,"Core/HttpUtilities.js",[t["Core/Globals.js"],t["Core/Utilities.js"]],function(e,t){let{win:s}=e,{discardElement:i,objectEach:a}=t,n={ajax:function(e){let t={json:"application/json",xml:"application/xml",text:"text/plain",octet:"application/octet-stream"},s=new XMLHttpRequest;function i(t,s){e.error&&e.error(t,s)}if(!e.url)return!1;s.open((e.type||"get").toUpperCase(),e.url,!0),e.headers&&e.headers["Content-Type"]||s.setRequestHeader("Content-Type",t[e.dataType||"json"]||t.text),a(e.headers,function(e,t){s.setRequestHeader(t,e)}),e.responseType&&(s.responseType=e.responseType),s.onreadystatechange=function(){let t;if(4===s.readyState){if(200===s.status){if("blob"!==e.responseType&&(t=s.responseText,"json"===e.dataType))try{t=JSON.parse(t)}catch(e){if(e instanceof Error)return i(s,e)}return e.success&&e.success(t,s)}i(s,s.responseText)}},e.data&&"string"!=typeof e.data&&(e.data=JSON.stringify(e.data)),s.send(e.data)},getJSON:function(e,t){n.ajax({url:e,success:t,dataType:"json",headers:{"Content-Type":"text/plain"}})},post:function(e,t,n){let r=new s.FormData;a(t,function(e,t){r.append(t,e)}),r.append("b64","true");let{filename:o,type:l}=t;return s.fetch(e,{method:"POST",body:r,...n}).then(e=>{e.ok&&e.text().then(e=>{let t=document.createElement("a");t.href=`data:${l};base64,${e}`,t.download=o,t.click(),i(t)})})}};return n}),s(t,"Extensions/Data.js",[t["Core/Axis/Axis.js"],t["Core/Chart/Chart.js"],t["Core/Defaults.js"],t["Core/Globals.js"],t["Core/HttpUtilities.js"],t["Core/Series/Point.js"],t["Core/Series/SeriesRegistry.js"],t["Core/Utilities.js"]],function(e,t,s,i,a,n,r,o){let{getOptions:l}=s,{doc:u}=i,{ajax:h}=a,{seriesTypes:d}=r,{addEvent:p,defined:m,extend:c,fireEvent:f,isNumber:g,merge:x,objectEach:C,pick:y,splat:v}=o;function T(e){return!!(e&&(e.rowsURL||e.csvURL||e.columnsURL))}class N{static data(e,t={},s){return new N(e,t,s)}static rowsToColumns(e){let t,s,i,a,n;if(e)for(t=0,n=[],s=e.length;tt.getFullYear()-2e3?s+=1900:s+=2e3,Date.UTC(s,+e[2]-1,+e[1])},alternative:"mm/dd/YY"},"mm/dd/YY":{regex:/^(\d{1,2})[\-\/\.](\d{1,2})[\-\/\.](\d{2})$/,parser:function(e){return e?Date.UTC(+e[3]+2e3,+e[1]-1,+e[2]):NaN}}},this.chart=s,this.chartOptions=t,this.options=e,this.rawColumns=[],this.init(e,t,s)}init(e,t,s){let i=e.decimalPoint,a;t&&(this.chartOptions=t),s&&(this.chart=s),"."!==i&&","!==i&&(i=void 0),this.options=e,this.columns=e.columns||this.rowsToColumns(e.rows)||[],this.firstRowAsNames=y(e.firstRowAsNames,this.firstRowAsNames,!0),this.decimalRegex=i&&RegExp("^(-?[0-9]+)"+i+"([0-9]+)$"),void 0!==this.liveDataTimeout&&clearTimeout(this.liveDataTimeout),this.rawColumns=[],this.columns.length&&(this.dataFound(),a=!T(e)),a||(a=this.fetchLiveData()),a||(a=!!this.parseCSV().length),a||(a=!!this.parseTable().length),a||(a=this.parseGoogleSpreadsheet()),!a&&e.afterComplete&&e.afterComplete(this)}getColumnDistribution(){let e=this.chartOptions,t=this.options,s=[],i=function(e="line"){return(d[e].prototype.pointArrayMap||[0]).length},a=function(e="line"){return d[e].prototype.pointArrayMap},n=e?.chart?.type,r=[],o=[],l=t?.seriesMapping||e?.series?.map(function(){return{x:0}})||[],u=0;(e?.series||[]).forEach(e=>{r.push(i(e.type||n))}),l.forEach(e=>{s.push(e.x||0)}),0===s.length&&s.push(0),l.forEach(t=>{let s=new R,l=r[u]||i(n),h=(e?.series??[])[u]??{},d=a(h.type||n),p=d??["y"];(m(t.x)||h.isCartesian||!d)&&s.addColumnReader(t.x,"x"),C(t,function(e,t){"x"!==t&&s.addColumnReader(e,t)});for(let e=0;e=p.length)&&(h=p.length-1),i.itemDelimiter)d=i.itemDelimiter;else{let e,s,a;e=0,s=0,a=!1,p.some(function(t,i){let a=!1,n,r,l,u="";if(i>13)return!0;for(let i=0;io[","]?a=";":(o[","],o[";"],a=","),i.decimalPoint||(e>s?i.decimalPoint=".":i.decimalPoint=",",t.decimalRegex=RegExp("^(-?[0-9]+)"+i.decimalPoint+"([0-9]+)$")),d=a}let e=0;for(m=u;m<=h;m++)"#"===p[m][0]?e++:function(e,t,o,l){let u=0,h="",p="",m="",c="",f=0,g=0;function x(t){h=e[t],p=e[t-1],m=e[t+1]}function C(e){r.lengthf||f>n){++f,c="";return}i.columnTypes||(!isNaN(parseFloat(c))&&isFinite(c)?(c=parseFloat(c),C("number")):isNaN(Date.parse(c))?C("string"):(c=c.replace(/\//g,"-"),C("date"))),s.lengthe.length)&&(s=e.length);h31?o[p]<100?l[p]="YY":l[p]="YYYY":o[p]>12&&o[p]<=31?(l[p]="dd",d=!0):l[p].length||(l[p]="mm")));if(d){for(p=0;p12&&"YY"!==l[p]&&"YYYY"!==l[p]&&(l[p]="YY"):r[p]>12&&"mm"===l[p]&&(l[p]="dd");return(3===l.length&&"dd"===l[1]&&"dd"===l[2]&&(l[2]="YY"),u=l.join("/"),(i.dateFormats||t.dateFormats)[u])?u:(f(t,"deduceDateFailed"),a)}return a}(s[0])),this.dataFound()}return s}parseTable(){let e=this.options,t=this.columns||[],s=e.startRow||0,i=e.endRow||Number.MAX_VALUE,a=e.startColumn||0,n=e.endColumn||Number.MAX_VALUE;if(e.table){let r=e.table;"string"==typeof r&&(r=u.getElementById(r)),[].forEach.call(r.getElementsByTagName("tr"),(e,r)=>{r>=s&&r<=i&&[].forEach.call(e.children,(e,i)=>{let o=t[i-a],l=1;if(("TD"===e.tagName||"TH"===e.tagName)&&i>=a&&i<=n)for(t[i-a]||(t[i-a]=[]),t[i-a][r-s]=e.innerHTML;r-s>=l&&void 0===o[r-s-l];)o[r-s-l]=null,l++})}),this.dataFound()}return t}fetchLiveData(){let e=this,t=this.chart,s=this.options,i=s.enablePolling,a=x(s),n=0,r=1e3*(s.dataRefreshRate||2);return!!T(s)&&(r<1e3&&(r=1e3),delete s.csvURL,delete s.rowsURL,delete s.columnsURL,function o(l){function u(a,u,d){if(!a||!/^(http|\/|\.\/|\.\.\/)/.test(a))return a&&s.error&&s.error("Invalid URL"),!1;function p(){i&&t.liveDataURL===a&&(e.liveDataTimeout=setTimeout(o,r))}return l&&(clearTimeout(e.liveDataTimeout),t.liveDataURL=a),h({url:a,dataType:d||"json",success:function(e){t?.series&&u(e),p()},error:function(e,t){return++n<3&&p(),s.error?.(t,e)}}),!0}u(a.csvURL,function(e){t.update({data:{csv:e}})},"text")||u(a.rowsURL,function(e){t.update({data:{rows:e}})})||u(a.columnsURL,function(e){t.update({data:{columns:e}})})}(!0),T(s))}parseGoogleSpreadsheet(){let e=this,t=this.options,s=t.googleSpreadsheetKey,i=this.chart,a=Math.max(1e3*(t.dataRefreshRate||2),4e3),n=()=>{if(t.googleSpreadsheetRange)return t.googleSpreadsheetRange;let e="ABCDEFGHIJKLMNOPQRSTUVWXYZ",s=(e.charAt(t.startColumn||0)||"A")+((t.startRow||0)+1),i=e.charAt(y(t.endColumn,-1))||"ZZ";return m(t.endRow)&&(i+=t.endRow+1),`${s}:${i}`};return s&&(delete t.googleSpreadsheetKey,function i(r){h({url:["https://sheets.googleapis.com/v4/spreadsheets",s,"values",n(),"?alt=json&majorDimension=COLUMNS&valueRenderOption=UNFORMATTED_VALUE&dateTimeRenderOption=FORMATTED_STRING&key="+t.googleAPIKey].join("/"),dataType:"json",success:function(s){r(s),t.enablePolling&&(e.liveDataTimeout=setTimeout(function(){i(r)},a))},error:function(e,s){return t.error?.(s,e)}})}(function(t){let s=t.values;if(!s||0===s.length)return!1;let a=s.reduce((e,t)=>Math.max(e,t.length),0);s.forEach(e=>{for(let t=0;t31536e6&&"float"!==l?e.isDatetime=!0:e.isNumeric=!0,void 0!==e[d+1]&&(T=c>e[d+1])):(f?.length&&(C=this.parseDate(p)),n&&g(C)&&"float"!==l?(r[d]=p,e[d]=C,e.isDatetime=!0,void 0!==e[d+1]&&((y=C>e[d+1])!==T&&void 0!==T&&(this.alternativeFormat?(this.dateFormat=this.alternativeFormat,d=e.length,this.alternativeFormat=this.dateFormats[this.dateFormat].alternative):e.unsorted=!0),T=y)):(e[d]=""===f?null:f,0!==d&&(e.isDatetime||e.isNumeric)&&(e.mixed=!0)));if(n&&e.mixed&&(i[t]=s[t]),n&&T&&this.options.sort){for(t=0;t0;){for((d=new R).addColumnReader(0,"x"),-1!==(f=p.indexOf(0))&&p.splice(f,1),r=0;r0&&s[0].readers.length>0&&void 0!==(c=e?.[s[0].readers[0].columnIndex??-1])&&(c.isDatetime?i="datetime":c.isNumeric||(i="category")),"category"===i)for(u=0;u{void 0===t.columnIndex&&(t.columnIndex=e.shift())}),this.readers.forEach(e=>{void 0===e.columnIndex&&(t=!1)}),t}read(e,t){let s=this.pointIsArray,i=s?[]:{};if(this.readers.forEach(a=>{let r=e[a.columnIndex][t];s?i.push(r):a.configName.indexOf(".")>0?n.prototype.setNestedProperty(i,r,a.configName):i[a.configName]=r}),void 0===this.name&&this.readers.length>=2){let t=[];this.readers.forEach(function(e){("x"===e.configName||"name"===e.configName||"y"===e.configName)&&void 0!==e.columnIndex&&t.push(e.columnIndex)}),t.length>=2&&(t.shift(),t.sort(function(e,t){return e-t})),this.name=e[y(t.shift(),0)].name}return i}addColumnReader(e,t){this.readers.push({columnIndex:e,configName:t}),"x"===t||"y"===t||void 0===t||(this.pointIsArray=!1)}getReferencedColumnIndexes(){let e,t;let s=[];for(e=0;e{n.call(this.chart,e,t)})}}e.Additions=t}(e||(e={})),e}),n(t,"Extensions/Exporting/ExportingDefaults.js",[t["Core/Globals.js"]],function(e){let{isTouchDevice:t}=e;return{exporting:{allowTableSorting:!0,type:"image/png",url:"https://export.highcharts.com/",pdfFont:{normal:void 0,bold:void 0,bolditalic:void 0,italic:void 0},printMaxWidth:780,scale:2,buttons:{contextButton:{className:"highcharts-contextbutton",menuClassName:"highcharts-contextmenu",symbol:"menu",titleKey:"contextButtonTitle",menuItems:["viewFullscreen","printChart","separator","downloadPNG","downloadJPEG","downloadPDF","downloadSVG"]}},menuItemDefinitions:{viewFullscreen:{textKey:"viewFullscreen",onclick:function(){this.fullscreen&&this.fullscreen.toggle()}},printChart:{textKey:"printChart",onclick:function(){this.print()}},separator:{separator:!0},downloadPNG:{textKey:"downloadPNG",onclick:function(){this.exportChart()}},downloadJPEG:{textKey:"downloadJPEG",onclick:function(){this.exportChart({type:"image/jpeg"})}},downloadPDF:{textKey:"downloadPDF",onclick:function(){this.exportChart({type:"application/pdf"})}},downloadSVG:{textKey:"downloadSVG",onclick:function(){this.exportChart({type:"image/svg+xml"})}}}},lang:{viewFullscreen:"View in full screen",exitFullscreen:"Exit from full screen",printChart:"Print chart",downloadPNG:"Download PNG image",downloadJPEG:"Download JPEG image",downloadPDF:"Download PDF document",downloadSVG:"Download SVG vector image",contextButtonTitle:"Chart context menu"},navigation:{buttonOptions:{symbolSize:14,symbolX:14.5,symbolY:13.5,align:"right",buttonSpacing:3,height:28,verticalAlign:"top",width:28,symbolFill:"#666666",symbolStroke:"#666666",symbolStrokeWidth:3,theme:{fill:"#ffffff",padding:5,stroke:"none","stroke-linecap":"round"}},menuStyle:{border:"none",borderRadius:"3px",background:"#ffffff",padding:"0.5em"},menuItemStyle:{background:"none",borderRadius:"3px",color:"#333333",padding:"0.5em",fontSize:t?"0.9em":"0.8em",transition:"background 250ms, color 250ms"},menuItemHoverStyle:{background:"#f2f2f2"}}}}),n(t,"Extensions/Exporting/ExportingSymbols.js",[],function(){var e;return function(e){let t=[];function n(e,t,n,i){return[["M",e,t+2.5],["L",e+n,t+2.5],["M",e,t+i/2+.5],["L",e+n,t+i/2+.5],["M",e,t+i-1.5],["L",e+n,t+i-1.5]]}function i(e,t,n,i){let o=i/3-2;return[].concat(this.circle(n-o,t,o,o),this.circle(n-o,t+o+4,o,o),this.circle(n-o,t+2*(o+4),o,o))}e.compose=function(e){if(-1===t.indexOf(e)){t.push(e);let o=e.prototype.symbols;o.menu=n,o.menuball=i.bind(o)}}}(e||(e={})),e}),n(t,"Extensions/Exporting/Fullscreen.js",[t["Core/Renderer/HTML/AST.js"],t["Core/Globals.js"],t["Core/Utilities.js"]],function(e,t,n){let{composed:i}=t,{addEvent:o,fireEvent:r,pushUnique:s}=n;function l(){this.fullscreen=new a(this)}class a{static compose(e){s(i,"Fullscreen")&&o(e,"beforeRender",l)}constructor(e){this.chart=e,this.isOpen=!1;let t=e.renderTo;!this.browserProps&&("function"==typeof t.requestFullscreen?this.browserProps={fullscreenChange:"fullscreenchange",requestFullscreen:"requestFullscreen",exitFullscreen:"exitFullscreen"}:t.mozRequestFullScreen?this.browserProps={fullscreenChange:"mozfullscreenchange",requestFullscreen:"mozRequestFullScreen",exitFullscreen:"mozCancelFullScreen"}:t.webkitRequestFullScreen?this.browserProps={fullscreenChange:"webkitfullscreenchange",requestFullscreen:"webkitRequestFullScreen",exitFullscreen:"webkitExitFullscreen"}:t.msRequestFullscreen&&(this.browserProps={fullscreenChange:"MSFullscreenChange",requestFullscreen:"msRequestFullscreen",exitFullscreen:"msExitFullscreen"}))}close(){let e=this,t=e.chart,n=t.options.chart;r(t,"fullscreenClose",null,function(){e.isOpen&&e.browserProps&&t.container.ownerDocument instanceof Document&&t.container.ownerDocument[e.browserProps.exitFullscreen](),e.unbindFullscreenEvent&&(e.unbindFullscreenEvent=e.unbindFullscreenEvent()),t.setSize(e.origWidth,e.origHeight,!1),e.origWidth=void 0,e.origHeight=void 0,n.width=e.origWidthOption,n.height=e.origHeightOption,e.origWidthOption=void 0,e.origHeightOption=void 0,e.isOpen=!1,e.setButtonText()})}open(){let e=this,t=e.chart,n=t.options.chart;r(t,"fullscreenOpen",null,function(){if(n&&(e.origWidthOption=n.width,e.origHeightOption=n.height),e.origWidth=t.chartWidth,e.origHeight=t.chartHeight,e.browserProps){let n=o(t.container.ownerDocument,e.browserProps.fullscreenChange,function(){e.isOpen?(e.isOpen=!1,e.close()):(t.setSize(null,null,!1),e.isOpen=!0,e.setButtonText())}),i=o(t,"destroy",n);e.unbindFullscreenEvent=()=>{n(),i()};let r=t.renderTo[e.browserProps.requestFullscreen]();r&&r.catch(function(){alert("Full screen is not supported inside a frame.")})}})}setButtonText(){let t=this.chart,n=t.exportDivElements,i=t.options.exporting,o=i&&i.buttons&&i.buttons.contextButton.menuItems,r=t.options.lang;if(i&&i.menuItemDefinitions&&r&&r.exitFullscreen&&r.viewFullscreen&&o&&n){let t=n[o.indexOf("viewFullscreen")];t&&e.setElementHTML(t,this.isOpen?r.exitFullscreen:i.menuItemDefinitions.viewFullscreen.text||r.viewFullscreen)}}toggle(){this.isOpen?this.close():this.open()}}return a}),n(t,"Core/HttpUtilities.js",[t["Core/Globals.js"],t["Core/Utilities.js"]],function(e,t){let{win:n}=e,{discardElement:i,objectEach:o}=t,r={ajax:function(e){let t={json:"application/json",xml:"application/xml",text:"text/plain",octet:"application/octet-stream"},n=new XMLHttpRequest;function i(t,n){e.error&&e.error(t,n)}if(!e.url)return!1;n.open((e.type||"get").toUpperCase(),e.url,!0),e.headers&&e.headers["Content-Type"]||n.setRequestHeader("Content-Type",t[e.dataType||"json"]||t.text),o(e.headers,function(e,t){n.setRequestHeader(t,e)}),e.responseType&&(n.responseType=e.responseType),n.onreadystatechange=function(){let t;if(4===n.readyState){if(200===n.status){if("blob"!==e.responseType&&(t=n.responseText,"json"===e.dataType))try{t=JSON.parse(t)}catch(e){if(e instanceof Error)return i(n,e)}return e.success&&e.success(t,n)}i(n,n.responseText)}},e.data&&"string"!=typeof e.data&&(e.data=JSON.stringify(e.data)),n.send(e.data)},getJSON:function(e,t){r.ajax({url:e,success:t,dataType:"json",headers:{"Content-Type":"text/plain"}})},post:function(e,t,r){let s=new n.FormData;o(t,function(e,t){s.append(t,e)}),s.append("b64","true");let{filename:l,type:a}=t;return n.fetch(e,{method:"POST",body:s,...r}).then(e=>{e.ok&&e.text().then(e=>{let t=document.createElement("a");t.href=`data:${a};base64,${e}`,t.download=l,t.click(),i(t)})})}};return r}),n(t,"Extensions/Exporting/Exporting.js",[t["Core/Renderer/HTML/AST.js"],t["Core/Chart/Chart.js"],t["Core/Chart/ChartNavigationComposition.js"],t["Core/Defaults.js"],t["Extensions/Exporting/ExportingDefaults.js"],t["Extensions/Exporting/ExportingSymbols.js"],t["Extensions/Exporting/Fullscreen.js"],t["Core/Globals.js"],t["Core/HttpUtilities.js"],t["Core/Utilities.js"]],function(e,t,n,i,o,r,s,l,a,c){var u;let{defaultOptions:p}=i,{doc:h,SVG_NS:d,win:g}=l,{addEvent:f,css:m,createElement:x,discardElement:y,extend:b,find:v,fireEvent:w,isObject:E,merge:C,objectEach:S,pick:F,removeEvent:T,uniqueKey:O}=c;return function(t){let i;let u=[/-/,/^(clipPath|cssText|d|height|width)$/,/^font$/,/[lL]ogical(Width|Height)$/,/^parentRule$/,/^(cssRules|ownerRules)$/,/perspective/,/TapHighlightColor/,/^transition/,/^length$/,/^\d+$/],M=["fill","stroke","strokeLinecap","strokeLinejoin","strokeWidth","textAnchor","x","y"];t.inlineAllowlist=[];let P=["clipPath","defs","desc"];function k(e){let t,n;let i=this,o=i.renderer,r=C(i.options.navigation.buttonOptions,e),s=r.onclick,l=r.menuItems,a=r.symbolSize||12;if(i.btnCount||(i.btnCount=0),i.exportDivElements||(i.exportDivElements=[],i.exportSVGElements=[]),!1===r.enabled||!r.theme)return;let c=i.styledMode?{}:r.theme;s?n=function(e){e&&e.stopPropagation(),s.call(i,e)}:l&&(n=function(e){e&&e.stopPropagation(),i.contextMenu(u.menuClassName,l,u.translateX||0,u.translateY||0,u.width||0,u.height||0,u),u.setState(2)}),r.text&&r.symbol?c.paddingLeft=F(c.paddingLeft,30):r.text||b(c,{width:r.width,height:r.height,padding:0});let u=o.button(r.text,0,0,n,c,void 0,void 0,void 0,void 0,r.useHTML).addClass(e.className).attr({title:F(i.options.lang[r._titleKey||r.titleKey],"")});u.menuClassName=e.menuClassName||"highcharts-menu-"+i.btnCount++,r.symbol&&(t=o.symbol(r.symbol,Math.round((r.symbolX||0)-a/2),Math.round((r.symbolY||0)-a/2),a,a,{width:a,height:a}).addClass("highcharts-button-symbol").attr({zIndex:1}).add(u),i.styledMode||t.attr({stroke:r.symbolStroke,fill:r.symbolFill,"stroke-width":r.symbolStrokeWidth||1})),u.add(i.exportingGroup).align(b(r,{width:u.width,x:F(r.x,i.buttonOffset)}),!0,"spacingBox"),i.buttonOffset+=((u.width||0)+r.buttonSpacing)*("right"===r.align?-1:1),i.exportSVGElements.push(u,t)}function N(){if(!this.printReverseInfo)return;let{childNodes:e,origDisplay:t,resetParams:n}=this.printReverseInfo;this.moveContainers(this.renderTo),[].forEach.call(e,function(e,n){1===e.nodeType&&(e.style.display=t[n]||"")}),this.isPrinting=!1,n&&this.setSize.apply(this,n),delete this.printReverseInfo,i=void 0,w(this,"afterPrint")}function H(){let e=h.body,t=this.options.exporting.printMaxWidth,n={childNodes:e.childNodes,origDisplay:[],resetParams:void 0};this.isPrinting=!0,this.pointer?.reset(void 0,0),w(this,"beforePrint"),t&&this.chartWidth>t&&(n.resetParams=[this.options.chart.width,void 0,!1],this.setSize(t,void 0,!1)),[].forEach.call(n.childNodes,function(e,t){1===e.nodeType&&(n.origDisplay[t]=e.style.display,e.style.display="none")}),this.moveContainers(e),this.printReverseInfo=n}function j(e){e.renderExporting(),f(e,"redraw",e.renderExporting),f(e,"destroy",e.destroyExport)}function G(t,n,i,o,r,s,l){let a=this,u=a.options.navigation,p=a.chartWidth,d=a.chartHeight,y="cache-"+t,v=Math.max(r,s),C,S=a[y];S||(a.exportContextMenu=a[y]=S=x("div",{className:t},{position:"absolute",zIndex:1e3,padding:v+"px",pointerEvents:"auto",...a.renderer.style},a.scrollablePlotArea?.fixedDiv||a.container),C=x("ul",{className:"highcharts-menu"},a.styledMode?{}:{listStyle:"none",margin:0,padding:0},S),a.styledMode||m(C,b({MozBoxShadow:"3px 3px 10px #888",WebkitBoxShadow:"3px 3px 10px #888",boxShadow:"3px 3px 10px #888"},u.menuStyle)),S.hideMenu=function(){m(S,{display:"none"}),l&&l.setState(0),a.openMenu=!1,m(a.renderTo,{overflow:"hidden"}),m(a.container,{overflow:"hidden"}),c.clearTimeout(S.hideTimer),w(a,"exportMenuHidden")},a.exportEvents.push(f(S,"mouseleave",function(){S.hideTimer=g.setTimeout(S.hideMenu,500)}),f(S,"mouseenter",function(){c.clearTimeout(S.hideTimer)}),f(h,"mouseup",function(e){a.pointer?.inClass(e.target,t)||S.hideMenu()}),f(S,"click",function(){a.openMenu&&S.hideMenu()})),n.forEach(function(t){if("string"==typeof t&&(t=a.options.exporting.menuItemDefinitions[t]),E(t,!0)){let n;t.separator?n=x("hr",void 0,void 0,C):("viewData"===t.textKey&&a.isDataTableVisible&&(t.textKey="hideData"),n=x("li",{className:"highcharts-menu-item",onclick:function(e){e&&e.stopPropagation(),S.hideMenu(),"string"!=typeof t&&t.onclick&&t.onclick.apply(a,arguments)}},void 0,C),e.setElementHTML(n,t.text||a.options.lang[t.textKey]),a.styledMode||(n.onmouseover=function(){m(this,u.menuItemHoverStyle)},n.onmouseout=function(){m(this,u.menuItemStyle)},m(n,b({cursor:"pointer"},u.menuItemStyle||{})))),a.exportDivElements.push(n)}}),a.exportDivElements.push(C,S),a.exportMenuWidth=S.offsetWidth,a.exportMenuHeight=S.offsetHeight);let F={display:"block"};i+(a.exportMenuWidth||0)>p?F.right=p-i-r-v+"px":F.left=i-v+"px",o+s+(a.exportMenuHeight||0)>d&&l.alignOptions?.verticalAlign!=="top"?F.bottom=d-o-v+"px":F.top=o+s-v+"px",m(S,F),m(a.renderTo,{overflow:""}),m(a.container,{overflow:""}),a.openMenu=!0,w(a,"exportMenuShown")}function D(e){let t;let n=e?e.target:this,i=n.exportSVGElements,o=n.exportDivElements,r=n.exportEvents;i&&(i.forEach((e,o)=>{e&&(e.onclick=e.ontouchstart=null,n[t="cache-"+e.menuClassName]&&delete n[t],i[o]=e.destroy())}),i.length=0),n.exportingGroup&&(n.exportingGroup.destroy(),delete n.exportingGroup),o&&(o.forEach(function(e,t){e&&(c.clearTimeout(e.hideTimer),T(e,"mouseleave"),o[t]=e.onmouseout=e.onmouseover=e.ontouchstart=e.onclick=null,y(e))}),o.length=0),r&&(r.forEach(function(e){e()}),r.length=0)}function I(e,t){let n=this.getSVGForExport(e,t);e=C(this.options.exporting,e),a.post(e.url,{filename:e.filename?e.filename.replace(/\//g,"-"):this.getFilename(),type:e.type,width:e.width,scale:e.scale,svg:n},e.fetchOptions)}function W(){return this.styledMode&&this.inlineStyles(),this.container.innerHTML}function R(){let e=this.userOptions.title&&this.userOptions.title.text,t=this.options.exporting.filename;return t?t.replace(/\//g,"-"):("string"==typeof e&&(t=e.toLowerCase().replace(/<\/?[^>]+(>|$)/g,"").replace(/[\s_]+/g,"-").replace(/[^a-z\d\-]/g,"").replace(/^[\-]+/g,"").replace(/[\-]+/g,"-").substr(0,24).replace(/[\-]+$/g,"")),(!t||t.length<5)&&(t="chart"),t)}function L(e){let t,n,i=C(this.options,e);i.plotOptions=C(this.userOptions.plotOptions,e&&e.plotOptions),i.time=C(this.userOptions.time,e&&e.time);let o=x("div",null,{position:"absolute",top:"-9999em",width:this.chartWidth+"px",height:this.chartHeight+"px"},h.body),r=this.renderTo.style.width,s=this.renderTo.style.height,l=i.exporting.sourceWidth||i.chart.width||/px$/.test(r)&&parseInt(r,10)||(i.isGantt?800:600),a=i.exporting.sourceHeight||i.chart.height||/px$/.test(s)&&parseInt(s,10)||400;b(i.chart,{animation:!1,renderTo:o,forExport:!0,renderer:"SVGRenderer",width:l,height:a}),i.exporting.enabled=!1,delete i.data,i.series=[],this.series.forEach(function(e){(n=C(e.userOptions,{animation:!1,enableMouseTracking:!1,showCheckbox:!1,visible:e.visible})).isInternal||i.series.push(n)});let c={};this.axes.forEach(function(e){e.userOptions.internalKey||(e.userOptions.internalKey=O()),e.options.isInternal||(c[e.coll]||(c[e.coll]=!0,i[e.coll]=[]),i[e.coll].push(C(e.userOptions,{visible:e.visible,type:e.type,uniqueNames:e.uniqueNames})))}),i.colorAxis=this.userOptions.colorAxis;let u=new this.constructor(i,this.callback);return e&&["xAxis","yAxis","series"].forEach(function(t){let n={};e[t]&&(n[t]=e[t],u.update(n))}),this.axes.forEach(function(e){let t=v(u.axes,function(t){return t.options.internalKey===e.userOptions.internalKey}),n=e.getExtremes(),i=n.userMin,o=n.userMax;t&&(void 0!==i&&i!==t.min||void 0!==o&&o!==t.max)&&t.setExtremes(i,o,!0,!1)}),t=u.getChartHTML(),w(this,"getSVG",{chartCopy:u}),t=this.sanitizeSVG(t,i),i=null,u.destroy(),y(o),t}function q(e,t){let n=this.options.exporting;return this.getSVG(C({chart:{borderRadius:0}},n.chartOptions,t,{exporting:{sourceWidth:e&&e.sourceWidth||n.sourceWidth,sourceHeight:e&&e.sourceHeight||n.sourceHeight}}))}function $(){let e;let n=t.inlineAllowlist,i={},o=h.createElement("iframe");m(o,{width:"1px",height:"1px",visibility:"hidden"}),h.body.appendChild(o);let r=o.contentWindow&&o.contentWindow.document;r&&r.body.appendChild(r.createElementNS(d,"svg")),function t(o){let s,a,c,p,h,d;let f={};if(r&&1===o.nodeType&&-1===P.indexOf(o.nodeName)){if(s=g.getComputedStyle(o,null),a="svg"===o.nodeName?{}:g.getComputedStyle(o.parentNode,null),!i[o.nodeName]){e=r.getElementsByTagName("svg")[0],c=r.createElementNS(o.namespaceURI,o.nodeName),e.appendChild(c);let t=g.getComputedStyle(c,null),n={};for(let e in t)e.length<1e3&&"string"==typeof t[e]&&!/^\d+$/.test(e)&&(n[e]=t[e]);i[o.nodeName]=n,"text"===o.nodeName&&delete i.text.fill,e.removeChild(c)}for(let e in s)(l.isFirefox||l.isMS||l.isSafari||Object.hasOwnProperty.call(s,e))&&function(e,t){if(p=h=!1,n.length){for(d=n.length;d--&&!h;)h=n[d].test(t);p=!h}for("transform"===t&&"none"===e&&(p=!0),d=u.length;d--&&!p;){if(t.length>1e3)throw Error("Input too long");p=u[d].test(t)||"function"==typeof e}!p&&(a[t]!==e||"svg"===o.nodeName)&&i[o.nodeName][t]!==e&&(M&&-1===M.indexOf(t)?f[t]=e:e&&o.setAttribute(t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()}),e))}(s[e],e);if(m(o,f),"svg"===o.nodeName&&o.setAttribute("stroke-width","1px"),"text"===o.nodeName)return;[].forEach.call(o.children||o.childNodes,t)}}(this.container.querySelector("svg")),e.parentNode.removeChild(e),o.parentNode.removeChild(o)}function z(e){let{scrollablePlotArea:t}=this;(t?[t.fixedDiv,t.scrollingContainer]:[this.container]).forEach(function(t){e.appendChild(t)})}function V(){let e=this,t=(t,n,i)=>{e.isDirtyExporting=!0,C(!0,e.options[t],n),F(i,!0)&&e.redraw()};e.exporting={update:function(e,n){t("exporting",e,n)}},n.compose(e).navigation.addUpdate((e,n)=>{t("navigation",e,n)})}function A(){let e=this;e.isPrinting||(i=e,l.isSafari||e.beforePrint(),setTimeout(()=>{g.focus(),g.print(),l.isSafari||setTimeout(()=>{e.afterPrint()},1e3)},1))}function K(){let e=this,t=e.options.exporting,n=t.buttons,i=e.isDirtyExporting||!e.exportSVGElements;e.buttonOffset=0,e.isDirtyExporting&&e.destroyExport(),i&&!1!==t.enabled&&(e.exportEvents=[],e.exportingGroup=e.exportingGroup||e.renderer.g("exporting-group").attr({zIndex:3}).add(),S(n,function(t){e.addButton(t)}),e.isDirtyExporting=!1)}function U(e,t){let n=e.indexOf("")+6,i=e.substr(n);return e=e.substr(0,n),t&&t.exporting&&t.exporting.allowHTML&&i&&(i=''+i.replace(/(<(?:img|br).*?(?=\>))>/g,"$1 />")+"",e=e.replace("",i+"")),e=e.replace(/zIndex="[^"]+"/g,"").replace(/symbolName="[^"]+"/g,"").replace(/jQuery\d+="[^"]+"/g,"").replace(/url\(("|")(.*?)("|")\;?\)/g,"url($2)").replace(/url\([^#]+#/g,"url(#").replace(/t&&t.indexOf&&0===t.indexOf("highcharts-pattern-")).length){for(let t of this.series)if(t.visible)for(let e of t.points){let t=e.options&&e.options.color;t&&t.pattern&&(t.pattern._width="defer",t.pattern._height="defer")}this.redraw(!1)}}function m(){let t={},e=this.renderer,i=(e.defIds||[]).filter(t=>t.indexOf&&0===t.indexOf("highcharts-pattern-"));if(i.length)for(let r of([].forEach.call(this.renderTo.querySelectorAll('[color^="url("], [fill^="url("], [stroke^="url("]'),i=>{let r=i.getAttribute("fill")||i.getAttribute("color")||i.getAttribute("stroke");r&&(t[r.replace(e.url,"").replace("url(#","").replace(")","")]=!0)}),i))!t[r]&&(s(e.defIds,r),e.patternElements[r]&&(e.patternElements[r].destroy(),delete e.patternElements[r]))}function y(){let t=this.options.color;t&&t.pattern&&("string"==typeof t.pattern.path&&(t.pattern.path={d:t.pattern.path}),this.color=this.options.color=l(this.series.options.color,t))}function x(t){let e=t.args[0],i=t.args[1],r=t.args[2],a=this.chartIndex||0,n=e.pattern,o="#333333";if(void 0!==e.patternIndex&&f&&(n=f[e.patternIndex]),!n)return!0;if(n.image||"string"==typeof n.path||n.path&&n.path.d){let t=r.parentNode&&r.parentNode.getAttribute("class");t=t&&t.indexOf("highcharts-legend")>-1,("defer"===n._width||"defer"===n._height)&&M.call({graphic:{element:r}},n),(t||!n.id)&&((n=l({},n)).id="highcharts-pattern-"+a+"-"+u(n)+u(n,!0)),this.addPattern(n,!this.forExport&&p(n.animation,this.globalAnimation,{duration:100})),o=`url(${this.url}#${n.id+(this.forExport?"-export":"")})`}else o=n.color||o;return r.setAttribute(i,o),e.toString=function(){return o},!1}function w(){let t=this.chart.isResizing;if(this.isDirtyData||t||!this.chart.hasRendered)for(let e of this.points){let i=e.options&&e.options.color;i&&i.pattern&&(t&&!(e.shapeArgs&&e.shapeArgs.width&&e.shapeArgs.height)?(i.pattern._width="defer",i.pattern._height="defer"):e.calculatePatternDimensions(i.pattern))}}function M(t){if(t.width&&t.height)return;let e=this.graphic&&(this.graphic.getBBox&&this.graphic.getBBox(!0)||this.graphic.element&&this.graphic.element.getBBox())||{},i=this.shapeArgs;if(i&&(e.width=i.width||e.width,e.height=i.height||e.height,e.x=i.x||e.x,e.y=i.y||e.y),t.image){if(!e.width||!e.height){t._width="defer",t._height="defer";let e=this.series.chart.mapView&&this.series.chart.mapView.getSVGTransform().scaleY;o(e)&&e<0&&(t._inverted=!0);return}t.aspectRatio&&(e.aspectRatio=e.width/e.height,t.aspectRatio>e.aspectRatio?e.aspectWidth=e.height*t.aspectRatio:e.aspectHeight=e.width/t.aspectRatio),t._width=t.width||Math.ceil(e.aspectWidth||e.width),t._height=t.height||Math.ceil(e.aspectHeight||e.height)}t.width||(t._x=t.x||0,t._x+=e.x-Math.round(e.aspectWidth?Math.abs(e.aspectWidth-e.width)/2:0)),t.height||(t._y=t.y||0,t._y+=e.y-Math.round(e.aspectHeight?Math.abs(e.aspectHeight-e.height)/2:0))}function L(t,e){let a=p(e,!0),n=r(a),o=t.color||"#333333",s=t.height||("number"==typeof t._height?t._height:0)||32,h=t.width||("number"==typeof t._width?t._width:0)||32,l,d=t.id,f;if(!d&&(this.idCounter=this.idCounter||0,d="highcharts-pattern-"+this.idCounter+"-"+(this.chartIndex||0),++this.idCounter),this.forExport&&(d+="-export"),this.defIds=this.defIds||[],this.defIds.indexOf(d)>-1)return;this.defIds.push(d);let u={id:d,patternUnits:"userSpaceOnUse",patternContentUnits:t.patternContentUnits||"userSpaceOnUse",width:h,height:s,x:t._x||t.x||0,y:t._y||t.y||0};t._inverted&&(u.patternTransform="scale(1, -1)",t.patternTransform&&(t.patternTransform+=" scale(1, -1)")),t.patternTransform&&(u.patternTransform=t.patternTransform);let g=this.createElement("pattern").attr(u).add(this.defs);if(g.id=d,t.path){if(f=i.isObject(t.path)?t.path:{d:t.path},t.backgroundColor){let e;e=t.backgroundColor,this.rect(0,0,h,s).attr({fill:e}).add(g)}l={d:f.d},this.styledMode||(l.stroke=f.stroke||o,l["stroke-width"]=p(f.strokeWidth,2),l.fill=f.fill||"none"),f.transform&&(l.transform=f.transform),this.createElement("path").attr(l).add(g),g.color=o}else t.image&&(a?this.image(t.image,0,0,h,s,function(){this.animate({opacity:p(t.opacity,1)},n),c(this.element,"load")}).attr({opacity:0}).add(g):this.image(t.image,0,0,h,s).add(g));return t.image&&a||void 0===t.opacity||[].forEach.call(g.element.childNodes,e=>{e.setAttribute("opacity",t.opacity)}),this.patternElements=this.patternElements||{},this.patternElements[d]=g,g}function b(t){let e=this.options.color;e&&e.pattern&&!e.pattern.color?(delete this.options.color,t.apply(this,[].slice.call(arguments,1)),e.pattern.color=this.color,this.color=this.options.color=e):t.apply(this,[].slice.call(arguments,1))}function _(){if(!this.chart?.mapView)return;let t=this.chart.renderer,e=t.patternElements;t.defIds?.length&&e&&this.points.filter(function(t){return!!t.graphic&&(t.graphic.element.hasAttribute("fill")||t.graphic.element.hasAttribute("color")||t.graphic.element.hasAttribute("stroke"))&&!t.options.color?.pattern?.image&&!!t.group?.scaleX&&!!t.group?.scaleY}).map(function(e){return{id:(e.graphic?.element.getAttribute("fill")||e.graphic?.element.getAttribute("color")||e.graphic?.element.getAttribute("stroke")||"").replace(t.url,"").replace("url(#","").replace(")",""),x:e.group?.scaleX||1,y:e.group?.scaleY||1}}).filter(function(t,e,i){return""!==t.id&&-1!==t.id.indexOf("highcharts-pattern-")&&!i.some(function(i,r){return i.id===t.id&&rt.x-e.x),m="plot"+m[0].toUpperCase()+m.substr(1);x--&&i[f];)if(S=p[x],(b=i[f]).y=S.y,S.x<=b.x&&void 0!==S[m]){if(b.x<=v&&(b.plotY=S[m],S.xMath.pow(1-r,3)*t[s]+3*(1-r)*(1-r)*r*o[s]+3*(1-r)*r*r*i[s]+r*r*r*e[s],s=0,n=1,a;for(let t=0;t<100;t++){let t=(s+n)/2,e=r(t,0);if(null===e)break;if(.25>Math.abs(e-b.plotX)){a=t;break}e{let r;e.plotX+=y,(void 0===e.plotY||u)&&(e.plotX>=0&&e.plotX<=d.len?u?(e.plotY=d.translate(e.x,0,1,0,1),e.plotX=l(e.y)?c.translate(e.y,0,0,0,1):0):e.plotY=(d.opposite?0:t.yAxis.len)+d.offset:e.shapeArgs={}),(g=i[o-1])&&g.plotX===e.plotX&&(void 0===g.stackIndex&&(g.stackIndex=0),r=g.stackIndex+1),e.stackIndex=r}),this.onSeries=s}t.compose=function(t){if(p(s,"OnSeries")){let i=t.prototype;i.getPlotBox=e,i.translate=o}return t},t.getPlotBox=e,t.translate=o}(r||(r={})),r}),o(e,"Series/Windbarb/WindbarbPoint.js",[e["Series/Column/ColumnSeries.js"],e["Core/Utilities.js"]],function(t,e){let{isNumber:o}=e;class i extends t.prototype.pointClass{isValid(){return o(this.value)&&this.value>=0}}return i}),o(e,"Series/Windbarb/WindbarbSeriesDefaults.js",[],function(){return{dataGrouping:{enabled:!0,approximation:"windbarb",groupPixelWidth:30},lineWidth:2,onSeries:null,states:{hover:{lineWidthPlus:0}},tooltip:{pointFormat:' {series.name}: {point.value} ({point.beaufort})
    '},vectorLength:20,colorKey:"value",yOffset:-20,xOffset:0}}),o(e,"Series/Windbarb/WindbarbSeries.js",[e["Core/Animation/AnimationUtilities.js"],e["Extensions/DataGrouping/ApproximationRegistry.js"],e["Core/Globals.js"],e["Series/OnSeriesComposition.js"],e["Core/Series/SeriesRegistry.js"],e["Core/Utilities.js"],e["Series/Windbarb/WindbarbPoint.js"],e["Series/Windbarb/WindbarbSeriesDefaults.js"]],function(t,e,o,i,r,s,n,a){let{animObject:l}=t,{column:p}=r.seriesTypes,{extend:h,merge:u,pick:d}=s;class c extends p{init(t,e){super.init(t,e)}pointAttribs(t,e){let o=this.options,i=t.color||this.color,r=this.options.lineWidth;return e&&(i=o.states[e].color||i,r=(o.states[e].lineWidth||r)+(o.states[e].lineWidthPlus||0)),{stroke:i,"stroke-width":r}}windArrow(t){let e=t.beaufortLevel,o=this.options.vectorLength/20,i=1.943844*t.value,r,s=-10;if(t.isNull)return[];if(0===e)return this.chart.renderer.symbols.circle(-10*o,-10*o,20*o,20*o);let n=[["M",0,7*o],["L",-1.5*o,7*o],["L",0,10*o],["L",1.5*o,7*o],["L",0,7*o],["L",0,-10*o]];if((r=(i-i%50)/50)>0)for(;r--;)n.push(-10===s?["L",0,s*o]:["M",0,s*o],["L",5*o,s*o+2],["L",0,s*o+4]),i-=50,s+=7;if((r=(i-i%10)/10)>0)for(;r--;)n.push(-10===s?["L",0,s*o]:["M",0,s*o],["L",7*o,s*o]),i-=10,s+=3;if((r=(i-i%5)/5)>0)for(;r--;)n.push(-10===s?["L",0,s*o]:["M",0,s*o],["L",4*o,s*o]),i-=5,s+=3;return n}drawPoints(){let t=this.chart,e=this.yAxis,o=t.inverted,i=this.options.vectorLength/2;for(let r of this.points){let s=r.plotX,n=r.plotY;!1===this.options.clip||t.isInsidePlot(s,0)?(r.graphic||(r.graphic=this.chart.renderer.path().add(this.markerGroup).addClass("highcharts-point highcharts-color-"+d(r.colorIndex,r.series.colorIndex))),r.graphic.attr({d:this.windArrow(r),translateX:s+this.options.xOffset,translateY:n+this.options.yOffset,rotation:r.direction}),this.chart.styledMode||r.graphic.attr(this.pointAttribs(r))):r.graphic&&(r.graphic=r.graphic.destroy()),r.tooltipPos=[s+this.options.xOffset+(o&&!this.onSeries?i:0),n+this.options.yOffset-(o?0:i+e.pos-t.plotTop)]}}animate(t){t?this.markerGroup.attr({opacity:.01}):this.markerGroup.animate({opacity:1},l(this.options.animation))}markerAttribs(){return{}}getExtremes(){return{}}shouldShowTooltip(t,e,o={}){return o.ignoreX=this.chart.inverted,o.ignoreY=!o.ignoreX,super.shouldShowTooltip(t,e,o)}}return c.defaultOptions=u(p.defaultOptions,a),i.compose(c),h(c.prototype,{beaufortFloor:[0,.3,1.6,3.4,5.5,8,10.8,13.9,17.2,20.8,24.5,28.5,32.7],beaufortName:["Calm","Light air","Light breeze","Gentle breeze","Moderate breeze","Fresh breeze","Strong breeze","Near gale","Gale","Strong gale","Storm","Violent storm","Hurricane"],invertible:!1,parallelArrays:["x","value","direction"],pointArrayMap:["value","direction"],pointClass:n,trackerGroups:["markerGroup"],translate:function(){let t=this.beaufortFloor,e=this.beaufortName;for(let o of(i.translate.call(this),this.points)){let i=0;for(;io.value);i++);o.beaufortLevel=i-1,o.beaufort=e[i-1]}}}),r.registerSeriesType("windbarb",c),e.windbarb||(e.windbarb=(t,e)=>{let i=0,r=0;for(let s=0,n=t.length;st+e,0)/t.length,Math.atan2(r,i)/o.deg2rad]}),c}),o(e,"masters/modules/windbarb.src.js",[e["Core/Globals.js"]],function(t){return t})}); \ No newline at end of file diff --git a/factgenie/static/js/popper.min.js b/factgenie/static/js/lib/popper.min.js similarity index 100% rename from factgenie/static/js/popper.min.js rename to factgenie/static/js/lib/popper.min.js diff --git a/factgenie/static/js/split.min.js b/factgenie/static/js/lib/split.min.js similarity index 100% rename from factgenie/static/js/split.min.js rename to factgenie/static/js/lib/split.min.js diff --git a/factgenie/static/js/tableExport.min.js b/factgenie/static/js/lib/tableExport.min.js similarity index 100% rename from factgenie/static/js/tableExport.min.js rename to factgenie/static/js/lib/tableExport.min.js diff --git a/factgenie/static/js/lib/underscore-min.js b/factgenie/static/js/lib/underscore-min.js new file mode 100644 index 00000000..03277d15 --- /dev/null +++ b/factgenie/static/js/lib/underscore-min.js @@ -0,0 +1,6 @@ +!function(n,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define("underscore",r):(n="undefined"!=typeof globalThis?globalThis:n||self,function(){var t=n._,e=n._=r();e.noConflict=function(){return n._=t,e}}())}(this,(function(){ +// Underscore.js 1.13.7 +// https://underscorejs.org +// (c) 2009-2024 Jeremy Ashkenas, Julian Gonggrijp, and DocumentCloud and Investigative Reporters & Editors +// Underscore may be freely distributed under the MIT license. +var n="1.13.7",r="object"==typeof self&&self.self===self&&self||"object"==typeof global&&global.global===global&&global||Function("return this")()||{},t=Array.prototype,e=Object.prototype,u="undefined"!=typeof Symbol?Symbol.prototype:null,i=t.push,o=t.slice,a=e.toString,f=e.hasOwnProperty,c="undefined"!=typeof ArrayBuffer,l="undefined"!=typeof DataView,s=Array.isArray,p=Object.keys,v=Object.create,h=c&&ArrayBuffer.isView,y=isNaN,d=isFinite,g=!{toString:null}.propertyIsEnumerable("toString"),b=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"],m=Math.pow(2,53)-1;function j(n,r){return r=null==r?n.length-1:+r,function(){for(var t=Math.max(arguments.length-r,0),e=Array(t),u=0;u=0&&t<=m}}function J(n){return function(r){return null==r?void 0:r[n]}}var G=J("byteLength"),H=K(G),Q=/\[object ((I|Ui)nt(8|16|32)|Float(32|64)|Uint8Clamped|Big(I|Ui)nt64)Array\]/;var X=c?function(n){return h?h(n)&&!q(n):H(n)&&Q.test(a.call(n))}:C(!1),Y=J("length");function Z(n,r){r=function(n){for(var r={},t=n.length,e=0;e":">",'"':""","'":"'","`":"`"},$n=zn(Ln),Cn=zn(wn(Ln)),Kn=tn.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g},Jn=/(.)^/,Gn={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},Hn=/\\|'|\r|\n|\u2028|\u2029/g;function Qn(n){return"\\"+Gn[n]}var Xn=/^\s*(\w|\$)+\s*$/;var Yn=0;function Zn(n,r,t,e,u){if(!(e instanceof r))return n.apply(t,u);var i=Mn(n.prototype),o=n.apply(i,u);return w(o)?o:i}var nr=j((function(n,r){var t=nr.placeholder,e=function(){for(var u=0,i=r.length,o=Array(i),a=0;a1)er(a,r-1,t,e),u=e.length;else for(var f=0,c=a.length;f0&&(t=r.apply(this,arguments)),n<=1&&(r=null),t}}var cr=nr(fr,2);function lr(n,r,t){r=Pn(r,t);for(var e,u=nn(n),i=0,o=u.length;i0?0:u-1;i>=0&&i0?a=i>=0?i:Math.max(i+f,a):f=i>=0?Math.min(i+1,f):i+f+1;else if(t&&i&&f)return e[i=t(e,u)]===u?i:-1;if(u!=u)return(i=r(o.call(e,a,f),$))>=0?i+a:-1;for(i=n>0?a:f-1;i>=0&&i0?0:o-1;for(u||(e=r[i?i[a]:a],a+=n);a>=0&&a=3;return r(n,Rn(t,u,4),e,i)}}var _r=wr(1),Ar=wr(-1);function xr(n,r,t){var e=[];return r=Pn(r,t),mr(n,(function(n,t,u){r(n,t,u)&&e.push(n)})),e}function Sr(n,r,t){r=Pn(r,t);for(var e=!tr(n)&&nn(n),u=(e||n).length,i=0;i=0}var Er=j((function(n,r,t){var e,u;return D(r)?u=r:(r=Bn(r),e=r.slice(0,-1),r=r[r.length-1]),jr(n,(function(n){var i=u;if(!i){if(e&&e.length&&(n=Nn(n,e)),null==n)return;i=n[r]}return null==i?i:i.apply(n,t)}))}));function Br(n,r){return jr(n,Dn(r))}function Nr(n,r,t){var e,u,i=-1/0,o=-1/0;if(null==r||"number"==typeof r&&"object"!=typeof n[0]&&null!=n)for(var a=0,f=(n=tr(n)?n:jn(n)).length;ai&&(i=e);else r=Pn(r,t),mr(n,(function(n,t,e){((u=r(n,t,e))>o||u===-1/0&&i===-1/0)&&(i=n,o=u)}));return i}var Ir=/[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;function Tr(n){return n?U(n)?o.call(n):S(n)?n.match(Ir):tr(n)?jr(n,Tn):jn(n):[]}function kr(n,r,t){if(null==r||t)return tr(n)||(n=jn(n)),n[Un(n.length-1)];var e=Tr(n),u=Y(e);r=Math.max(Math.min(r,u),0);for(var i=u-1,o=0;o1&&(e=Rn(e,r[1])),r=an(n)):(e=qr,r=er(r,!1,!1),n=Object(n));for(var u=0,i=r.length;u1&&(t=r[1])):(r=jr(er(r,!1,!1),String),e=function(n,t){return!Mr(r,t)}),Ur(n,e,t)}));function zr(n,r,t){return o.call(n,0,Math.max(0,n.length-(null==r||t?1:r)))}function Lr(n,r,t){return null==n||n.length<1?null==r||t?void 0:[]:null==r||t?n[0]:zr(n,n.length-r)}function $r(n,r,t){return o.call(n,null==r||t?1:r)}var Cr=j((function(n,r){return r=er(r,!0,!0),xr(n,(function(n){return!Mr(r,n)}))})),Kr=j((function(n,r){return Cr(n,r)}));function Jr(n,r,t,e){A(r)||(e=t,t=r,r=!1),null!=t&&(t=Pn(t,e));for(var u=[],i=[],o=0,a=Y(n);or?(e&&(clearTimeout(e),e=null),a=c,o=n.apply(u,i),e||(u=i=null)):e||!1===t.trailing||(e=setTimeout(f,l)),o};return c.cancel=function(){clearTimeout(e),a=0,e=u=i=null},c},debounce:function(n,r,t){var e,u,i,o,a,f=function(){var c=Wn()-u;r>c?e=setTimeout(f,r-c):(e=null,t||(o=n.apply(a,i)),e||(i=a=null))},c=j((function(c){return a=this,i=c,u=Wn(),e||(e=setTimeout(f,r),t&&(o=n.apply(a,i))),o}));return c.cancel=function(){clearTimeout(e),e=i=a=null},c},wrap:function(n,r){return nr(r,n)},negate:ar,compose:function(){var n=arguments,r=n.length-1;return function(){for(var t=r,e=n[r].apply(this,arguments);t--;)e=n[t].call(this,e);return e}},after:function(n,r){return function(){if(--n<1)return r.apply(this,arguments)}},before:fr,once:cr,findKey:lr,findIndex:pr,findLastIndex:vr,sortedIndex:hr,indexOf:dr,lastIndexOf:gr,find:br,detect:br,findWhere:function(n,r){return br(n,kn(r))},each:mr,forEach:mr,map:jr,collect:jr,reduce:_r,foldl:_r,inject:_r,reduceRight:Ar,foldr:Ar,filter:xr,select:xr,reject:function(n,r,t){return xr(n,ar(Pn(r)),t)},every:Sr,all:Sr,some:Or,any:Or,contains:Mr,includes:Mr,include:Mr,invoke:Er,pluck:Br,where:function(n,r){return xr(n,kn(r))},max:Nr,min:function(n,r,t){var e,u,i=1/0,o=1/0;if(null==r||"number"==typeof r&&"object"!=typeof n[0]&&null!=n)for(var a=0,f=(n=tr(n)?n:jn(n)).length;ae||void 0===t)return 1;if(t${split}`); +function deleteCampaign(campaignId, mode) { + // ask for confirmation + if (!confirm(`Are you sure you want to delete the campaign ${campaignId}? All the data will be lost!`)) { + return; } + + $.post({ + url: `${url_prefix}/delete_campaign`, + contentType: 'application/json', // Specify JSON content type + data: JSON.stringify({ + campaignId: campaignId, + mode: mode, + }), + success: function (response) { + console.log(response); + + if (response.success !== true) { + alert(response.error); + } else { + window.location.hash = "#annotations"; + // reload the page + location.reload(); + } + } + }); } function deleteOutput(dataset, split, setup_id) { @@ -59,75 +74,22 @@ function deleteOutput(dataset, split, setup_id) { alert(response.error); } else { // reload + window.location.hash = "#local"; location.reload(); } } }); } +function changeDataset() { + const dataset = $('#dataset-select').val(); -function uploadDataset() { - const dataset_id = $("#dataset-id").val(); - const description = $("#dataset-description").val(); - const format = $("#dataset-format").val(); - const dataset = {}; - var filesToRead = $("#dataset-files").children().length; - - $("#upload-dataset-btn").text("Uploading...").prop("disabled", true); - - // Function to send the POST request - function sendRequest() { - $.post({ - url: `${url_prefix}/upload_dataset`, - contentType: 'application/json', // Specify JSON content type - data: JSON.stringify({ - id: dataset_id, - description: description, - format: format, - dataset: dataset, - }), - success: function (response) { - console.log(response); + // set available splits in #split-select + $('#split-select').empty(); - if (response.success !== true) { - alert(response.error); - $("#upload-dataset-btn").text("Upload dataset").prop("disabled", false); - } else { - // reload - location.reload(); - } - } - }); + for (const split of datasets[dataset].splits) { + $('#split-select').append(``); } - - // Read each file - $("#dataset-files").children().each(function () { - const splitName = $(this).find("input[name='split-name']").val(); - const splitFile = $(this).find("input[name='split-file']")[0].files[0]; - const reader = new FileReader(); - - reader.onload = function (e) { - // Check if the file is a ZIP file - if (splitFile.type === "application/zip") { - dataset[splitName] = Array.from(new Uint8Array(e.target.result)); - } else { - dataset[splitName] = e.target.result; - } - filesToRead--; - - // If all files are read, send the request - if (filesToRead === 0) { - sendRequest(); - } - }; - - // Read as ArrayBuffer for binary files (e.g., ZIP) - if (splitFile.type === "application/zip") { - reader.readAsArrayBuffer(splitFile); - } else { - reader.readAsText(splitFile); - } - }); } @@ -157,6 +119,8 @@ function downloadDataset(datasetId) { // remove the spinner // $(`#spinner-download-${datasetId}`).remove(); // $(`#check-downloaded-${datasetId}`).show(); + + window.location.hash = "#local"; location.reload(); } } @@ -219,11 +183,8 @@ function uploadModelOutputs() { if (response.success !== true) { alert(response.error); } else { - // reload + window.location.hash = "#outputs"; location.reload(); - // set the selectbox to the corresponding dataset and split - $("#dataset-select").val(dataset).trigger("change"); - $("#split-select").val(split).trigger("change"); } } }); @@ -253,19 +214,78 @@ function setDatasetEnabled(name, enabled) { } -function enableTooltips() { - // enable tooltips - var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')) - var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { - return new bootstrap.Tooltip(tooltipTriggerEl) - }) + +function uploadDataset() { + const dataset_id = $("#dataset-id").val(); + const description = $("#dataset-description").val(); + const format = $("#dataset-format").val(); + const dataset = {}; + var filesToRead = $("#dataset-files").children().length; + + $("#upload-dataset-btn").text("Uploading...").prop("disabled", true); + + // Function to send the POST request + function sendRequest() { + $.post({ + url: `${url_prefix}/upload_dataset`, + contentType: 'application/json', // Specify JSON content type + data: JSON.stringify({ + name: dataset_id, + description: description, + format: format, + dataset: dataset, + }), + success: function (response) { + console.log(response); + + if (response.success !== true) { + alert(response.error); + $("#upload-dataset-btn").text("Upload dataset").prop("disabled", false); + } else { + // reload + window.location.hash = "#local"; + location.reload(); + } + } + }); + } + + // Read each file + $("#dataset-files").children().each(function () { + const splitName = $(this).find("input[name='split-name']").val(); + const splitFile = $(this).find("input[name='split-file']")[0].files[0]; + const reader = new FileReader(); + + reader.onload = function (e) { + // Check if the file is a ZIP file + if (splitFile.type === "application/zip") { + dataset[splitName] = Array.from(new Uint8Array(e.target.result)); + } else { + dataset[splitName] = e.target.result; + } + filesToRead--; + + // If all files are read, send the request + if (filesToRead === 0) { + sendRequest(); + } + }; + + // Read as ArrayBuffer for binary files (e.g., ZIP) + if (splitFile.type === "application/zip") { + reader.readAsArrayBuffer(splitFile); + } else { + reader.readAsText(splitFile); + } + }); } -$(document).ready(function () { - $("#dataset-select-overview").val(Object.keys(datasets)[0]).trigger("change"); - updateSplits(); +$(document).ready(function () { + if (Object.keys(datasets).length > 0) { + $("#dataset-select").val(Object.keys(datasets)[0]).trigger("change"); + } // Function to activate the tab based on the anchor function activateTabFromAnchor() { var anchor = window.location.hash.substring(1); @@ -297,3 +317,4 @@ $(document).ready(function () { enableTooltips(); }); +$("#dataset-select").on("change", changeDataset); \ No newline at end of file diff --git a/factgenie/static/js/utils.js b/factgenie/static/js/utils.js index 381439dc..ba1f4f74 100644 --- a/factgenie/static/js/utils.js +++ b/factgenie/static/js/utils.js @@ -1,3 +1,144 @@ +const datasets = window.datasets; +const url_prefix = window.url_prefix; +const mode = window.mode; + +function addAnnotationSpanCategory() { + const annotationSpanCategories = $("#annotation-span-categories"); + const randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16); + const newCategory = createAnnotationSpanCategoryElem("", randomColor, ""); + annotationSpanCategories.append(newCategory); +} + +function addModelArgument() { + const modelArguments = $("#model-arguments"); + const newArg = createArgElem("", ""); + modelArguments.append(newArg); +} + +function addExtraArgument() { + const modelArguments = $("#extra-arguments"); + const newArg = createArgElem("", ""); + modelArguments.append(newArg); +} + +function addFlag() { + const flags = $("#flags"); + const newFlag = createFlagElem(""); + flags.append(newFlag); +} + +function addOption() { + const options = $("#options"); + const newOption = createOptionElem("select", "", ""); + options.append(newOption); +} + +function addTextField() { + const textFields = $("#textFields"); + const newTextField = createTextFieldElem(""); + textFields.append(newTextField); +} + + +function deleteRow(button) { + $(button).parent().parent().remove(); +} + +function createFlagElem(key) { + // text area and selectbox for the flag ("checked" or "unchecked" based on the value) + const newFlag = $(` +
    +
    + +
    +
    + +
    +
    + `); + return newFlag; +} + +function createOptionElem(type, label, values) { + // three columns: option type (selectbox, slider) text input for the label, and text input for comma-separated values + const newOption = $(` +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    + `); + return newOption; +} + +function createTextFieldElem(key) { + // text area and selectbox for the flag ("checked" or "unchecked" based on the value) + const newFlag = $(` +
    +
    + +
    +
    + +
    +
    + `); + return newFlag; +} + +function createArgElem(key, value) { + // escape quotes in the value + if (typeof value === 'string') { + value = value.replace(/"/g, """); + } + const newArg = $(` +
    +
    + +
    +
    + +
    +
    + +
    +
    + `); + return newArg; +} + +function createAnnotationSpanCategoryElem(name, color, description) { + const newCategory = $(` +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + `); + return newCategory; +} + + function detailFormatter(index, row) { const keys = Object.keys(row).filter(key => key.match(/^\d+$/)); const key = keys[keys.length - 1]; @@ -22,3 +163,107 @@ function detailFilter(index, row) { } } } + +function enableTooltips() { + // enable tooltips + var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')) + var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { + return new bootstrap.Tooltip(tooltipTriggerEl) + }) +} + + +function gatherSelectedCombinations() { + // read all the rows the remained in #selectedDatasetsContent + var selectedData = []; + $("#selectedDatasetsContent tr").each(function () { + var dataset = $(this).find("td:eq(0)").text(); + var split = $(this).find("td:eq(1)").text(); + if (mode != "llm_gen") { + var setup_id = $(this).find("td:eq(2)").text(); + selectedData.push({ dataset: dataset, split: split, setup_id: setup_id }); + } else { + selectedData.push({ dataset: dataset, split: split }); + } + }); + return selectedData; +} + + +function getAnnotationSpanCategories() { + var annotationSpanCategories = []; + + $("#annotation-span-categories").children().each(function () { + const name = $(this).find("input[name='annotationSpanCategoryName']").val(); + const color = $(this).find("input[name='annotationSpanCategoryColor']").val(); + const description = $(this).find("input[name='annotationSpanCategoryDescription']").val(); + annotationSpanCategories.push({ name: name, color: color, description: description }); + }); + return annotationSpanCategories; +} + +function getKeysAndValues(div) { + var args = {}; + div.children().each(function () { + const key = $(this).find("input[name='argName']").val(); + const value = $(this).find("input[name='argValue']").val(); + args[key] = value; + }); + return args; +} + +function getKeys(div) { + var keys = []; + div.children().each(function () { + const key = $(this).find("input[name='argName']").val(); + keys.push(key); + }); + return keys; +} + +function getOptions() { + var options = []; + + $("#options").children().each(function () { + const type = $(this).find("select[name='optionType']").val(); + const label = $(this).find("input[name='optionLabel']").val(); + const values = $(this).find("input[name='optionValues']").val().split(",").map(v => v.trim()); + options.push({ type: type, label: label, values: values }); + }); + return options; +} + +function mod(n, m) { + return ((n % m) + m) % m; +} + + +function randInt(max) { + return Math.floor(Math.random() * max); +} + +function nextBtn() { + goToPage(current_example_idx + 1); +} + +function prevBtn() { + goToPage(current_example_idx - 1); +} + +function startBtn() { + goToPage(0); +} + +function endBtn() { + goToPage(total_examples - 1); +} + +function randomBtn() { + goToPage(randInt(total_examples - 1)); +} + + +function goToBtn() { + var n = $("#page-input").val(); + goToPage(n); +} \ No newline at end of file diff --git a/factgenie/templates/campaigns/annotate_header.html b/factgenie/templates/campaigns/annotate_header.html deleted file mode 100755 index e0b8bbec..00000000 --- a/factgenie/templates/campaigns/annotate_header.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - Annotations - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/factgenie/templates/campaigns/quintd1-human/annotate.html b/factgenie/templates/campaigns/quintd1-human/annotate.html deleted file mode 100755 index 0ca47ba2..00000000 --- a/factgenie/templates/campaigns/quintd1-human/annotate.html +++ /dev/null @@ -1,201 +0,0 @@ - - - - - D2T Annotation - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - - -
    -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -
    -
    -
    -
    - -
    -
    -

    - -

    -
    -
    - - {% include 'campaigns/quintd1-human/instructions.html' %} - - -
    -
    -
    -
    - - -
    -
    -
    The following text is a(n) . Please - annotate all the - errors in the text: -
    -
    - - - -
    -
    -

    After annotating the errors in the text, please also check if you agree with any of the following - statements, then mark the example as complete:

    -
    - - -
    -
    - - -
    -
    - - -
    -
    - -
    - -
    -
    -
    - -
    -
    -
    -
    -
    - -
    -
    -

    Welcome!

    - -

    Please, make sure you are familiar with the instructions below. If you don't remember everything, don't worry – - you will also have - access to these instructions during the task.

    - - {% include 'campaigns/quintd1-human/instructions.html' %} -
    - - -
    -
    - - - - - - - - - - - \ No newline at end of file diff --git a/factgenie/templates/campaigns/quintd1-human/instructions.html b/factgenie/templates/campaigns/quintd1-human/instructions.html deleted file mode 100644 index 1cefbf2c..00000000 --- a/factgenie/templates/campaigns/quintd1-human/instructions.html +++ /dev/null @@ -1,196 +0,0 @@ -

    In this task, you will annotate {{ annotation_example_cnt }} examples in total. For each example, you will see - data on the - left side and the corresponding generated text on the right side. Your task is to annotate errors in - the text with respect to the data. -

    -

    - There are five - types of errors that you can mark in the generated text:

    - -
      -
    1. Incorrect fact: The fact - in the - text contradicts the data.
    2. -
    3. Not checkable : - The - fact in the text cannot be checked given the data.
    4. -
    5. Misleading: - The fact in the text is misleading in the given context.
    6. -
    7. Other : - The text is problematic for another reason, e.g. grammatically or stylistically incorrect, - irrelevant, or repetitive.
    8. -
    - -
    -
    -

    - -

    -
    -
    - -

    Use your mouse to highlight specific - parts of the text containing the errors. To switch between error - categories, repeatedly click on the highlighted text (the last click removes the highlight). - Note that highlighting from the right to left can work better for longer spans.

    -

    - Once you think you have marked all the errors present in the text, click the ✅ Mark example as - complete - button (you can still update the annotation later). You will be able to submit the annotations - once they - are all are - marked - as complete.

    -
    -
    -
    -
    -

    - -

    -
    -
    -
      -
    • Each error span should include all the words related to the - error (but nothing else).
    • -
    • If you think the fact - is probably true, but - cannot be derived from the data, mark it as not checkable.
    • -
    • - If you are not really sure if the fact - should be marked as an error, leave it unmarked.
    • -
    -
    -
    -
    -
    -

    - -

    -
    -
    - An example of the data input and the corresponding text annotated with errors: - -
    -
    -
    data
    - - Nokia 3310 -
      -
    • color: black, blue, grey
    • -
    • display: 320x240px
    • -
    - - -
    -
    -
    -
    text (product description)
    - Nokia 3310 is produced in - Finland and features a - 320x320 display. It is available in black color. The data seem to provide only partial - information about the phone. -
    -
    -
    explanation
    -
    -
      -
    • produced in - Finland: The country where the phone is produced is not - mentioned in the data.
    • - -
    • 320x320: The data mentions - that the display has resolution 320x240px.
    • - -
    • available in black color: - Misleadingly - suggests that the phone is not available in other colors.
    • -
    • The data seem to provide only - partial information about the phone.: The note is irrelevant for the phone - description.
    • -
    -
    - Note that for the sake of brevity, this particular example is based on a small data input and - contains many errors, which may not always be the case. -
    -
    -
    -
    -

    - -

    -
    -
    - - You can encounter the following kinds of texts: -
      -
    • a 5-day weather forecast generated from weather data,
    • -
    • a description of a product generated from product specifications
    • -
    • an ice hockey game report generated from information about the game,
    • -
    • a caption of a health-related chart,
    • -
    • a description of an entity (human, country, film, or company) based on its properties.
    • -
    -
    -
    -
    -
    -

    - -

    -
    -
    - The data is downloaded from public sources - - ( - openweathermap.org, gsmarena.com, rapidapi.com, - ourworldindata.com, wikidata.com). - The texts are - generated by large language models. -
    -
    -
    -
    -

    - -

    -
    -
    - The research is conducted by the NLP research group at Charles University in Prague. The research is a - part of the ERC - project Next-Generation Natural Language - Generation and was approved by the ethical committee of the project.

    By - continuing, - you agree that the anonymous annotations - will be made available to the research community to - help - evaluating and - improving data-to-text generation systems based on large language models. -
    -
    -
    - - - -
    \ No newline at end of file diff --git a/factgenie/templates/campaigns/annotate_body.html b/factgenie/templates/crowdsourcing/annotate_body.html similarity index 96% rename from factgenie/templates/campaigns/annotate_body.html rename to factgenie/templates/crowdsourcing/annotate_body.html index 2529c550..2ff6c200 100755 --- a/factgenie/templates/campaigns/annotate_body.html +++ b/factgenie/templates/crowdsourcing/annotate_body.html @@ -95,11 +95,11 @@

    -
    +

    Welcome!

    {{ instructions | safe }} - +
    diff --git a/factgenie/templates/campaigns/annotate_footer.html b/factgenie/templates/crowdsourcing/annotate_footer.html similarity index 66% rename from factgenie/templates/campaigns/annotate_footer.html rename to factgenie/templates/crowdsourcing/annotate_footer.html index 770e6192..0aa4ad1a 100755 --- a/factgenie/templates/campaigns/annotate_footer.html +++ b/factgenie/templates/crowdsourcing/annotate_footer.html @@ -6,5 +6,5 @@ window.metadata = {{ metadata | tojson | safe }}; - - \ No newline at end of file + + \ No newline at end of file diff --git a/factgenie/templates/crowdsourcing/annotate_header.html b/factgenie/templates/crowdsourcing/annotate_header.html new file mode 100755 index 00000000..e0b3cc03 --- /dev/null +++ b/factgenie/templates/crowdsourcing/annotate_header.html @@ -0,0 +1,24 @@ + + + + + Annotations + + + + + + + + + + + + + + + + + + {% include 'crowdsourcing/custom.html' %} + \ No newline at end of file diff --git a/factgenie/templates/campaigns/closed.html b/factgenie/templates/crowdsourcing/closed.html similarity index 75% rename from factgenie/templates/campaigns/closed.html rename to factgenie/templates/crowdsourcing/closed.html index 18348348..637200c3 100755 --- a/factgenie/templates/campaigns/closed.html +++ b/factgenie/templates/crowdsourcing/closed.html @@ -7,9 +7,9 @@ - - - + + + @@ -49,11 +49,6 @@

    Welcome!

    - \ No newline at end of file diff --git a/factgenie/templates/crowdsourcing/custom.html b/factgenie/templates/crowdsourcing/custom.html new file mode 100644 index 00000000..53f51b57 --- /dev/null +++ b/factgenie/templates/crowdsourcing/custom.html @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/factgenie/templates/forms/add_dataset_modal.html b/factgenie/templates/forms/add_dataset_modal.html deleted file mode 100644 index 3739b4eb..00000000 --- a/factgenie/templates/forms/add_dataset_modal.html +++ /dev/null @@ -1,127 +0,0 @@ - \ No newline at end of file diff --git a/factgenie/templates/forms/about_modal.html b/factgenie/templates/include/about_modal.html similarity index 100% rename from factgenie/templates/forms/about_modal.html rename to factgenie/templates/include/about_modal.html diff --git a/factgenie/templates/forms/actions_modal.html b/factgenie/templates/include/actions_modal.html similarity index 97% rename from factgenie/templates/forms/actions_modal.html rename to factgenie/templates/include/actions_modal.html index ec054f77..95bf8abe 100644 --- a/factgenie/templates/forms/actions_modal.html +++ b/factgenie/templates/include/actions_modal.html @@ -45,8 +45,8 @@
    View statistics
    View the statistics of the annotation spans.

    - + View statistics
    @@ -56,7 +56,7 @@
    Export {{ outputs_name }}
    Download the {{ outputs_name }} collected in the campaign in a ZIP file.

    - Download {{ outputs_name }} @@ -176,7 +176,7 @@
    Copy as LLM config
    diff --git a/factgenie/templates/include/add_dataset_modal.html b/factgenie/templates/include/add_dataset_modal.html new file mode 100644 index 00000000..3e1ea95f --- /dev/null +++ b/factgenie/templates/include/add_dataset_modal.html @@ -0,0 +1,197 @@ +
    ${d.dataset}${d.split}${d.setup_id}${d.output_ids.length}
    ${d.dataset}${d.split}${d.output_ids.length}
    ${categoryIndex}${annotatedText} + +
    ${d.dataset}${d.split}${d.setup_id}${d.example_cnt}
    ${d.dataset}${d.split}${d.example_cnt}
    + + + + + + + + + + + + + {% for dataset_id, dataset in resources.items() %} + + + + + + + + + + + + {% endfor %} + +
    DatasetSplitsOutputsCampaignsSourceDownloadDescription
    + {{ dataset_id }} + + {% for split in dataset.splits %} + {{ split }} + {% endfor %} + + {% if not dataset.outputs %} + - + {% endif %} + {% for output in dataset.outputs %} + {{ output }} + {% endfor %} + + {% if not dataset.annotations %} + - + {% endif %} + {% for ann in dataset.annotations %} + {{ ann }} + {% endfor %} + + {% if dataset.source %} + link + {% else %} + - + {% endif %} + + + + + +
    +
    +
    + +
    + + +
    + + +
    +
    + + +
    + +
    + + +
    + +
    +
    + Dataset files +
    +
    +
    +
    + +
    +
    +
    + + +
    +
    + + +
    + + + + + \ No newline at end of file diff --git a/factgenie/templates/forms/add_model_output_modal.html b/factgenie/templates/include/add_model_output_modal.html similarity index 100% rename from factgenie/templates/forms/add_model_output_modal.html rename to factgenie/templates/include/add_model_output_modal.html diff --git a/factgenie/templates/forms/agreement_modal.html b/factgenie/templates/include/agreement_modal.html similarity index 100% rename from factgenie/templates/forms/agreement_modal.html rename to factgenie/templates/include/agreement_modal.html diff --git a/factgenie/templates/forms/annotation_span_categories.html b/factgenie/templates/include/annotation_span_categories.html similarity index 84% rename from factgenie/templates/forms/annotation_span_categories.html rename to factgenie/templates/include/annotation_span_categories.html index 0479866a..ddc2a523 100644 --- a/factgenie/templates/forms/annotation_span_categories.html +++ b/factgenie/templates/include/annotation_span_categories.html @@ -1,9 +1,10 @@ -
    +
    Annotation span categories
    - Categories used for annotation. You should instruct the annotators to + Categories used for annotation. You should instruct the annotators (or + the model) to use these categories. The order of the category (0 to n) corresponds to value of the `type` field in the annotation JSON. diff --git a/factgenie/templates/forms/config_form.html b/factgenie/templates/include/config_form.html similarity index 100% rename from factgenie/templates/forms/config_form.html rename to factgenie/templates/include/config_form.html diff --git a/factgenie/templates/forms/config_modal.html b/factgenie/templates/include/config_modal.html similarity index 90% rename from factgenie/templates/forms/config_modal.html rename to factgenie/templates/include/config_modal.html index 8500fcbe..174cda66 100644 --- a/factgenie/templates/forms/config_modal.html +++ b/factgenie/templates/include/config_modal.html @@ -11,7 +11,7 @@
    diff --git a/factgenie/templates/include/custom.html b/factgenie/templates/include/custom.html deleted file mode 100644 index 44445b7e..00000000 --- a/factgenie/templates/include/custom.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/factgenie/templates/forms/data_selection.html b/factgenie/templates/include/data_selection.html similarity index 78% rename from factgenie/templates/forms/data_selection.html rename to factgenie/templates/include/data_selection.html index 6c0529ea..02c3e393 100644 --- a/factgenie/templates/forms/data_selection.html +++ b/factgenie/templates/include/data_selection.html @@ -13,16 +13,8 @@ Datasets
    -
    - {% for dataset, splits in model_outs.items() %} -
    - - -
    - {% endfor %} +
    +
    diff --git a/factgenie/templates/include/example_annotation_modal.html b/factgenie/templates/include/example_annotation_modal.html new file mode 100644 index 00000000..4203984f --- /dev/null +++ b/factgenie/templates/include/example_annotation_modal.html @@ -0,0 +1,117 @@ + + + + + + + + + + \ No newline at end of file diff --git a/factgenie/templates/forms/progress_bar_crowdsourcing.html b/factgenie/templates/include/progress_bar_crowdsourcing.html similarity index 100% rename from factgenie/templates/forms/progress_bar_crowdsourcing.html rename to factgenie/templates/include/progress_bar_crowdsourcing.html diff --git a/factgenie/templates/forms/progress_bar_llm.html b/factgenie/templates/include/progress_bar_llm.html similarity index 100% rename from factgenie/templates/forms/progress_bar_llm.html rename to factgenie/templates/include/progress_bar_llm.html diff --git a/factgenie/templates/forms/save_generations_modal.html b/factgenie/templates/include/save_generations_modal.html similarity index 100% rename from factgenie/templates/forms/save_generations_modal.html rename to factgenie/templates/include/save_generations_modal.html diff --git a/factgenie/templates/manage.html b/factgenie/templates/manage.html deleted file mode 100755 index 941140f6..00000000 --- a/factgenie/templates/manage.html +++ /dev/null @@ -1,322 +0,0 @@ - - - - - factgenie - - - - - - - - - - - - - - - - {% include 'navbar.html' %} - -
    - - -

    - Data management -

    - -
    -
    -
    - - - - - - - - - - - - - - {% for dataset_id, dataset in datasets_for_download.items() %} - - - - - - - - - - - - {% endfor %} - -
    DatasetSplitsOutputsAnnotationsSourceDownloadDescription
    - {{ dataset_id }} - - {% for split in dataset.splits %} - {{ split }} - {% endfor %} - - {% if not dataset.outputs %} - - - {% endif %} - {% for output in dataset.outputs %} - {{ output }} - {% endfor %} - - {% if not dataset.annotations %} - - - {% endif %} - {% for ann in dataset.annotations %} - {{ ann }} - {% endfor %} - - {% if dataset.source %} - link - {% else %} - - - {% endif %} - - - - - -
    -
    -
    - - - - - - - - - - - - - - {% for dataset_id, dataset in datasets.items() %} - - - - - - - - - - - {% endfor %} - -
    EnabledDatasetClassSplitsExamplesActionsDescription
    -
    - -
    -
    - {% if dataset.enabled %} - - {{ dataset_id - }} - {% else %} - {{ dataset_id }} - {% endif %} - {{ dataset.class }} - {% for split in dataset.splits %} - {{ split - }} - {% endfor %} - - {% for split in dataset.splits %} - {{ dataset.example_count[split] }}{% if not loop.last %} /{% endif %} - {% endfor %} - - - - - - - -
    - -
    -
    - - - - - - - - - - - - {% for dataset, splits in model_outputs.items() %} - {% for split, setups in splits.items() %} - {% for setup_id, outputs in setups.items() %} - - - - - - - - {% endfor %} - {% endfor %} - {% endfor %} - -
    DatasetSplitSourceExamplesActions
    {{ dataset }}{{ split }}{{ setup_id }}{{ datasets[dataset].output_ids[split][setup_id] | length }} - - - - - - - - - -
    - -
    -
    - - - - - - - - - - - - - {% for campaign_id, campaign in campaigns.items() %} - - - - - - - - - {% endfor %} - -
    CampaignCreatedLabelsStatusActions
    {% if campaign.metadata.source == 'external' %}{{ - campaign.metadata.id }}{% else %}{{ - campaign.metadata.id }}{% endif %}{{ campaign.metadata.created }}{% for category in campaign.metadata.config.annotation_span_categories - %} - {{ - category.name - }} - {% endfor %} - - {% if campaign.metadata.source == 'llm_eval' or campaign.metadata.source == 'llm_gen' %} - {% include 'forms/progress_bar_llm.html' %} - {% elif campaign.metadata.source == 'crowdsourcing' %} - {% include 'forms/progress_bar_crowdsourcing.html' %} - {% else %} - external - {% endif %} - - - - -
    -
    -
    - - {% include 'forms/add_dataset_modal.html' %} - {% include 'forms/add_model_output_modal.html' %} - - - - \ No newline at end of file diff --git a/factgenie/templates/analyze.html b/factgenie/templates/pages/analyze.html similarity index 86% rename from factgenie/templates/analyze.html rename to factgenie/templates/pages/analyze.html index cc0fcc26..7adcbda0 100755 --- a/factgenie/templates/analyze.html +++ b/factgenie/templates/pages/analyze.html @@ -8,14 +8,14 @@ - - - + + + - {% include 'navbar.html' %} + {% include 'pages/navbar.html' %}
    @@ -54,9 +54,8 @@

    {{ campaign.metadata.id }} {{ campaign.metadata.created }} @@ -67,10 +66,10 @@

    - - \ No newline at end of file + + \ No newline at end of file diff --git a/factgenie/templates/analyze_detail.html b/factgenie/templates/pages/analyze_detail.html similarity index 92% rename from factgenie/templates/analyze_detail.html rename to factgenie/templates/pages/analyze_detail.html index 1654d9ae..cd3b624f 100755 --- a/factgenie/templates/analyze_detail.html +++ b/factgenie/templates/pages/analyze_detail.html @@ -8,22 +8,22 @@ - - - + + + - - - - + + + + - {% include 'navbar.html' %} + {% include 'pages/navbar.html' %}
    @@ -32,7 +32,7 @@

    Statistics: + href="{{ host_prefix }}/{{ campaign.metadata.mode }}/detail/{{ campaign.metadata.id }}">Statistics: {{ campaign.metadata.id }}

    @@ -42,7 +42,7 @@

    Created
    {{ campaign.metadata.created }}
    Source
    -
    {{ campaign.metadata.source }}
    +
    {{ campaign.metadata.mode }}
    Span categories
    {% for category in campaign.metadata.config.annotation_span_categories %} @@ -203,10 +203,10 @@

    + \ No newline at end of file diff --git a/factgenie/templates/pages/app_config.html b/factgenie/templates/pages/app_config.html new file mode 100755 index 00000000..e28e8c14 --- /dev/null +++ b/factgenie/templates/pages/app_config.html @@ -0,0 +1,113 @@ + + + + + factgenie + + + + + + + + + + + {% include 'pages/navbar.html' %} + +
    + +
    +
    +
    Password protection
    +
    + +
    + If enabled, users will have to log in to access most of the app + features. Make sure to + enable this feature before any public deployment. +
    +
    + +
    +
    +
    + +
    + The username for logging in the app. +
    + +
    +
    + +
    + The app password. +
    + + +
    +
    +
    +
    Deployment
    +
    + +
    + The prefix of the host URL where factgenie is deployed, e.g. + /demo/factgenie. Keep empty for localhost. +
    + + + +
    +
    + +
    +
    Logging
    +
    + +
    + The logging level for the app. +
    + +
    +
    + +
    + Enable Flask debug logging. +
    +
    + +
    +
    +
    + + +
    + +
    + + + + + \ No newline at end of file diff --git a/factgenie/templates/browse.html b/factgenie/templates/pages/browse.html similarity index 88% rename from factgenie/templates/browse.html rename to factgenie/templates/pages/browse.html index b4465e98..ae8e8e44 100755 --- a/factgenie/templates/browse.html +++ b/factgenie/templates/pages/browse.html @@ -6,15 +6,15 @@ + - - - - - - - - {% include 'include/custom.html' %} + + + + + + + {% include 'crowdsourcing/custom.html' %} @@ -30,8 +30,8 @@ @@ -89,7 +89,7 @@ - {% include 'forms/about_modal.html' %} + {% include 'include/about_modal.html' %}

    @@ -180,12 +180,12 @@ - - \ No newline at end of file + + \ No newline at end of file diff --git a/factgenie/templates/crowdsourcing.html b/factgenie/templates/pages/crowdsourcing.html similarity index 83% rename from factgenie/templates/crowdsourcing.html rename to factgenie/templates/pages/crowdsourcing.html index 34f48427..7974dc44 100755 --- a/factgenie/templates/crowdsourcing.html +++ b/factgenie/templates/pages/crowdsourcing.html @@ -8,18 +8,18 @@ - - - + + + - + - {% include 'navbar.html' %} + {% include 'pages/navbar.html' %}
    @@ -30,9 +30,10 @@ {% if not is_password_protected %} - - {% include 'forms/config_modal.html' %} + {% include 'include/config_modal.html' %} @@ -170,10 +195,9 @@
    - - \ No newline at end of file + + \ No newline at end of file diff --git a/factgenie/templates/crowdsourcing_new.html b/factgenie/templates/pages/crowdsourcing_new.html similarity index 90% rename from factgenie/templates/crowdsourcing_new.html rename to factgenie/templates/pages/crowdsourcing_new.html index 8e81ab09..25af2846 100755 --- a/factgenie/templates/crowdsourcing_new.html +++ b/factgenie/templates/pages/crowdsourcing_new.html @@ -8,19 +8,19 @@ - - - + + + - {% include 'navbar.html' %} + {% include 'pages/navbar.html' %} -
    +
    ← Crowdsourcing campaigns
    @@ -81,8 +81,16 @@

    New crowdsourcing campaign

    + {% include 'include/annotation_span_categories.html' %} + +
    + +
    + -
    +
    Instructions shown to the annotators. Use Markdown syntax. @@ -90,18 +98,8 @@

    New crowdsourcing campaign

    - -
    - -
    - Display an introductory overlay with the instructions. -
    -
    - -
    -
    -
    +
    The message shown after the annotator has submitted the @@ -118,7 +116,7 @@

    New crowdsourcing campaign

    @@ -129,6 +127,15 @@

    New crowdsourcing campaign

    +
    + +
    + Maximum number of annotators that can annotate an example. + +
    + +
    @@ -165,8 +172,7 @@

    New crowdsourcing campaign

    - {% include 'forms/annotation_span_categories.html' %} -
    +

    - - \ No newline at end of file + + \ No newline at end of file diff --git a/factgenie/templates/index.html b/factgenie/templates/pages/index.html similarity index 87% rename from factgenie/templates/index.html rename to factgenie/templates/pages/index.html index 5a2a4ebc..8cf453a1 100755 --- a/factgenie/templates/index.html +++ b/factgenie/templates/pages/index.html @@ -7,13 +7,13 @@ - - - + + + - {% include 'navbar.html' %} + {% include 'pages/navbar.html' %}
    @@ -32,7 +32,7 @@
    Browse
    - +
    Annotate with LLMs
    @@ -54,7 +54,7 @@
    Annotate with human annotators
    - +
    Generate with LLMs
    @@ -90,6 +90,5 @@
    Manage
    \ No newline at end of file diff --git a/factgenie/templates/llm_campaign.html b/factgenie/templates/pages/llm_campaign.html similarity index 81% rename from factgenie/templates/llm_campaign.html rename to factgenie/templates/pages/llm_campaign.html index a77dbbbf..177aa862 100755 --- a/factgenie/templates/llm_campaign.html +++ b/factgenie/templates/pages/llm_campaign.html @@ -8,24 +8,24 @@ - - - + + + - + - {% include 'navbar.html' %} + {% include 'pages/navbar.html' %}
    ← Main page - New LLM + New LLM campaign
    @@ -54,8 +54,7 @@

    {{ + {{ metadata.id }} {{ metadata.created }} {{ metadata.config.type }} @@ -73,7 +72,7 @@

    - - {% include 'forms/actions_modal.html' %} + {% include 'include/actions_modal.html' %} {% endfor %} @@ -112,7 +111,6 @@

    - \ No newline at end of file + + \ No newline at end of file diff --git a/factgenie/templates/llm_campaign_detail.html b/factgenie/templates/pages/llm_campaign_detail.html similarity index 77% rename from factgenie/templates/llm_campaign_detail.html rename to factgenie/templates/pages/llm_campaign_detail.html index 7c16ea1c..c843fbec 100755 --- a/factgenie/templates/llm_campaign_detail.html +++ b/factgenie/templates/pages/llm_campaign_detail.html @@ -8,22 +8,22 @@ - - - + + + - + - {% include 'navbar.html' %} + {% include 'pages/navbar.html' %}
    - ← List of LLM {% + ← List of LLM {% if mode == 'llm_eval' %}evaluation{% else %}generation{% endif %} campaigns

    {{ campaign_id }}

    @@ -53,9 +53,8 @@

    style="display: none;" {% endif %}> Pause {% if mode == 'llm_eval' %}evaluation{% else %}generation{% endif %} - + Download {% if mode == 'llm_eval' %}annotations{% else %}outputs{% endif %} @@ -68,14 +67,14 @@

    data-bs-target="#config-modal-{{ metadata.id }}"> Show configuration {% if mode == 'llm_gen' %} - + - Export as model outputs + Save as model outputs {% endif %} {% if mode == 'llm_eval' %} - View statistics {% endif %}

    @@ -95,9 +94,7 @@

    Examples

    @@ -108,8 +105,8 @@

    Examples

    + - @@ -125,26 +122,28 @@

    Examples

    - + - + {% endfor %} @@ -155,11 +154,10 @@

    Examples

    - {% include 'forms/config_modal.html' %} - {% include 'forms/save_generations_modal.html' %} + {% include 'include/config_modal.html' %} + {% include 'include/save_generations_modal.html' %} - - \ No newline at end of file + + \ No newline at end of file diff --git a/factgenie/templates/llm_campaign_new.html b/factgenie/templates/pages/llm_campaign_new.html similarity index 89% rename from factgenie/templates/llm_campaign_new.html rename to factgenie/templates/pages/llm_campaign_new.html index 90a002eb..28f3d0a4 100755 --- a/factgenie/templates/llm_campaign_new.html +++ b/factgenie/templates/pages/llm_campaign_new.html @@ -8,17 +8,17 @@ - - - + + + - {% include 'navbar.html' %} + {% include 'pages/navbar.html' %} -
    - ← LLM campaigns +
    + ← LLM campaigns
    @@ -77,39 +77,15 @@

    New LLM {% if mode == 'llm_gen' %}generation{% else %}evaluation{% endif %}

    -
    -
    - - API -
    - Select one of the available metric types (or add your own class - to - factgenie/metrics.py). - -
    -
    -
    - -
    -
    - -
    -
    - - Model -
    - The identifier of the model you are querying. - -
    -
    -
    - -
    + {% if mode == 'llm_eval' %} + {% include 'include/annotation_span_categories.html' %} +
    + +
    + {% endif %}
    @@ -118,7 +94,7 @@

    New LLM {% if mode == 'llm_gen' %}generation{% else %}evaluation{% endif %}
    Prompt for the model. See the instructions for configuring the prompt at factgenie + href="https://github.com/ufal/factgenie/wiki/LLM-Annotations#-configuring-the-prompt">factgenie wiki.
    @@ -162,6 +138,27 @@

    New LLM {% if mode == 'llm_gen' %}generation{% else %}evaluation{% endif %}

    {% endif %} +
    +
    + + LLM evaluator +
    + Select one of the available LLM evaluators (or add your own + class + to + factgenie/models.py). + +
    +
    +
    + +
    +
    +
    @@ -178,6 +175,20 @@

    New LLM {% if mode == 'llm_gen' %}generation{% else %}evaluation{% endif %}

    +
    +
    + + Model +
    + The identifier of the model you are querying. + +
    +
    +
    + +
    +
    +
    @@ -197,10 +208,6 @@

    New LLM {% if mode == 'llm_gen' %}generation{% else %}evaluation{% endif %}

    - {% if mode == 'llm_eval' %} - {% include 'forms/annotation_span_categories.html' %} - {% endif %} -
    @@ -231,7 +238,7 @@

    New LLM {% if mode == 'llm_gen' %}generation{% else %}evaluation{% endif %}
    - {% include 'forms/data_selection.html' %} + {% include 'include/data_selection.html' %}
    + {% include 'include/example_annotation_modal.html' %} +

    Split Example StatusOutput ActionsOutput
    {{ example.status }} +
    + +
    +
    - - - + + + -
    -
    {{ example.output }}
    -
    -
    + + + + + + + + + + + + + {% for dataset_id, dataset in datasets.items() %} + + + + + + + + + + + {% endfor %} + + +
    +
    + {% if not model_outputs %} + + {% endif %} + + + + + + + + + + + + {% for record in model_outputs %} + + + + + + + + {% endfor %} + + +
    +
    + {% if not campaigns %} + + {% endif %} + + + + + + + + + + + + + {% for campaign_id, campaign in campaigns.items() %} + + + + + + + + + {% endfor %} + + +
    +
    + +
    Add dataset
    + + +
    Add model outputs
    + + + +
    +
    + + {% include 'include/add_dataset_modal.html' %} + {% include 'include/add_model_output_modal.html' %} + + + + \ No newline at end of file diff --git a/factgenie/templates/navbar.html b/factgenie/templates/pages/navbar.html similarity index 56% rename from factgenie/templates/navbar.html rename to factgenie/templates/pages/navbar.html index 41508ed5..98177a40 100644 --- a/factgenie/templates/navbar.html +++ b/factgenie/templates/pages/navbar.html @@ -1,18 +1,25 @@ - + + + + + style="position: absolute; top: 0; right: 0; margin-right: 57px; margin-top:15px;"> -{% include 'forms/about_modal.html' %} \ No newline at end of file + + + +{% include 'include/about_modal.html' %} \ No newline at end of file diff --git a/factgenie/templates/no_datasets.html b/factgenie/templates/pages/no_datasets.html similarity index 66% rename from factgenie/templates/no_datasets.html rename to factgenie/templates/pages/no_datasets.html index 9db9a952..a63fe1e3 100755 --- a/factgenie/templates/no_datasets.html +++ b/factgenie/templates/pages/no_datasets.html @@ -7,20 +7,20 @@ - - - + + + - {% include 'navbar.html' %} + {% include 'pages/navbar.html' %}
    @@ -28,5 +28,5 @@ \ No newline at end of file + window.url_prefix = " {{ host_prefix }}"; + \ No newline at end of file diff --git a/factgenie/templates/outputs.html b/factgenie/templates/pages/outputs.html similarity index 85% rename from factgenie/templates/outputs.html rename to factgenie/templates/pages/outputs.html index 95b1fc09..5130e2b5 100755 --- a/factgenie/templates/outputs.html +++ b/factgenie/templates/pages/outputs.html @@ -7,13 +7,13 @@ - - - + + + - {% include 'navbar.html' %} + {% include 'pages/navbar.html' %}